Fix merge r65567.
[reactos.git] / dll / win32 / iphlpapi / address.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * FILE: dll/win32/iphlpapi/address.c
5 * PURPOSE: iphlpapi implementation - Adapter Address APIs
6 * PROGRAMMERS: Jérôme Gardou (jerome.gardou@reactos.org)
7 */
8
9 #include "iphlpapi_private.h"
10
11 WINE_DEFAULT_DEBUG_CHANNEL(iphlpapi);
12
13 /* Helper for GetAdaptersAddresses:
14 * Retrieves the list of network adapters from tcpip.sys */
15 static
16 NTSTATUS
17 GetInterfacesList(
18 _In_ HANDLE TcpFile,
19 _Out_ TDIEntityID **EntityList,
20 _Out_ ULONG* InterfaceCount)
21 {
22
23 TCP_REQUEST_QUERY_INFORMATION_EX TcpQueryInfo;
24 IO_STATUS_BLOCK StatusBlock;
25 NTSTATUS Status;
26 ULONG_PTR BufferSize;
27
28 ZeroMemory(&TcpQueryInfo, sizeof(TcpQueryInfo));
29 TcpQueryInfo.ID.toi_class = INFO_CLASS_GENERIC;
30 TcpQueryInfo.ID.toi_type = INFO_TYPE_PROVIDER;
31 TcpQueryInfo.ID.toi_id = ENTITY_LIST_ID;
32 TcpQueryInfo.ID.toi_entity.tei_entity = GENERIC_ENTITY;
33 TcpQueryInfo.ID.toi_entity.tei_instance = 0;
34
35 Status = NtDeviceIoControlFile(
36 TcpFile,
37 NULL,
38 NULL,
39 NULL,
40 &StatusBlock,
41 IOCTL_TCP_QUERY_INFORMATION_EX,
42 &TcpQueryInfo,
43 sizeof(TcpQueryInfo),
44 NULL,
45 0);
46 if (Status == STATUS_PENDING)
47 {
48 /* So we have to wait a bit */
49 Status = NtWaitForSingleObject(TcpFile, FALSE, NULL);
50 if (NT_SUCCESS(Status))
51 Status = StatusBlock.Status;
52 }
53
54 if (!NT_SUCCESS(Status))
55 return Status;
56
57 BufferSize = StatusBlock.Information;
58 *EntityList = HeapAlloc(GetProcessHeap(), 0, BufferSize);
59 if (!*EntityList)
60 return STATUS_NO_MEMORY;
61
62 /* Do the real call */
63 Status = NtDeviceIoControlFile(
64 TcpFile,
65 NULL,
66 NULL,
67 NULL,
68 &StatusBlock,
69 IOCTL_TCP_QUERY_INFORMATION_EX,
70 &TcpQueryInfo,
71 sizeof(TcpQueryInfo),
72 *EntityList,
73 BufferSize);
74 if (Status == STATUS_PENDING)
75 {
76 /* So we have to wait a bit */
77 Status = NtWaitForSingleObject(TcpFile, FALSE, NULL);
78 if (NT_SUCCESS(Status))
79 Status = StatusBlock.Status;
80 }
81
82 if (!NT_SUCCESS(Status))
83 {
84 HeapFree(GetProcessHeap(), 0, *EntityList);
85 return Status;
86 }
87
88 *InterfaceCount = BufferSize / sizeof(TDIEntityID);
89 return Status;
90 }
91
92 static
93 NTSTATUS
94 GetSnmpInfo(
95 _In_ HANDLE TcpFile,
96 _In_ TDIEntityID InterfaceID,
97 _Out_ IPSNMPInfo* Info)
98 {
99 TCP_REQUEST_QUERY_INFORMATION_EX TcpQueryInfo;
100 IO_STATUS_BLOCK StatusBlock;
101 NTSTATUS Status;
102
103 ZeroMemory(&TcpQueryInfo, sizeof(TcpQueryInfo));
104 TcpQueryInfo.ID.toi_class = INFO_CLASS_PROTOCOL;
105 TcpQueryInfo.ID.toi_type = INFO_TYPE_PROVIDER;
106 TcpQueryInfo.ID.toi_id = IP_MIB_STATS_ID;
107 TcpQueryInfo.ID.toi_entity = InterfaceID;
108
109 Status = NtDeviceIoControlFile(
110 TcpFile,
111 NULL,
112 NULL,
113 NULL,
114 &StatusBlock,
115 IOCTL_TCP_QUERY_INFORMATION_EX,
116 &TcpQueryInfo,
117 sizeof(TcpQueryInfo),
118 Info,
119 sizeof(*Info));
120 if (Status == STATUS_PENDING)
121 {
122 /* So we have to wait a bit */
123 Status = NtWaitForSingleObject(TcpFile, FALSE, NULL);
124 if (NT_SUCCESS(Status))
125 Status = StatusBlock.Status;
126 }
127
128 return Status;
129 }
130
131 static
132 NTSTATUS
133 GetAddrEntries(
134 _In_ HANDLE TcpFile,
135 _In_ TDIEntityID InterfaceID,
136 _Out_ IPAddrEntry* Entries,
137 _In_ ULONG NumEntries)
138 {
139 TCP_REQUEST_QUERY_INFORMATION_EX TcpQueryInfo;
140 IO_STATUS_BLOCK StatusBlock;
141 NTSTATUS Status;
142
143 ZeroMemory(&TcpQueryInfo, sizeof(TcpQueryInfo));
144 TcpQueryInfo.ID.toi_class = INFO_CLASS_PROTOCOL;
145 TcpQueryInfo.ID.toi_type = INFO_TYPE_PROVIDER;
146 TcpQueryInfo.ID.toi_id = IP_MIB_ADDRTABLE_ENTRY_ID;
147 TcpQueryInfo.ID.toi_entity = InterfaceID;
148
149 Status = NtDeviceIoControlFile(
150 TcpFile,
151 NULL,
152 NULL,
153 NULL,
154 &StatusBlock,
155 IOCTL_TCP_QUERY_INFORMATION_EX,
156 &TcpQueryInfo,
157 sizeof(TcpQueryInfo),
158 Entries,
159 NumEntries * sizeof(Entries[0]));
160 if (Status == STATUS_PENDING)
161 {
162 /* So we have to wait a bit */
163 Status = NtWaitForSingleObject(TcpFile, FALSE, NULL);
164 if (NT_SUCCESS(Status))
165 Status = StatusBlock.Status;
166 }
167
168 return Status;
169 }
170
171 /*
172 * Fills the IFEntry buffer from tcpip.sys.
173 * The buffer size MUST be FIELD_OFFSET(IFEntry, if_descr[MAX_ADAPTER_DESCRIPTION_LENGTH + 1]).
174 * See MSDN IFEntry struct definition if you don't believe me. ;-)
175 */
176 static
177 NTSTATUS
178 GetInterfaceEntry(
179 _In_ HANDLE TcpFile,
180 _In_ TDIEntityID InterfaceID,
181 _Out_ IFEntry* Entry)
182 {
183 TCP_REQUEST_QUERY_INFORMATION_EX TcpQueryInfo;
184 IO_STATUS_BLOCK StatusBlock;
185 NTSTATUS Status;
186
187 ZeroMemory(&TcpQueryInfo, sizeof(TcpQueryInfo));
188 TcpQueryInfo.ID.toi_class = INFO_CLASS_PROTOCOL;
189 TcpQueryInfo.ID.toi_type = INFO_TYPE_PROVIDER;
190 TcpQueryInfo.ID.toi_id = IP_MIB_STATS_ID;
191 TcpQueryInfo.ID.toi_entity = InterfaceID;
192
193 Status = NtDeviceIoControlFile(
194 TcpFile,
195 NULL,
196 NULL,
197 NULL,
198 &StatusBlock,
199 IOCTL_TCP_QUERY_INFORMATION_EX,
200 &TcpQueryInfo,
201 sizeof(TcpQueryInfo),
202 Entry,
203 FIELD_OFFSET(IFEntry, if_descr[MAX_ADAPTER_DESCRIPTION_LENGTH + 1]));
204 if (Status == STATUS_PENDING)
205 {
206 /* So we have to wait a bit */
207 Status = NtWaitForSingleObject(TcpFile, FALSE, NULL);
208 if (NT_SUCCESS(Status))
209 Status = StatusBlock.Status;
210 }
211
212 return Status;
213 }
214
215 /* Helpers to get the list of DNS for an interface */
216 static
217 VOID
218 EnumerateServerNameSize(
219 _In_ PWCHAR Interface,
220 _In_ PWCHAR NameServer,
221 _Inout_ PVOID Data)
222 {
223 ULONG* BufferSize = Data;
224
225 /* This is just sizing here */
226 UNREFERENCED_PARAMETER(Interface);
227 UNREFERENCED_PARAMETER(NameServer);
228
229 *BufferSize += sizeof(IP_ADAPTER_DNS_SERVER_ADDRESS) + sizeof(SOCKADDR);
230 }
231
232 static
233 VOID
234 EnumerateServerName(
235 _In_ PWCHAR Interface,
236 _In_ PWCHAR NameServer,
237 _Inout_ PVOID Data)
238 {
239 PIP_ADAPTER_DNS_SERVER_ADDRESS** Ptr = Data;
240 PIP_ADAPTER_DNS_SERVER_ADDRESS ServerAddress = **Ptr;
241
242 UNREFERENCED_PARAMETER(Interface);
243
244 ServerAddress->Length = sizeof(IP_ADAPTER_DNS_SERVER_ADDRESS);
245 ServerAddress->Address.lpSockaddr = (PVOID)(ServerAddress + 1);
246 ServerAddress->Address.iSockaddrLength = sizeof(SOCKADDR);
247
248
249 /* Get the address from the server name string */
250 //FIXME: Only ipv4 for now...
251 if (WSAStringToAddressW(
252 NameServer,
253 AF_INET,
254 NULL,
255 ServerAddress->Address.lpSockaddr,
256 &ServerAddress->Address.iSockaddrLength))
257 {
258 /* Pass along, name conversion failed */
259 ERR("%S is not a valid IP address\n", NameServer);
260 return;
261 }
262
263 /* Go to next item */
264 ServerAddress->Next = (PVOID)(ServerAddress->Address.lpSockaddr + 1);
265 *Ptr = &ServerAddress->Next;
266 }
267
268 DWORD
269 WINAPI
270 DECLSPEC_HOTPATCH
271 GetAdaptersAddresses(
272 _In_ ULONG Family,
273 _In_ ULONG Flags,
274 _In_ PVOID Reserved,
275 _Inout_ PIP_ADAPTER_ADDRESSES pAdapterAddresses,
276 _Inout_ PULONG pOutBufLen)
277 {
278 NTSTATUS Status;
279 HANDLE TcpFile;
280 TDIEntityID* InterfacesList;
281 ULONG InterfacesCount;
282 ULONG AdaptersCount = 0;
283 ULONG i;
284 ULONG TotalSize = 0, RemainingSize;
285 BYTE* Ptr = (BYTE*)pAdapterAddresses;
286
287 FIXME("GetAdaptersAddresses - Semi Stub: Family %u, Flags 0x%08x, Reserved %p, pAdapterAddress %p, pOutBufLen %p.\n",
288 Family, Flags, Reserved, pAdapterAddresses, pOutBufLen);
289
290 if (!pOutBufLen)
291 return ERROR_INVALID_PARAMETER;
292
293 if ((Family == AF_INET6) || (Family == AF_UNSPEC))
294 {
295 /* One day maybe... */
296 FIXME("IPv6 is not supported in ReactOS!\n");
297 if (Family == AF_INET6)
298 {
299 /* We got nothing to say in this case */
300 return ERROR_NO_DATA;
301 }
302 }
303
304 RemainingSize = *pOutBufLen;
305 if (Ptr)
306 ZeroMemory(Ptr, RemainingSize);
307
308 /* open the tcpip driver */
309 Status = openTcpFile(&TcpFile);
310 if (!NT_SUCCESS(Status))
311 {
312 ERR("Could not open handle to tcpip.sys. Status %08x\n", Status);
313 return RtlNtStatusToDosError(Status);
314 }
315
316 /* Get the interfaces list */
317 Status = GetInterfacesList(TcpFile, &InterfacesList, &InterfacesCount);
318 if (!NT_SUCCESS(Status))
319 {
320 ERR("Could not get adapters list. Status %08x\n", Status);
321 NtClose(TcpFile);
322 return RtlNtStatusToDosError(Status);
323 }
324
325 /* Let's see if we got any adapter. */
326 for (i = 0; i < InterfacesCount; i++)
327 {
328 PIP_ADAPTER_ADDRESSES CurrentAA = (PIP_ADAPTER_ADDRESSES)Ptr, PreviousAA = NULL;
329 ULONG CurrentAASize = 0;
330
331 if (InterfacesList[i].tei_entity == IF_ENTITY)
332 {
333 BYTE EntryBuffer[FIELD_OFFSET(IFEntry, if_descr) +
334 RTL_FIELD_SIZE(IFEntry, if_descr[0]) * (MAX_ADAPTER_DESCRIPTION_LENGTH + 1)];
335 IFEntry* Entry = (IFEntry*)EntryBuffer;
336
337 /* Remember we got one */
338 AdaptersCount++;
339
340 /* Of course we need some space for the base structure. */
341 CurrentAASize = sizeof(IP_ADAPTER_ADDRESSES);
342
343 /* Get the entry */
344 Status = GetInterfaceEntry(TcpFile, InterfacesList[i], Entry);
345 if (!NT_SUCCESS(Status))
346 goto Error;
347
348 TRACE("Got entity %*s, index %u.\n",
349 Entry->if_descrlen, &Entry->if_descr[0], Entry->if_index);
350
351 /* Add the adapter name */
352 CurrentAASize += Entry->if_descrlen + sizeof(CHAR);
353
354 /* Add the DNS suffix */
355 CurrentAASize += sizeof(WCHAR);
356
357 /* Add the description. */
358 CurrentAASize += sizeof(WCHAR);
359
360 if (!(Flags & GAA_FLAG_SKIP_FRIENDLY_NAME))
361 {
362 /* Just an empty string for now. */
363 FIXME("Should get adapter friendly name.\n");
364 CurrentAASize += sizeof(WCHAR);
365 }
366
367 if (!(Flags & GAA_FLAG_SKIP_DNS_SERVER))
368 {
369 /* Enumerate the name servers */
370 HKEY InterfaceKey;
371 CHAR KeyName[256];
372
373 snprintf(KeyName, 256,
374 "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\%*s",
375 Entry->if_descrlen, &Entry->if_descr[0]);
376
377 if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, KeyName, 0, KEY_READ, &InterfaceKey) != ERROR_SUCCESS)
378 {
379 ERR("Failed opening interface key for interface %*s\n", Entry->if_descrlen, &Entry->if_descr[0]);
380 Flags |= GAA_FLAG_SKIP_DNS_SERVER;
381 }
382 else
383 {
384 EnumNameServers(InterfaceKey, NULL, &CurrentAASize, EnumerateServerNameSize);
385 RegCloseKey(InterfaceKey);
386 }
387 }
388
389 /* This is part of what we will need */
390 TotalSize += CurrentAASize;
391
392 /* Fill in the data */
393 if ((CurrentAA) && (RemainingSize >= CurrentAASize))
394 {
395 CurrentAA->Length = sizeof(IP_ADAPTER_ADDRESSES);
396 CurrentAA->IfIndex = Entry->if_index;
397 CopyMemory(CurrentAA->PhysicalAddress, Entry->if_physaddr, Entry->if_physaddrlen);
398 CurrentAA->PhysicalAddressLength = Entry->if_physaddrlen;
399 CurrentAA->Flags = 0; // FIXME!
400 CurrentAA->Mtu = Entry->if_mtu;
401 CurrentAA->IfType = Entry->if_type;
402 CurrentAA->OperStatus = Entry->if_operstatus;
403 CurrentAA->Next = PreviousAA;
404 /* Next items */
405 Ptr = (BYTE*)(CurrentAA + 1);
406
407 /* Now fill in the name */
408 CopyMemory(Ptr, &Entry->if_descr[0], Entry->if_descrlen);
409 CurrentAA->AdapterName = (PCHAR)Ptr;
410 CurrentAA->AdapterName[Entry->if_descrlen] = '\0';
411 /* Next items */
412 Ptr = (BYTE*)(CurrentAA->AdapterName + Entry->if_descrlen + 1);
413
414 /* The DNS suffix */
415 CurrentAA->DnsSuffix = (PWCHAR)Ptr;
416 CurrentAA->DnsSuffix[0] = L'\0';
417 /* Next items */
418 Ptr = (BYTE*)(CurrentAA->DnsSuffix + 1);
419
420 /* The description */
421 CurrentAA->Description = (PWCHAR)Ptr;
422 CurrentAA->Description[0] = L'\0';
423 /* Next items */
424 Ptr = (BYTE*)(CurrentAA->Description + 1);
425
426 /* The friendly name */
427 if (!(Flags & GAA_FLAG_SKIP_FRIENDLY_NAME))
428 {
429 CurrentAA->FriendlyName = (PWCHAR)Ptr;
430 CurrentAA->FriendlyName[0] = L'\0';
431 /* Next items */
432 Ptr = (BYTE*)(CurrentAA->FriendlyName + 1);
433 }
434
435 /* The DNS Servers */
436 if (!(Flags & GAA_FLAG_SKIP_DNS_SERVER))
437 {
438 /* Enumerate the name servers */
439 HKEY InterfaceKey;
440 CHAR KeyName[256];
441
442 snprintf(KeyName, 256,
443 "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\%*s",
444 Entry->if_descrlen, &Entry->if_descr[0]);
445
446 if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, KeyName, 0, KEY_READ, &InterfaceKey) != ERROR_SUCCESS)
447 {
448 ERR("Failed opening interface key for interface %*s\n", Entry->if_descrlen, &Entry->if_descr[0]);
449 }
450 else
451 {
452 PIP_ADAPTER_DNS_SERVER_ADDRESS* ServerAddressPtr;
453
454 CurrentAA->FirstDnsServerAddress = (PIP_ADAPTER_DNS_SERVER_ADDRESS)Ptr;
455 ServerAddressPtr = &CurrentAA->FirstDnsServerAddress;
456
457 EnumNameServers(InterfaceKey, NULL, &ServerAddressPtr, EnumerateServerName);
458 RegCloseKey(InterfaceKey);
459
460 /* Set the last entry in the list as having NULL next member */
461 Ptr = (BYTE*)*ServerAddressPtr;
462 *ServerAddressPtr = NULL;
463 }
464 }
465
466 /* We're done for this interface */
467 PreviousAA = CurrentAA;
468 RemainingSize -= CurrentAASize;
469 }
470 }
471 }
472
473 if (AdaptersCount == 0)
474 {
475 /* Uh? Not even localhost ?! */
476 ERR("No Adapters found!\n");
477 *pOutBufLen = 0;
478 return ERROR_NO_DATA;
479 }
480
481 /* See if we have anything to add */
482 // FIXME: Anycast and multicast
483 if ((Flags & (GAA_FLAG_SKIP_UNICAST | GAA_FLAG_INCLUDE_PREFIX)) == GAA_FLAG_SKIP_UNICAST)
484 goto Success;
485
486 /* Now fill in the addresses */
487 for (i = 0; i < InterfacesCount; i++)
488 {
489 /* Look for network layers */
490 if ((InterfacesList[i].tei_entity == CL_NL_ENTITY)
491 || (InterfacesList[i].tei_entity == CO_NL_ENTITY))
492 {
493 IPSNMPInfo SnmpInfo;
494 PIP_ADAPTER_ADDRESSES CurrentAA = NULL;
495 IPAddrEntry* AddrEntries;
496 ULONG j;
497
498 /* Get its SNMP info */
499 Status = GetSnmpInfo(TcpFile, InterfacesList[i], &SnmpInfo);
500 if (!NT_SUCCESS(Status))
501 goto Error;
502
503 if (SnmpInfo.ipsi_numaddr == 0)
504 continue;
505
506 /* Allocate the address entry array and get them */
507 AddrEntries = HeapAlloc(GetProcessHeap(),
508 HEAP_ZERO_MEMORY,
509 SnmpInfo.ipsi_numaddr * sizeof(AddrEntries[0]));
510 if (!AddrEntries)
511 {
512 Status = STATUS_NO_MEMORY;
513 goto Error;
514 }
515 Status = GetAddrEntries(TcpFile, InterfacesList[i], AddrEntries, SnmpInfo.ipsi_numaddr);
516 if (!NT_SUCCESS(Status))
517 {
518 HeapFree(GetProcessHeap(), 0, AddrEntries);
519 goto Error;
520 }
521
522 for (j = 0; j < SnmpInfo.ipsi_numaddr; j++)
523 {
524 /* Find the adapters struct for this address. */
525 if (pAdapterAddresses)
526 {
527 CurrentAA = pAdapterAddresses;
528 while (CurrentAA)
529 {
530 if (CurrentAA->IfIndex == AddrEntries[j].iae_index)
531 break;
532 }
533
534 if (!CurrentAA)
535 {
536 ERR("Got address for interface %u but no adapter was found for it.\n", AddrEntries[j].iae_index);
537 /* Go to the next address */
538 continue;
539 }
540 }
541
542 ERR("address is 0x%08x, mask is 0x%08x\n", AddrEntries[j].iae_addr, AddrEntries[j].iae_mask);
543
544 //FIXME: For now reactos only supports unicast addresses
545 if (!(Flags & GAA_FLAG_SKIP_UNICAST))
546 {
547 ULONG Size = sizeof(IP_ADAPTER_UNICAST_ADDRESS) + sizeof(SOCKADDR);
548
549 if (Ptr && (RemainingSize >= Size))
550 {
551 PIP_ADAPTER_UNICAST_ADDRESS UnicastAddress = (PIP_ADAPTER_UNICAST_ADDRESS)Ptr;
552
553 /* Fill in the structure */
554 UnicastAddress->Length = sizeof(IP_ADAPTER_UNICAST_ADDRESS);
555 UnicastAddress->Next = CurrentAA->FirstUnicastAddress;
556
557 // FIXME: Put meaningful value here
558 UnicastAddress->Flags = 0;
559 UnicastAddress->PrefixOrigin = IpPrefixOriginOther;
560 UnicastAddress->SuffixOrigin = IpSuffixOriginOther;
561 UnicastAddress->DadState = IpDadStatePreferred;
562 UnicastAddress->ValidLifetime = 0xFFFFFFFF;
563 UnicastAddress->PreferredLifetime = 0xFFFFFFFF;
564
565 /* Set the address */
566 //FIXME: ipv4 only (again...)
567 UnicastAddress->Address.lpSockaddr = (LPSOCKADDR)(UnicastAddress + 1);
568 UnicastAddress->Address.iSockaddrLength = sizeof(AddrEntries[j].iae_addr);
569 UnicastAddress->Address.lpSockaddr->sa_family = AF_INET;
570 memcpy(UnicastAddress->Address.lpSockaddr->sa_data, &AddrEntries[j].iae_addr, sizeof(AddrEntries[j].iae_addr));
571
572 CurrentAA->FirstUnicastAddress = UnicastAddress;
573 Ptr += Size;
574 RemainingSize -= Size;
575 }
576
577 TotalSize += Size;
578 }
579
580 if (Flags & GAA_FLAG_INCLUDE_PREFIX)
581 {
582 ULONG Size = sizeof(IP_ADAPTER_PREFIX) + sizeof(SOCKADDR);
583
584 if (Ptr && (RemainingSize >= Size))
585 {
586 PIP_ADAPTER_PREFIX Prefix = (PIP_ADAPTER_PREFIX)Ptr;
587
588 /* Fill in the structure */
589 Prefix->Length = sizeof(IP_ADAPTER_PREFIX);
590 Prefix->Next = CurrentAA->FirstPrefix;
591
592 /* Set the address */
593 //FIXME: ipv4 only (again...)
594 Prefix->Address.lpSockaddr = (LPSOCKADDR)(Prefix + 1);
595 Prefix->Address.iSockaddrLength = sizeof(AddrEntries[j].iae_mask);
596 Prefix->Address.lpSockaddr->sa_family = AF_INET;
597 memcpy(Prefix->Address.lpSockaddr->sa_data, &AddrEntries[j].iae_mask, sizeof(AddrEntries[j].iae_mask));
598
599 /* Compute the prefix size */
600 _BitScanReverse(&Prefix->PrefixLength, AddrEntries[j].iae_mask);
601
602 CurrentAA->FirstPrefix = Prefix;
603 Ptr += Size;
604 RemainingSize -= Size;
605 }
606
607 TotalSize += Size;
608 }
609 }
610
611 HeapFree(GetProcessHeap(), 0, AddrEntries);
612 }
613 }
614
615 Success:
616 /* We're done */
617 HeapFree(GetProcessHeap(), 0, InterfacesList);
618 NtClose(TcpFile);
619 *pOutBufLen = TotalSize;
620 return ERROR_SUCCESS;
621
622 Error:
623 ERR("Failed! Status 0x%08x\n", Status);
624 *pOutBufLen = 0;
625 HeapFree(GetProcessHeap(), 0, InterfacesList);
626 NtClose(TcpFile);
627 return RtlNtStatusToDosError(Status);
628 }