kernel32: Make GetModuleHandleA hotpatchable.
[wine] / dlls / iphlpapi / tests / iphlpapi.c
1 /*
2  * iphlpapi dll test
3  *
4  * Copyright (C) 2003 Juan Lang
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 /*
22  * Some observations that an automated test can't produce:
23  * An adapter index is a key for an adapter.  That is, if an index is returned
24  * from one API, that same index may be used successfully in another API, as
25  * long as the adapter remains present.
26  * If the adapter is removed and reinserted, however, the index may change (and
27  * indeed it does change on Win2K).
28  *
29  * The Name field of the IP_ADAPTER_INDEX_MAP entries returned by
30  * GetInterfaceInfo is declared as a wide string, but the bytes are actually
31  * an ASCII string on some versions of the IP helper API under Win9x.  This was
32  * apparently an MS bug, it's corrected in later versions.
33  *
34  * The DomainName field of FIXED_INFO isn't NULL-terminated on Win98.
35  */
36
37 #include <stdarg.h>
38 #include "winsock2.h"
39 #include "windef.h"
40 #include "winbase.h"
41 #include "iphlpapi.h"
42 #include "iprtrmib.h"
43 #include "wine/test.h"
44 #include <stdio.h>
45 #include <stdlib.h>
46
47 static HMODULE hLibrary = NULL;
48
49 typedef DWORD (WINAPI *GetNumberOfInterfacesFunc)(PDWORD);
50 typedef DWORD (WINAPI *GetIpAddrTableFunc)(PMIB_IPADDRTABLE,PULONG,BOOL);
51 typedef DWORD (WINAPI *GetIfEntryFunc)(PMIB_IFROW);
52 typedef DWORD (WINAPI *GetFriendlyIfIndexFunc)(DWORD);
53 typedef DWORD (WINAPI *GetIfTableFunc)(PMIB_IFTABLE,PULONG,BOOL);
54 typedef DWORD (WINAPI *GetIpForwardTableFunc)(PMIB_IPFORWARDTABLE,PULONG,BOOL);
55 typedef DWORD (WINAPI *GetIpNetTableFunc)(PMIB_IPNETTABLE,PULONG,BOOL);
56 typedef DWORD (WINAPI *GetInterfaceInfoFunc)(PIP_INTERFACE_INFO,PULONG);
57 typedef DWORD (WINAPI *GetAdaptersInfoFunc)(PIP_ADAPTER_INFO,PULONG);
58 typedef DWORD (WINAPI *GetNetworkParamsFunc)(PFIXED_INFO,PULONG);
59 typedef DWORD (WINAPI *GetIcmpStatisticsFunc)(PMIB_ICMP);
60 typedef DWORD (WINAPI *GetIpStatisticsFunc)(PMIB_IPSTATS);
61 typedef DWORD (WINAPI *GetTcpStatisticsFunc)(PMIB_TCPSTATS);
62 typedef DWORD (WINAPI *GetUdpStatisticsFunc)(PMIB_UDPSTATS);
63 typedef DWORD (WINAPI *GetTcpTableFunc)(PMIB_TCPTABLE,PDWORD,BOOL);
64 typedef DWORD (WINAPI *GetUdpTableFunc)(PMIB_UDPTABLE,PDWORD,BOOL);
65 typedef DWORD (WINAPI *GetPerAdapterInfoFunc)(ULONG,PIP_PER_ADAPTER_INFO,PULONG);
66 typedef DWORD (WINAPI *GetAdaptersAddressesFunc)(ULONG,ULONG,PVOID,PIP_ADAPTER_ADDRESSES,PULONG);
67 typedef DWORD (WINAPI *NotifyAddrChangeFunc)(PHANDLE,LPOVERLAPPED);
68 typedef BOOL  (WINAPI *CancelIPChangeNotifyFunc)(LPOVERLAPPED);
69
70 static GetNumberOfInterfacesFunc gGetNumberOfInterfaces = NULL;
71 static GetIpAddrTableFunc gGetIpAddrTable = NULL;
72 static GetIfEntryFunc gGetIfEntry = NULL;
73 static GetFriendlyIfIndexFunc gGetFriendlyIfIndex = NULL;
74 static GetIfTableFunc gGetIfTable = NULL;
75 static GetIpForwardTableFunc gGetIpForwardTable = NULL;
76 static GetIpNetTableFunc gGetIpNetTable = NULL;
77 static GetInterfaceInfoFunc gGetInterfaceInfo = NULL;
78 static GetAdaptersInfoFunc gGetAdaptersInfo = NULL;
79 static GetNetworkParamsFunc gGetNetworkParams = NULL;
80 static GetIcmpStatisticsFunc gGetIcmpStatistics = NULL;
81 static GetIpStatisticsFunc gGetIpStatistics = NULL;
82 static GetTcpStatisticsFunc gGetTcpStatistics = NULL;
83 static GetUdpStatisticsFunc gGetUdpStatistics = NULL;
84 static GetTcpTableFunc gGetTcpTable = NULL;
85 static GetUdpTableFunc gGetUdpTable = NULL;
86 static GetPerAdapterInfoFunc gGetPerAdapterInfo = NULL;
87 static GetAdaptersAddressesFunc gGetAdaptersAddresses = NULL;
88 static NotifyAddrChangeFunc gNotifyAddrChange = NULL;
89 static CancelIPChangeNotifyFunc gCancelIPChangeNotify = NULL;
90
91 static void loadIPHlpApi(void)
92 {
93   hLibrary = LoadLibraryA("iphlpapi.dll");
94   if (hLibrary) {
95     gGetNumberOfInterfaces = (GetNumberOfInterfacesFunc)GetProcAddress(
96      hLibrary, "GetNumberOfInterfaces");
97     gGetIpAddrTable = (GetIpAddrTableFunc)GetProcAddress(
98      hLibrary, "GetIpAddrTable");
99     gGetIfEntry = (GetIfEntryFunc)GetProcAddress(
100      hLibrary, "GetIfEntry");
101     gGetFriendlyIfIndex = (GetFriendlyIfIndexFunc)GetProcAddress(
102      hLibrary, "GetFriendlyIfIndex");
103     gGetIfTable = (GetIfTableFunc)GetProcAddress(
104      hLibrary, "GetIfTable");
105     gGetIpForwardTable = (GetIpForwardTableFunc)GetProcAddress(
106      hLibrary, "GetIpForwardTable");
107     gGetIpNetTable = (GetIpNetTableFunc)GetProcAddress(
108      hLibrary, "GetIpNetTable");
109     gGetInterfaceInfo = (GetInterfaceInfoFunc)GetProcAddress(
110      hLibrary, "GetInterfaceInfo");
111     gGetAdaptersInfo = (GetAdaptersInfoFunc)GetProcAddress(
112      hLibrary, "GetAdaptersInfo");
113     gGetNetworkParams = (GetNetworkParamsFunc)GetProcAddress(
114      hLibrary, "GetNetworkParams");
115     gGetIcmpStatistics = (GetIcmpStatisticsFunc)GetProcAddress(
116      hLibrary, "GetIcmpStatistics");
117     gGetIpStatistics = (GetIpStatisticsFunc)GetProcAddress(
118      hLibrary, "GetIpStatistics");
119     gGetTcpStatistics = (GetTcpStatisticsFunc)GetProcAddress(
120      hLibrary, "GetTcpStatistics");
121     gGetUdpStatistics = (GetUdpStatisticsFunc)GetProcAddress(
122      hLibrary, "GetUdpStatistics");
123     gGetTcpTable = (GetTcpTableFunc)GetProcAddress(
124      hLibrary, "GetTcpTable");
125     gGetUdpTable = (GetUdpTableFunc)GetProcAddress(
126      hLibrary, "GetUdpTable");
127     gGetPerAdapterInfo = (GetPerAdapterInfoFunc)GetProcAddress(hLibrary, "GetPerAdapterInfo");
128     gGetAdaptersAddresses = (GetAdaptersAddressesFunc)GetProcAddress(hLibrary, "GetAdaptersAddresses");
129     gNotifyAddrChange = (NotifyAddrChangeFunc)GetProcAddress(
130      hLibrary, "NotifyAddrChange");
131     gCancelIPChangeNotify = (CancelIPChangeNotifyFunc)GetProcAddress(
132      hLibrary, "CancelIPChangeNotify");
133   }
134 }
135
136 static void freeIPHlpApi(void)
137 {
138   if (hLibrary) {
139     gGetNumberOfInterfaces = NULL;
140     gGetIpAddrTable = NULL;
141     gGetIfEntry = NULL;
142     gGetFriendlyIfIndex = NULL;
143     gGetIfTable = NULL;
144     gGetIpForwardTable = NULL;
145     gGetIpNetTable = NULL;
146     gGetInterfaceInfo = NULL;
147     gGetAdaptersInfo = NULL;
148     gGetNetworkParams = NULL;
149     gGetIcmpStatistics = NULL;
150     gGetIpStatistics = NULL;
151     gGetTcpStatistics = NULL;
152     gGetUdpStatistics = NULL;
153     gGetTcpTable = NULL;
154     gGetUdpTable = NULL;
155     gNotifyAddrChange = NULL;
156     gCancelIPChangeNotify = NULL;
157     FreeLibrary(hLibrary);
158     hLibrary = NULL;
159   }
160 }
161
162 /* replacement for inet_ntoa */
163 static const char *ntoa( DWORD ip )
164 {
165     static char buffer[40];
166
167     ip = htonl(ip);
168     sprintf( buffer, "%u.%u.%u.%u", (ip >> 24) & 0xff, (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff );
169     return buffer;
170 }
171
172 /*
173 still-to-be-tested 98-only functions:
174 GetUniDirectionalAdapterInfo
175 */
176 static void testWin98OnlyFunctions(void)
177 {
178 }
179
180 static void testGetNumberOfInterfaces(void)
181 {
182   if (gGetNumberOfInterfaces) {
183     DWORD apiReturn, numInterfaces;
184
185     /* Crashes on Vista */
186     if (0) {
187       apiReturn = gGetNumberOfInterfaces(NULL);
188       if (apiReturn == ERROR_NOT_SUPPORTED)
189         return;
190       ok(apiReturn == ERROR_INVALID_PARAMETER,
191        "GetNumberOfInterfaces(NULL) returned %d, expected ERROR_INVALID_PARAMETER\n",
192        apiReturn);
193     }
194
195     apiReturn = gGetNumberOfInterfaces(&numInterfaces);
196     if (apiReturn == ERROR_NOT_SUPPORTED) {
197       skip("GetNumberOfInterfaces is not supported\n");
198       return;
199     }
200     ok(apiReturn == NO_ERROR,
201      "GetNumberOfInterfaces returned %d, expected 0\n", apiReturn);
202   }
203 }
204
205 static void testGetIfEntry(DWORD index)
206 {
207   if (gGetIfEntry) {
208     DWORD apiReturn;
209     MIB_IFROW row;
210
211     memset(&row, 0, sizeof(row));
212     apiReturn = gGetIfEntry(NULL);
213     if (apiReturn == ERROR_NOT_SUPPORTED) {
214       skip("GetIfEntry is not supported\n");
215       return;
216     }
217     ok(apiReturn == ERROR_INVALID_PARAMETER,
218      "GetIfEntry(NULL) returned %d, expected ERROR_INVALID_PARAMETER\n",
219      apiReturn);
220     row.dwIndex = -1; /* hope that's always bogus! */
221     apiReturn = gGetIfEntry(&row);
222     ok(apiReturn == ERROR_INVALID_DATA ||
223      apiReturn == ERROR_FILE_NOT_FOUND /* Vista */,
224      "GetIfEntry(bogus row) returned %d, expected ERROR_INVALID_DATA or ERROR_FILE_NOT_FOUND\n",
225      apiReturn);
226     row.dwIndex = index;
227     apiReturn = gGetIfEntry(&row);
228     ok(apiReturn == NO_ERROR, 
229      "GetIfEntry returned %d, expected NO_ERROR\n", apiReturn);
230   }
231 }
232
233 static void testGetIpAddrTable(void)
234 {
235   if (gGetIpAddrTable) {
236     DWORD apiReturn;
237     ULONG dwSize = 0;
238
239     apiReturn = gGetIpAddrTable(NULL, NULL, FALSE);
240     if (apiReturn == ERROR_NOT_SUPPORTED) {
241       skip("GetIpAddrTable is not supported\n");
242       return;
243     }
244     ok(apiReturn == ERROR_INVALID_PARAMETER,
245      "GetIpAddrTable(NULL, NULL, FALSE) returned %d, expected ERROR_INVALID_PARAMETER\n",
246      apiReturn);
247     apiReturn = gGetIpAddrTable(NULL, &dwSize, FALSE);
248     ok(apiReturn == ERROR_INSUFFICIENT_BUFFER,
249      "GetIpAddrTable(NULL, &dwSize, FALSE) returned %d, expected ERROR_INSUFFICIENT_BUFFER\n",
250      apiReturn);
251     if (apiReturn == ERROR_INSUFFICIENT_BUFFER) {
252       PMIB_IPADDRTABLE buf = HeapAlloc(GetProcessHeap(), 0, dwSize);
253
254       apiReturn = gGetIpAddrTable(buf, &dwSize, FALSE);
255       ok(apiReturn == NO_ERROR,
256        "GetIpAddrTable(buf, &dwSize, FALSE) returned %d, expected NO_ERROR\n",
257        apiReturn);
258       if (apiReturn == NO_ERROR && buf->dwNumEntries)
259         testGetIfEntry(buf->table[0].dwIndex);
260       HeapFree(GetProcessHeap(), 0, buf);
261     }
262   }
263 }
264
265 static void testGetIfTable(void)
266 {
267   if (gGetIfTable) {
268     DWORD apiReturn;
269     ULONG dwSize = 0;
270
271     apiReturn = gGetIfTable(NULL, NULL, FALSE);
272     if (apiReturn == ERROR_NOT_SUPPORTED) {
273       skip("GetIfTable is not supported\n");
274       return;
275     }
276     ok(apiReturn == ERROR_INVALID_PARAMETER,
277      "GetIfTable(NULL, NULL, FALSE) returned %d, expected ERROR_INVALID_PARAMETER\n",
278      apiReturn);
279     apiReturn = gGetIfTable(NULL, &dwSize, FALSE);
280     ok(apiReturn == ERROR_INSUFFICIENT_BUFFER,
281      "GetIfTable(NULL, &dwSize, FALSE) returned %d, expected ERROR_INSUFFICIENT_BUFFER\n",
282      apiReturn);
283     if (apiReturn == ERROR_INSUFFICIENT_BUFFER) {
284       PMIB_IFTABLE buf = HeapAlloc(GetProcessHeap(), 0, dwSize);
285
286       apiReturn = gGetIfTable(buf, &dwSize, FALSE);
287       ok(apiReturn == NO_ERROR,
288        "GetIfTable(buf, &dwSize, FALSE) returned %d, expected NO_ERROR\n\n",
289        apiReturn);
290
291       if (apiReturn == NO_ERROR && winetest_debug > 1)
292       {
293           DWORD i, j;
294           char name[MAX_INTERFACE_NAME_LEN];
295
296           trace( "interface table: %u entries\n", buf->dwNumEntries );
297           for (i = 0; i < buf->dwNumEntries; i++)
298           {
299               MIB_IFROW *row = &buf->table[i];
300               WideCharToMultiByte( CP_ACP, 0, row->wszName, -1, name, MAX_INTERFACE_NAME_LEN, NULL, NULL );
301               trace( "%u: '%s' type %u mtu %u speed %u phys",
302                      row->dwIndex, name, row->dwType, row->dwMtu, row->dwSpeed );
303               for (j = 0; j < row->dwPhysAddrLen; j++)
304                   printf( " %02x", row->bPhysAddr[j] );
305               printf( "\n" );
306               trace( "        in: bytes %u upkts %u nupkts %u disc %u err %u unk %u\n",
307                      row->dwInOctets, row->dwInUcastPkts, row->dwInNUcastPkts,
308                      row->dwInDiscards, row->dwInErrors, row->dwInUnknownProtos );
309               trace( "        out: bytes %u upkts %u nupkts %u disc %u err %u\n",
310                      row->dwOutOctets, row->dwOutUcastPkts, row->dwOutNUcastPkts,
311                      row->dwOutDiscards, row->dwOutErrors );
312           }
313       }
314       HeapFree(GetProcessHeap(), 0, buf);
315     }
316   }
317 }
318
319 static void testGetIpForwardTable(void)
320 {
321   if (gGetIpForwardTable) {
322     DWORD apiReturn;
323     ULONG dwSize = 0;
324
325     apiReturn = gGetIpForwardTable(NULL, NULL, FALSE);
326     if (apiReturn == ERROR_NOT_SUPPORTED) {
327       skip("GetIpForwardTable is not supported\n");
328       return;
329     }
330     ok(apiReturn == ERROR_INVALID_PARAMETER,
331      "GetIpForwardTable(NULL, NULL, FALSE) returned %d, expected ERROR_INVALID_PARAMETER\n",
332      apiReturn);
333     apiReturn = gGetIpForwardTable(NULL, &dwSize, FALSE);
334     ok(apiReturn == ERROR_INSUFFICIENT_BUFFER,
335      "GetIpForwardTable(NULL, &dwSize, FALSE) returned %d, expected ERROR_INSUFFICIENT_BUFFER\n",
336      apiReturn);
337     if (apiReturn == ERROR_INSUFFICIENT_BUFFER) {
338       PMIB_IPFORWARDTABLE buf = HeapAlloc(GetProcessHeap(), 0, dwSize);
339
340       apiReturn = gGetIpForwardTable(buf, &dwSize, FALSE);
341       ok(apiReturn == NO_ERROR,
342        "GetIpForwardTable(buf, &dwSize, FALSE) returned %d, expected NO_ERROR\n",
343        apiReturn);
344
345       if (apiReturn == NO_ERROR && winetest_debug > 1)
346       {
347           DWORD i;
348
349           trace( "IP forward table: %u entries\n", buf->dwNumEntries );
350           for (i = 0; i < buf->dwNumEntries; i++)
351           {
352               char buffer[40];
353               sprintf( buffer, "dest %s", ntoa( buf->table[i].dwForwardDest ));
354               sprintf( buffer + strlen(buffer), " mask %s", ntoa( buf->table[i].dwForwardMask ));
355               trace( "%u: %s gw %s if %u type %u\n", i, buffer,
356                      ntoa( buf->table[i].dwForwardNextHop ),
357                      buf->table[i].dwForwardIfIndex, U1(buf->table[i]).dwForwardType );
358           }
359       }
360       HeapFree(GetProcessHeap(), 0, buf);
361     }
362   }
363 }
364
365 static void testGetIpNetTable(void)
366 {
367   if (gGetIpNetTable) {
368     DWORD apiReturn;
369     ULONG dwSize = 0;
370
371     apiReturn = gGetIpNetTable(NULL, NULL, FALSE);
372     if (apiReturn == ERROR_NOT_SUPPORTED) {
373       skip("GetIpNetTable is not supported\n");
374       return;
375     }
376     ok(apiReturn == ERROR_INVALID_PARAMETER,
377      "GetIpNetTable(NULL, NULL, FALSE) returned %d, expected ERROR_INVALID_PARAMETER\n",
378      apiReturn);
379     apiReturn = gGetIpNetTable(NULL, &dwSize, FALSE);
380     ok(apiReturn == ERROR_NO_DATA || apiReturn == ERROR_INSUFFICIENT_BUFFER,
381      "GetIpNetTable(NULL, &dwSize, FALSE) returned %d, expected ERROR_NO_DATA or ERROR_INSUFFICIENT_BUFFER\n",
382      apiReturn);
383     if (apiReturn == ERROR_NO_DATA)
384       ; /* empty ARP table's okay */
385     else if (apiReturn == ERROR_INSUFFICIENT_BUFFER) {
386       PMIB_IPNETTABLE buf = HeapAlloc(GetProcessHeap(), 0, dwSize);
387
388       apiReturn = gGetIpNetTable(buf, &dwSize, FALSE);
389       ok(apiReturn == NO_ERROR ||
390          apiReturn == ERROR_NO_DATA, /* empty ARP table's okay */
391        "GetIpNetTable(buf, &dwSize, FALSE) returned %d, expected NO_ERROR\n",
392        apiReturn);
393
394       if (apiReturn == NO_ERROR && winetest_debug > 1)
395       {
396           DWORD i, j;
397
398           trace( "IP net table: %u entries\n", buf->dwNumEntries );
399           for (i = 0; i < buf->dwNumEntries; i++)
400           {
401               trace( "%u: idx %u type %u addr %s phys",
402                      i, buf->table[i].dwIndex, U(buf->table[i]).dwType, ntoa( buf->table[i].dwAddr ));
403               for (j = 0; j < buf->table[i].dwPhysAddrLen; j++)
404                   printf( " %02x", buf->table[i].bPhysAddr[j] );
405               printf( "\n" );
406           }
407       }
408       HeapFree(GetProcessHeap(), 0, buf);
409     }
410   }
411 }
412
413 static void testGetIcmpStatistics(void)
414 {
415   if (gGetIcmpStatistics) {
416     DWORD apiReturn;
417     MIB_ICMP stats;
418
419     /* Crashes on Vista */
420     if (0) {
421       apiReturn = gGetIcmpStatistics(NULL);
422       if (apiReturn == ERROR_NOT_SUPPORTED)
423         return;
424       ok(apiReturn == ERROR_INVALID_PARAMETER,
425        "GetIcmpStatistics(NULL) returned %d, expected ERROR_INVALID_PARAMETER\n",
426        apiReturn);
427     }
428
429     apiReturn = gGetIcmpStatistics(&stats);
430     if (apiReturn == ERROR_NOT_SUPPORTED)
431     {
432       skip("GetIcmpStatistics is not supported\n");
433       return;
434     }
435     ok(apiReturn == NO_ERROR,
436      "GetIcmpStatistics returned %d, expected NO_ERROR\n", apiReturn);
437     if (apiReturn == NO_ERROR && winetest_debug > 1)
438     {
439         trace( "ICMP stats:          %8s %8s\n", "in", "out" );
440         trace( "    dwMsgs:          %8u %8u\n", stats.stats.icmpInStats.dwMsgs, stats.stats.icmpOutStats.dwMsgs );
441         trace( "    dwErrors:        %8u %8u\n", stats.stats.icmpInStats.dwErrors, stats.stats.icmpOutStats.dwErrors );
442         trace( "    dwDestUnreachs:  %8u %8u\n", stats.stats.icmpInStats.dwDestUnreachs, stats.stats.icmpOutStats.dwDestUnreachs );
443         trace( "    dwTimeExcds:     %8u %8u\n", stats.stats.icmpInStats.dwTimeExcds, stats.stats.icmpOutStats.dwTimeExcds );
444         trace( "    dwParmProbs:     %8u %8u\n", stats.stats.icmpInStats.dwParmProbs, stats.stats.icmpOutStats.dwParmProbs );
445         trace( "    dwSrcQuenchs:    %8u %8u\n", stats.stats.icmpInStats.dwSrcQuenchs, stats.stats.icmpOutStats.dwSrcQuenchs );
446         trace( "    dwRedirects:     %8u %8u\n", stats.stats.icmpInStats.dwRedirects, stats.stats.icmpOutStats.dwRedirects );
447         trace( "    dwEchos:         %8u %8u\n", stats.stats.icmpInStats.dwEchos, stats.stats.icmpOutStats.dwEchos );
448         trace( "    dwEchoReps:      %8u %8u\n", stats.stats.icmpInStats.dwEchoReps, stats.stats.icmpOutStats.dwEchoReps );
449         trace( "    dwTimestamps:    %8u %8u\n", stats.stats.icmpInStats.dwTimestamps, stats.stats.icmpOutStats.dwTimestamps );
450         trace( "    dwTimestampReps: %8u %8u\n", stats.stats.icmpInStats.dwTimestampReps, stats.stats.icmpOutStats.dwTimestampReps );
451         trace( "    dwAddrMasks:     %8u %8u\n", stats.stats.icmpInStats.dwAddrMasks, stats.stats.icmpOutStats.dwAddrMasks );
452         trace( "    dwAddrMaskReps:  %8u %8u\n", stats.stats.icmpInStats.dwAddrMaskReps, stats.stats.icmpOutStats.dwAddrMaskReps );
453     }
454   }
455 }
456
457 static void testGetIpStatistics(void)
458 {
459   if (gGetIpStatistics) {
460     DWORD apiReturn;
461     MIB_IPSTATS stats;
462
463     apiReturn = gGetIpStatistics(NULL);
464     if (apiReturn == ERROR_NOT_SUPPORTED) {
465       skip("GetIpStatistics is not supported\n");
466       return;
467     }
468     ok(apiReturn == ERROR_INVALID_PARAMETER,
469      "GetIpStatistics(NULL) returned %d, expected ERROR_INVALID_PARAMETER\n",
470      apiReturn);
471     apiReturn = gGetIpStatistics(&stats);
472     ok(apiReturn == NO_ERROR,
473       "GetIpStatistics returned %d, expected NO_ERROR\n", apiReturn);
474     if (apiReturn == NO_ERROR && winetest_debug > 1)
475     {
476         trace( "IP stats:\n" );
477         trace( "    dwForwarding:      %u\n", U(stats).dwForwarding );
478         trace( "    dwDefaultTTL:      %u\n", stats.dwDefaultTTL );
479         trace( "    dwInReceives:      %u\n", stats.dwInReceives );
480         trace( "    dwInHdrErrors:     %u\n", stats.dwInHdrErrors );
481         trace( "    dwInAddrErrors:    %u\n", stats.dwInAddrErrors );
482         trace( "    dwForwDatagrams:   %u\n", stats.dwForwDatagrams );
483         trace( "    dwInUnknownProtos: %u\n", stats.dwInUnknownProtos );
484         trace( "    dwInDiscards:      %u\n", stats.dwInDiscards );
485         trace( "    dwInDelivers:      %u\n", stats.dwInDelivers );
486         trace( "    dwOutRequests:     %u\n", stats.dwOutRequests );
487         trace( "    dwRoutingDiscards: %u\n", stats.dwRoutingDiscards );
488         trace( "    dwOutDiscards:     %u\n", stats.dwOutDiscards );
489         trace( "    dwOutNoRoutes:     %u\n", stats.dwOutNoRoutes );
490         trace( "    dwReasmTimeout:    %u\n", stats.dwReasmTimeout );
491         trace( "    dwReasmReqds:      %u\n", stats.dwReasmReqds );
492         trace( "    dwReasmOks:        %u\n", stats.dwReasmOks );
493         trace( "    dwReasmFails:      %u\n", stats.dwReasmFails );
494         trace( "    dwFragOks:         %u\n", stats.dwFragOks );
495         trace( "    dwFragFails:       %u\n", stats.dwFragFails );
496         trace( "    dwFragCreates:     %u\n", stats.dwFragCreates );
497         trace( "    dwNumIf:           %u\n", stats.dwNumIf );
498         trace( "    dwNumAddr:         %u\n", stats.dwNumAddr );
499         trace( "    dwNumRoutes:       %u\n", stats.dwNumRoutes );
500     }
501   }
502 }
503
504 static void testGetTcpStatistics(void)
505 {
506   if (gGetTcpStatistics) {
507     DWORD apiReturn;
508     MIB_TCPSTATS stats;
509
510     apiReturn = gGetTcpStatistics(NULL);
511     if (apiReturn == ERROR_NOT_SUPPORTED) {
512       skip("GetTcpStatistics is not supported\n");
513       return;
514     }
515     ok(apiReturn == ERROR_INVALID_PARAMETER,
516      "GetTcpStatistics(NULL) returned %d, expected ERROR_INVALID_PARAMETER\n",
517      apiReturn);
518     apiReturn = gGetTcpStatistics(&stats);
519     ok(apiReturn == NO_ERROR,
520       "GetTcpStatistics returned %d, expected NO_ERROR\n", apiReturn);
521     if (apiReturn == NO_ERROR && winetest_debug > 1)
522     {
523         trace( "TCP stats:\n" );
524         trace( "    dwRtoAlgorithm: %u\n", U(stats).dwRtoAlgorithm );
525         trace( "    dwRtoMin:       %u\n", stats.dwRtoMin );
526         trace( "    dwRtoMax:       %u\n", stats.dwRtoMax );
527         trace( "    dwMaxConn:      %u\n", stats.dwMaxConn );
528         trace( "    dwActiveOpens:  %u\n", stats.dwActiveOpens );
529         trace( "    dwPassiveOpens: %u\n", stats.dwPassiveOpens );
530         trace( "    dwAttemptFails: %u\n", stats.dwAttemptFails );
531         trace( "    dwEstabResets:  %u\n", stats.dwEstabResets );
532         trace( "    dwCurrEstab:    %u\n", stats.dwCurrEstab );
533         trace( "    dwInSegs:       %u\n", stats.dwInSegs );
534         trace( "    dwOutSegs:      %u\n", stats.dwOutSegs );
535         trace( "    dwRetransSegs:  %u\n", stats.dwRetransSegs );
536         trace( "    dwInErrs:       %u\n", stats.dwInErrs );
537         trace( "    dwOutRsts:      %u\n", stats.dwOutRsts );
538         trace( "    dwNumConns:     %u\n", stats.dwNumConns );
539     }
540   }
541 }
542
543 static void testGetUdpStatistics(void)
544 {
545   if (gGetUdpStatistics) {
546     DWORD apiReturn;
547     MIB_UDPSTATS stats;
548
549     apiReturn = gGetUdpStatistics(NULL);
550     if (apiReturn == ERROR_NOT_SUPPORTED) {
551       skip("GetUdpStatistics is not supported\n");
552       return;
553     }
554     ok(apiReturn == ERROR_INVALID_PARAMETER,
555      "GetUdpStatistics(NULL) returned %d, expected ERROR_INVALID_PARAMETER\n",
556      apiReturn);
557     apiReturn = gGetUdpStatistics(&stats);
558     ok(apiReturn == NO_ERROR,
559      "GetUdpStatistics returned %d, expected NO_ERROR\n", apiReturn);
560     if (apiReturn == NO_ERROR && winetest_debug > 1)
561     {
562         trace( "UDP stats:\n" );
563         trace( "    dwInDatagrams:  %u\n", stats.dwInDatagrams );
564         trace( "    dwNoPorts:      %u\n", stats.dwNoPorts );
565         trace( "    dwInErrors:     %u\n", stats.dwInErrors );
566         trace( "    dwOutDatagrams: %u\n", stats.dwOutDatagrams );
567         trace( "    dwNumAddrs:     %u\n", stats.dwNumAddrs );
568     }
569   }
570 }
571
572 static void testGetTcpTable(void)
573 {
574   if (gGetTcpTable) {
575     DWORD apiReturn;
576     ULONG dwSize = 0;
577
578     apiReturn = gGetTcpTable(NULL, &dwSize, FALSE);
579     if (apiReturn == ERROR_NOT_SUPPORTED) {
580       skip("GetTcpTable is not supported\n");
581       return;
582     }
583     ok(apiReturn == ERROR_INSUFFICIENT_BUFFER ||
584        broken(apiReturn == ERROR_NO_DATA), /* win95 */
585      "GetTcpTable(NULL, &dwSize, FALSE) returned %d, expected ERROR_INSUFFICIENT_BUFFER\n",
586      apiReturn);
587     if (apiReturn == ERROR_INSUFFICIENT_BUFFER) {
588       PMIB_TCPTABLE buf = HeapAlloc(GetProcessHeap(), 0, dwSize);
589
590       apiReturn = gGetTcpTable(buf, &dwSize, FALSE);
591       ok(apiReturn == NO_ERROR,
592        "GetTcpTable(buf, &dwSize, FALSE) returned %d, expected NO_ERROR\n",
593        apiReturn);
594
595       if (apiReturn == NO_ERROR && winetest_debug > 1)
596       {
597           DWORD i;
598           trace( "TCP table: %u entries\n", buf->dwNumEntries );
599           for (i = 0; i < buf->dwNumEntries; i++)
600           {
601               char buffer[40];
602               sprintf( buffer, "local %s:%u",
603                        ntoa(buf->table[i].dwLocalAddr), ntohs(buf->table[i].dwLocalPort) );
604               trace( "%u: %s remote %s:%u state %u\n",
605                      i, buffer, ntoa( buf->table[i].dwRemoteAddr ),
606                      ntohs(buf->table[i].dwRemotePort), U(buf->table[i]).dwState );
607           }
608       }
609       HeapFree(GetProcessHeap(), 0, buf);
610     }
611   }
612 }
613
614 static void testGetUdpTable(void)
615 {
616   if (gGetUdpTable) {
617     DWORD apiReturn;
618     ULONG dwSize = 0;
619
620     apiReturn = gGetUdpTable(NULL, &dwSize, FALSE);
621     if (apiReturn == ERROR_NOT_SUPPORTED) {
622       skip("GetUdpTable is not supported\n");
623       return;
624     }
625     ok(apiReturn == ERROR_INSUFFICIENT_BUFFER,
626      "GetUdpTable(NULL, &dwSize, FALSE) returned %d, expected ERROR_INSUFFICIENT_BUFFER\n",
627      apiReturn);
628     if (apiReturn == ERROR_INSUFFICIENT_BUFFER) {
629       PMIB_UDPTABLE buf = HeapAlloc(GetProcessHeap(), 0, dwSize);
630
631       apiReturn = gGetUdpTable(buf, &dwSize, FALSE);
632       ok(apiReturn == NO_ERROR,
633        "GetUdpTable(buf, &dwSize, FALSE) returned %d, expected NO_ERROR\n",
634        apiReturn);
635
636       if (apiReturn == NO_ERROR && winetest_debug > 1)
637       {
638           DWORD i;
639           trace( "UDP table: %u entries\n", buf->dwNumEntries );
640           for (i = 0; i < buf->dwNumEntries; i++)
641               trace( "%u: %s:%u\n",
642                      i, ntoa( buf->table[i].dwLocalAddr ), ntohs(buf->table[i].dwLocalPort) );
643       }
644       HeapFree(GetProcessHeap(), 0, buf);
645     }
646   }
647 }
648
649 /*
650 still-to-be-tested NT4-onward functions:
651 CreateIpForwardEntry
652 DeleteIpForwardEntry
653 CreateIpNetEntry
654 DeleteIpNetEntry
655 GetFriendlyIfIndex
656 GetRTTAndHopCount
657 SetIfEntry
658 SetIpForwardEntry
659 SetIpNetEntry
660 SetIpStatistics
661 SetIpTTL
662 SetTcpEntry
663 */
664 static void testWinNT4Functions(void)
665 {
666   testGetNumberOfInterfaces();
667   testGetIpAddrTable();
668   testGetIfTable();
669   testGetIpForwardTable();
670   testGetIpNetTable();
671   testGetIcmpStatistics();
672   testGetIpStatistics();
673   testGetTcpStatistics();
674   testGetUdpStatistics();
675   testGetTcpTable();
676   testGetUdpTable();
677 }
678
679 static void testGetInterfaceInfo(void)
680 {
681   if (gGetInterfaceInfo) {
682     DWORD apiReturn;
683     ULONG len = 0;
684
685     apiReturn = gGetInterfaceInfo(NULL, NULL);
686     if (apiReturn == ERROR_NOT_SUPPORTED) {
687       skip("GetInterfaceInfo is not supported\n");
688       return;
689     }
690     ok(apiReturn == ERROR_INVALID_PARAMETER,
691      "GetInterfaceInfo returned %d, expected ERROR_INVALID_PARAMETER\n",
692      apiReturn);
693     apiReturn = gGetInterfaceInfo(NULL, &len);
694     ok(apiReturn == ERROR_INSUFFICIENT_BUFFER,
695      "GetInterfaceInfo returned %d, expected ERROR_INSUFFICIENT_BUFFER\n",
696      apiReturn);
697     if (apiReturn == ERROR_INSUFFICIENT_BUFFER) {
698       PIP_INTERFACE_INFO buf = HeapAlloc(GetProcessHeap(), 0, len);
699
700       apiReturn = gGetInterfaceInfo(buf, &len);
701       ok(apiReturn == NO_ERROR,
702        "GetInterfaceInfo(buf, &dwSize) returned %d, expected NO_ERROR\n",
703        apiReturn);
704       HeapFree(GetProcessHeap(), 0, buf);
705     }
706   }
707 }
708
709 static void testGetAdaptersInfo(void)
710 {
711   if (gGetAdaptersInfo) {
712     DWORD apiReturn;
713     ULONG len = 0;
714
715     apiReturn = gGetAdaptersInfo(NULL, NULL);
716     if (apiReturn == ERROR_NOT_SUPPORTED) {
717       skip("GetAdaptersInfo is not supported\n");
718       return;
719     }
720     ok(apiReturn == ERROR_INVALID_PARAMETER,
721      "GetAdaptersInfo returned %d, expected ERROR_INVALID_PARAMETER\n",
722      apiReturn);
723     apiReturn = gGetAdaptersInfo(NULL, &len);
724     ok(apiReturn == ERROR_NO_DATA || apiReturn == ERROR_BUFFER_OVERFLOW,
725      "GetAdaptersInfo returned %d, expected ERROR_NO_DATA or ERROR_BUFFER_OVERFLOW\n",
726      apiReturn);
727     if (apiReturn == ERROR_NO_DATA)
728       ; /* no adapter's, that's okay */
729     else if (apiReturn == ERROR_BUFFER_OVERFLOW) {
730       PIP_ADAPTER_INFO buf = HeapAlloc(GetProcessHeap(), 0, len);
731
732       apiReturn = gGetAdaptersInfo(buf, &len);
733       ok(apiReturn == NO_ERROR,
734        "GetAdaptersInfo(buf, &dwSize) returned %d, expected NO_ERROR\n",
735        apiReturn);
736       HeapFree(GetProcessHeap(), 0, buf);
737     }
738   }
739 }
740
741 static void testGetNetworkParams(void)
742 {
743   if (gGetNetworkParams) {
744     DWORD apiReturn;
745     ULONG len = 0;
746
747     apiReturn = gGetNetworkParams(NULL, NULL);
748     if (apiReturn == ERROR_NOT_SUPPORTED) {
749       skip("GetNetworkParams is not supported\n");
750       return;
751     }
752     ok(apiReturn == ERROR_INVALID_PARAMETER,
753      "GetNetworkParams returned %d, expected ERROR_INVALID_PARAMETER\n",
754      apiReturn);
755     apiReturn = gGetNetworkParams(NULL, &len);
756     ok(apiReturn == ERROR_BUFFER_OVERFLOW,
757      "GetNetworkParams returned %d, expected ERROR_BUFFER_OVERFLOW\n",
758      apiReturn);
759     if (apiReturn == ERROR_BUFFER_OVERFLOW) {
760       PFIXED_INFO buf = HeapAlloc(GetProcessHeap(), 0, len);
761
762       apiReturn = gGetNetworkParams(buf, &len);
763       ok(apiReturn == NO_ERROR,
764        "GetNetworkParams(buf, &dwSize) returned %d, expected NO_ERROR\n",
765        apiReturn);
766       HeapFree(GetProcessHeap(), 0, buf);
767     }
768   }
769 }
770
771 /*
772 still-to-be-tested 98-onward functions:
773 GetBestInterface
774 GetBestRoute
775 IpReleaseAddress
776 IpRenewAddress
777 */
778 static DWORD CALLBACK testWin98Functions(void *p)
779 {
780   testGetInterfaceInfo();
781   testGetAdaptersInfo();
782   testGetNetworkParams();
783   return 0;
784 }
785
786 static void testGetPerAdapterInfo(void)
787 {
788     DWORD ret, needed;
789     void *buffer;
790
791     if (!gGetPerAdapterInfo) return;
792     ret = gGetPerAdapterInfo(1, NULL, NULL);
793     if (ret == ERROR_NOT_SUPPORTED) {
794       skip("GetPerAdapterInfo is not supported\n");
795       return;
796     }
797     ok( ret == ERROR_INVALID_PARAMETER, "got %u instead of ERROR_INVALID_PARAMETER\n", ret );
798     needed = 0xdeadbeef;
799     ret = gGetPerAdapterInfo(1, NULL, &needed);
800     if (ret == ERROR_NO_DATA) return;  /* no such adapter */
801     ok( ret == ERROR_BUFFER_OVERFLOW, "got %u instead of ERROR_BUFFER_OVERFLOW\n", ret );
802     ok( needed != 0xdeadbeef, "needed not set\n" );
803     buffer = HeapAlloc( GetProcessHeap(), 0, needed );
804     ret = gGetPerAdapterInfo(1, buffer, &needed);
805     ok( ret == NO_ERROR, "got %u instead of NO_ERROR\n", ret );
806     HeapFree( GetProcessHeap(), 0, buffer );
807 }
808
809 static void testNotifyAddrChange(void)
810 {
811     DWORD ret, bytes;
812     OVERLAPPED overlapped;
813     HANDLE handle;
814     BOOL success;
815
816     if (!gNotifyAddrChange)
817     {
818         win_skip("NotifyAddrChange not present\n");
819         return;
820     }
821     if (!gCancelIPChangeNotify)
822     {
823         win_skip("CancelIPChangeNotify not present\n");
824         return;
825     }
826
827     handle = NULL;
828     ZeroMemory(&overlapped, sizeof(overlapped));
829     ret = gNotifyAddrChange(&handle, &overlapped);
830     if (ret == ERROR_NOT_SUPPORTED)
831     {
832         win_skip("NotifyAddrChange is not supported\n");
833         return;
834     }
835     ok(ret == ERROR_IO_PENDING, "NotifyAddrChange returned %d, expected ERROR_IO_PENDING\n", ret);
836     ret = GetLastError();
837     todo_wine ok(ret == ERROR_IO_PENDING, "GetLastError returned %d, expected ERROR_IO_PENDING\n", ret);
838     success = gCancelIPChangeNotify(&overlapped);
839     todo_wine ok(success == TRUE, "CancelIPChangeNotify returned FALSE, expected TRUE\n");
840
841     ZeroMemory(&overlapped, sizeof(overlapped));
842     success = gCancelIPChangeNotify(&overlapped);
843     ok(success == FALSE, "CancelIPChangeNotify returned TRUE, expected FALSE\n");
844
845     handle = NULL;
846     ZeroMemory(&overlapped, sizeof(overlapped));
847     overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
848     ret = gNotifyAddrChange(&handle, &overlapped);
849     ok(ret == ERROR_IO_PENDING, "NotifyAddrChange returned %d, expected ERROR_IO_PENDING\n", ret);
850     todo_wine ok(handle != INVALID_HANDLE_VALUE, "NotifyAddrChange returned invalid file handle\n");
851     success = GetOverlappedResult(handle, &overlapped, &bytes, FALSE);
852     ok(success == FALSE, "GetOverlappedResult returned TRUE, expected FALSE\n");
853     ret = GetLastError();
854     ok(ret == ERROR_IO_INCOMPLETE, "GetLastError returned %d, expected ERROR_IO_INCOMPLETE\n", ret);
855     success = gCancelIPChangeNotify(&overlapped);
856     todo_wine ok(success == TRUE, "CancelIPChangeNotify returned FALSE, expected TRUE\n");
857
858     if (winetest_interactive)
859     {
860         handle = NULL;
861         ZeroMemory(&overlapped, sizeof(overlapped));
862         overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
863         trace("Testing asynchronous ipv4 address change notification. Please "
864               "change the ipv4 address of one of your network interfaces\n");
865         ret = gNotifyAddrChange(&handle, &overlapped);
866         ok(ret == ERROR_IO_PENDING, "NotifyAddrChange returned %d, expected NO_ERROR\n", ret);
867         success = GetOverlappedResult(handle, &overlapped, &bytes, TRUE);
868         ok(success == TRUE, "GetOverlappedResult returned FALSE, expected TRUE\n");
869     }
870
871     /* test synchronous functionality */
872     if (winetest_interactive)
873     {
874         trace("Testing synchronous ipv4 address change notification. Please "
875               "change the ipv4 address of one of your network interfaces\n");
876         ret = gNotifyAddrChange(NULL, NULL);
877         todo_wine ok(ret == NO_ERROR, "NotifyAddrChange returned %d, expected NO_ERROR\n", ret);
878     }
879 }
880
881 /*
882 still-to-be-tested 2K-onward functions:
883 AddIPAddress
884 CreateProxyArpEntry
885 DeleteIPAddress
886 DeleteProxyArpEntry
887 EnableRouter
888 FlushIpNetTable
889 GetAdapterIndex
890 NotifyRouteChange + CancelIPChangeNotify
891 SendARP
892 UnenableRouter
893 */
894 static void testWin2KFunctions(void)
895 {
896     testGetPerAdapterInfo();
897     testNotifyAddrChange();
898 }
899
900 static void test_GetAdaptersAddresses(void)
901 {
902     ULONG ret, size;
903     IP_ADAPTER_ADDRESSES *aa, *ptr;
904     IP_ADAPTER_UNICAST_ADDRESS *ua;
905
906     if (!gGetAdaptersAddresses)
907     {
908         win_skip("GetAdaptersAddresses not present\n");
909         return;
910     }
911
912     ret = gGetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, NULL);
913     ok(ret == ERROR_INVALID_PARAMETER, "expected ERROR_INVALID_PARAMETER got %u\n", ret);
914
915     /* size should be ignored and overwritten if buffer is NULL */
916     size = 0x7fffffff;
917     ret = gGetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &size);
918     ok(ret == ERROR_BUFFER_OVERFLOW, "expected ERROR_BUFFER_OVERFLOW, got %u\n", ret);
919     if (ret != ERROR_BUFFER_OVERFLOW) return;
920
921     ptr = HeapAlloc(GetProcessHeap(), 0, size);
922     ret = gGetAdaptersAddresses(AF_UNSPEC, 0, NULL, ptr, &size);
923     ok(!ret, "expected ERROR_SUCCESS got %u\n", ret);
924
925     for (aa = ptr; !ret && aa; aa = aa->Next)
926     {
927         ok(aa->DnsSuffix != NULL, "DnsSuffix is not a valid pointer\n");
928         ok(aa->Description != NULL, "Description is not a valid pointer\n");
929         ok(aa->FriendlyName != NULL, "FriendlyName is not a valid pointer\n");
930
931         if (winetest_debug <= 1)
932             continue;
933
934         trace("Length:                %u\n", S(U(*aa)).Length);
935         trace("IfIndex:               %u\n", S(U(*aa)).IfIndex);
936         trace("Next:                  %p\n", aa->Next);
937         trace("AdapterName:           %s\n", aa->AdapterName);
938         trace("FirstUnicastAddress:   %p\n", aa->FirstUnicastAddress);
939         ua = aa->FirstUnicastAddress;
940         while (ua)
941         {
942             trace("\tLength:                  %u\n", S(U(*ua)).Length);
943             trace("\tFlags:                   0x%08x\n", S(U(*ua)).Flags);
944             trace("\tNext:                    %p\n", ua->Next);
945             trace("\tAddress.lpSockaddr:      %p\n", ua->Address.lpSockaddr);
946             trace("\tAddress.iSockaddrLength: %d\n", ua->Address.iSockaddrLength);
947             trace("\tPrefixOrigin:            %u\n", ua->PrefixOrigin);
948             trace("\tSuffixOrigin:            %u\n", ua->SuffixOrigin);
949             trace("\tDadState:                %u\n", ua->DadState);
950             trace("\tValidLifetime:           0x%08x\n", ua->ValidLifetime);
951             trace("\tPreferredLifetime:       0x%08x\n", ua->PreferredLifetime);
952             trace("\tLeaseLifetime:           0x%08x\n", ua->LeaseLifetime);
953             trace("\n");
954             ua = ua->Next;
955         }
956         trace("FirstAnycastAddress:   %p\n", aa->FirstAnycastAddress);
957         trace("FirstMulticastAddress: %p\n", aa->FirstMulticastAddress);
958         trace("FirstDnsServerAddress: %p\n", aa->FirstDnsServerAddress);
959         trace("DnsSuffix:             %p\n", aa->DnsSuffix);
960         trace("Description:           %p\n", aa->Description);
961         trace("FriendlyName:          %p\n", aa->FriendlyName);
962         trace("PhysicalAddress:       %02x\n", aa->PhysicalAddress[0]);
963         trace("PhysicalAddressLength: %u\n", aa->PhysicalAddressLength);
964         trace("Flags:                 0x%08x\n", aa->Flags);
965         trace("Mtu:                   %u\n", aa->Mtu);
966         trace("IfType:                %u\n", aa->IfType);
967         trace("OperStatus:            %u\n", aa->OperStatus);
968         trace("\n");
969     }
970     HeapFree(GetProcessHeap(), 0, ptr);
971 }
972
973 START_TEST(iphlpapi)
974 {
975
976   loadIPHlpApi();
977   if (hLibrary) {
978     HANDLE thread;
979
980     testWin98OnlyFunctions();
981     testWinNT4Functions();
982
983     /* run testGetXXXX in two threads at once to make sure we don't crash in that case */
984     thread = CreateThread(NULL, 0, testWin98Functions, NULL, 0, NULL);
985     testWin98Functions(NULL);
986     WaitForSingleObject(thread, INFINITE);
987
988     testWin2KFunctions();
989     test_GetAdaptersAddresses();
990     freeIPHlpApi();
991   }
992 }