wininet: Send notifications from InternetWriteFile.
[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
67 static GetNumberOfInterfacesFunc gGetNumberOfInterfaces = NULL;
68 static GetIpAddrTableFunc gGetIpAddrTable = NULL;
69 static GetIfEntryFunc gGetIfEntry = NULL;
70 static GetFriendlyIfIndexFunc gGetFriendlyIfIndex = NULL;
71 static GetIfTableFunc gGetIfTable = NULL;
72 static GetIpForwardTableFunc gGetIpForwardTable = NULL;
73 static GetIpNetTableFunc gGetIpNetTable = NULL;
74 static GetInterfaceInfoFunc gGetInterfaceInfo = NULL;
75 static GetAdaptersInfoFunc gGetAdaptersInfo = NULL;
76 static GetNetworkParamsFunc gGetNetworkParams = NULL;
77 static GetIcmpStatisticsFunc gGetIcmpStatistics = NULL;
78 static GetIpStatisticsFunc gGetIpStatistics = NULL;
79 static GetTcpStatisticsFunc gGetTcpStatistics = NULL;
80 static GetUdpStatisticsFunc gGetUdpStatistics = NULL;
81 static GetTcpTableFunc gGetTcpTable = NULL;
82 static GetUdpTableFunc gGetUdpTable = NULL;
83 static GetPerAdapterInfoFunc gGetPerAdapterInfo = NULL;
84
85 static void loadIPHlpApi(void)
86 {
87   hLibrary = LoadLibraryA("iphlpapi.dll");
88   if (hLibrary) {
89     gGetNumberOfInterfaces = (GetNumberOfInterfacesFunc)GetProcAddress(
90      hLibrary, "GetNumberOfInterfaces");
91     gGetIpAddrTable = (GetIpAddrTableFunc)GetProcAddress(
92      hLibrary, "GetIpAddrTable");
93     gGetIfEntry = (GetIfEntryFunc)GetProcAddress(
94      hLibrary, "GetIfEntry");
95     gGetFriendlyIfIndex = (GetFriendlyIfIndexFunc)GetProcAddress(
96      hLibrary, "GetFriendlyIfIndex");
97     gGetIfTable = (GetIfTableFunc)GetProcAddress(
98      hLibrary, "GetIfTable");
99     gGetIpForwardTable = (GetIpForwardTableFunc)GetProcAddress(
100      hLibrary, "GetIpForwardTable");
101     gGetIpNetTable = (GetIpNetTableFunc)GetProcAddress(
102      hLibrary, "GetIpNetTable");
103     gGetInterfaceInfo = (GetInterfaceInfoFunc)GetProcAddress(
104      hLibrary, "GetInterfaceInfo");
105     gGetAdaptersInfo = (GetAdaptersInfoFunc)GetProcAddress(
106      hLibrary, "GetAdaptersInfo");
107     gGetNetworkParams = (GetNetworkParamsFunc)GetProcAddress(
108      hLibrary, "GetNetworkParams");
109     gGetIcmpStatistics = (GetIcmpStatisticsFunc)GetProcAddress(
110      hLibrary, "GetIcmpStatistics");
111     gGetIpStatistics = (GetIpStatisticsFunc)GetProcAddress(
112      hLibrary, "GetIpStatistics");
113     gGetTcpStatistics = (GetTcpStatisticsFunc)GetProcAddress(
114      hLibrary, "GetTcpStatistics");
115     gGetUdpStatistics = (GetUdpStatisticsFunc)GetProcAddress(
116      hLibrary, "GetUdpStatistics");
117     gGetTcpTable = (GetTcpTableFunc)GetProcAddress(
118      hLibrary, "GetTcpTable");
119     gGetUdpTable = (GetUdpTableFunc)GetProcAddress(
120      hLibrary, "GetUdpTable");
121     gGetPerAdapterInfo = (GetPerAdapterInfoFunc)GetProcAddress(hLibrary, "GetPerAdapterInfo");
122   }
123 }
124
125 static void freeIPHlpApi(void)
126 {
127   if (hLibrary) {
128     gGetNumberOfInterfaces = NULL;
129     gGetIpAddrTable = NULL;
130     gGetIfEntry = NULL;
131     gGetFriendlyIfIndex = NULL;
132     gGetIfTable = NULL;
133     gGetIpForwardTable = NULL;
134     gGetIpNetTable = NULL;
135     gGetInterfaceInfo = NULL;
136     gGetAdaptersInfo = NULL;
137     gGetNetworkParams = NULL;
138     gGetIcmpStatistics = NULL;
139     gGetIpStatistics = NULL;
140     gGetTcpStatistics = NULL;
141     gGetUdpStatistics = NULL;
142     gGetTcpTable = NULL;
143     gGetUdpTable = NULL;
144     FreeLibrary(hLibrary);
145     hLibrary = NULL;
146   }
147 }
148
149 /* replacement for inet_ntoa */
150 static const char *ntoa( DWORD ip )
151 {
152     static char buffer[40];
153
154     ip = htonl(ip);
155     sprintf( buffer, "%u.%u.%u.%u", (ip >> 24) & 0xff, (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff );
156     return buffer;
157 }
158
159 /*
160 still-to-be-tested 98-only functions:
161 GetUniDirectionalAdapterInfo
162 */
163 static void testWin98OnlyFunctions(void)
164 {
165 }
166
167 static void testGetNumberOfInterfaces(void)
168 {
169   if (gGetNumberOfInterfaces) {
170     DWORD apiReturn, numInterfaces;
171
172     /* Crashes on Vista */
173     if (0) {
174       apiReturn = gGetNumberOfInterfaces(NULL), numInterfaces;
175       if (apiReturn == ERROR_NOT_SUPPORTED)
176         return;
177       ok(apiReturn == ERROR_INVALID_PARAMETER,
178        "GetNumberOfInterfaces(NULL) returned %d, expected ERROR_INVALID_PARAMETER\n",
179        apiReturn);
180     }
181
182     apiReturn = gGetNumberOfInterfaces(&numInterfaces);
183     if (apiReturn == ERROR_NOT_SUPPORTED) {
184       skip("GetNumberOfInterfaces is not supported\n");
185       return;
186     }
187     ok(apiReturn == NO_ERROR,
188      "GetNumberOfInterfaces returned %d, expected 0\n", apiReturn);
189   }
190 }
191
192 static void testGetIfEntry(DWORD index)
193 {
194   if (gGetIfEntry) {
195     DWORD apiReturn;
196     MIB_IFROW row;
197
198     memset(&row, 0, sizeof(row));
199     apiReturn = gGetIfEntry(NULL);
200     if (apiReturn == ERROR_NOT_SUPPORTED) {
201       skip("GetIfEntry is not supported\n");
202       return;
203     }
204     ok(apiReturn == ERROR_INVALID_PARAMETER,
205      "GetIfEntry(NULL) returned %d, expected ERROR_INVALID_PARAMETER\n",
206      apiReturn);
207     row.dwIndex = -1; /* hope that's always bogus! */
208     apiReturn = gGetIfEntry(&row);
209     ok(apiReturn == ERROR_INVALID_DATA ||
210      apiReturn == ERROR_FILE_NOT_FOUND /* Vista */,
211      "GetIfEntry(bogus row) returned %d, expected ERROR_INVALID_DATA or ERROR_FILE_NOT_FOUND\n",
212      apiReturn);
213     row.dwIndex = index;
214     apiReturn = gGetIfEntry(&row);
215     ok(apiReturn == NO_ERROR, 
216      "GetIfEntry returned %d, expected NO_ERROR\n", apiReturn);
217   }
218 }
219
220 static void testGetIpAddrTable(void)
221 {
222   if (gGetIpAddrTable) {
223     DWORD apiReturn;
224     ULONG dwSize = 0;
225
226     apiReturn = gGetIpAddrTable(NULL, NULL, FALSE);
227     if (apiReturn == ERROR_NOT_SUPPORTED) {
228       skip("GetIpAddrTable is not supported\n");
229       return;
230     }
231     ok(apiReturn == ERROR_INVALID_PARAMETER,
232      "GetIpAddrTable(NULL, NULL, FALSE) returned %d, expected ERROR_INVALID_PARAMETER\n",
233      apiReturn);
234     apiReturn = gGetIpAddrTable(NULL, &dwSize, FALSE);
235     ok(apiReturn == ERROR_INSUFFICIENT_BUFFER,
236      "GetIpAddrTable(NULL, &dwSize, FALSE) returned %d, expected ERROR_INSUFFICIENT_BUFFER\n",
237      apiReturn);
238     if (apiReturn == ERROR_INSUFFICIENT_BUFFER) {
239       PMIB_IPADDRTABLE buf = HeapAlloc(GetProcessHeap(), 0, dwSize);
240
241       apiReturn = gGetIpAddrTable(buf, &dwSize, FALSE);
242       ok(apiReturn == NO_ERROR,
243        "GetIpAddrTable(buf, &dwSize, FALSE) returned %d, expected NO_ERROR\n",
244        apiReturn);
245       if (apiReturn == NO_ERROR && buf->dwNumEntries)
246         testGetIfEntry(buf->table[0].dwIndex);
247       HeapFree(GetProcessHeap(), 0, buf);
248     }
249   }
250 }
251
252 static void testGetIfTable(void)
253 {
254   if (gGetIfTable) {
255     DWORD apiReturn;
256     ULONG dwSize = 0;
257
258     apiReturn = gGetIfTable(NULL, NULL, FALSE);
259     if (apiReturn == ERROR_NOT_SUPPORTED) {
260       skip("GetIfTable is not supported\n");
261       return;
262     }
263     ok(apiReturn == ERROR_INVALID_PARAMETER,
264      "GetIfTable(NULL, NULL, FALSE) returned %d, expected ERROR_INVALID_PARAMETER\n",
265      apiReturn);
266     apiReturn = gGetIfTable(NULL, &dwSize, FALSE);
267     ok(apiReturn == ERROR_INSUFFICIENT_BUFFER,
268      "GetIfTable(NULL, &dwSize, FALSE) returned %d, expected ERROR_INSUFFICIENT_BUFFER\n",
269      apiReturn);
270     if (apiReturn == ERROR_INSUFFICIENT_BUFFER) {
271       PMIB_IFTABLE buf = HeapAlloc(GetProcessHeap(), 0, dwSize);
272
273       apiReturn = gGetIfTable(buf, &dwSize, FALSE);
274       ok(apiReturn == NO_ERROR,
275        "GetIfTable(buf, &dwSize, FALSE) returned %d, expected NO_ERROR\n\n",
276        apiReturn);
277
278       if (apiReturn == NO_ERROR && winetest_debug > 1)
279       {
280           DWORD i, j;
281           char name[MAX_INTERFACE_NAME_LEN];
282
283           trace( "interface table: %u entries\n", buf->dwNumEntries );
284           for (i = 0; i < buf->dwNumEntries; i++)
285           {
286               MIB_IFROW *row = &buf->table[i];
287               WideCharToMultiByte( CP_ACP, 0, row->wszName, -1, name, MAX_INTERFACE_NAME_LEN, NULL, NULL );
288               trace( "%u: '%s' type %u mtu %u speed %u phys",
289                      row->dwIndex, name, row->dwType, row->dwMtu, row->dwSpeed );
290               for (j = 0; j < row->dwPhysAddrLen; j++)
291                   printf( " %02x", row->bPhysAddr[j] );
292               printf( "\n" );
293               trace( "        in: bytes %u upkts %u nupkts %u disc %u err %u unk %u\n",
294                      row->dwInOctets, row->dwInUcastPkts, row->dwInNUcastPkts,
295                      row->dwInDiscards, row->dwInErrors, row->dwInUnknownProtos );
296               trace( "        out: bytes %u upkts %u nupkts %u disc %u err %u\n",
297                      row->dwOutOctets, row->dwOutUcastPkts, row->dwOutNUcastPkts,
298                      row->dwOutDiscards, row->dwOutErrors );
299           }
300       }
301       HeapFree(GetProcessHeap(), 0, buf);
302     }
303   }
304 }
305
306 static void testGetIpForwardTable(void)
307 {
308   if (gGetIpForwardTable) {
309     DWORD apiReturn;
310     ULONG dwSize = 0;
311
312     apiReturn = gGetIpForwardTable(NULL, NULL, FALSE);
313     if (apiReturn == ERROR_NOT_SUPPORTED) {
314       skip("GetIpForwardTable is not supported\n");
315       return;
316     }
317     ok(apiReturn == ERROR_INVALID_PARAMETER,
318      "GetIpForwardTable(NULL, NULL, FALSE) returned %d, expected ERROR_INVALID_PARAMETER\n",
319      apiReturn);
320     apiReturn = gGetIpForwardTable(NULL, &dwSize, FALSE);
321     ok(apiReturn == ERROR_INSUFFICIENT_BUFFER,
322      "GetIpForwardTable(NULL, &dwSize, FALSE) returned %d, expected ERROR_INSUFFICIENT_BUFFER\n",
323      apiReturn);
324     if (apiReturn == ERROR_INSUFFICIENT_BUFFER) {
325       PMIB_IPFORWARDTABLE buf = HeapAlloc(GetProcessHeap(), 0, dwSize);
326
327       apiReturn = gGetIpForwardTable(buf, &dwSize, FALSE);
328       ok(apiReturn == NO_ERROR,
329        "GetIpForwardTable(buf, &dwSize, FALSE) returned %d, expected NO_ERROR\n",
330        apiReturn);
331
332       if (apiReturn == NO_ERROR && winetest_debug > 1)
333       {
334           DWORD i;
335
336           trace( "IP forward table: %u entries\n", buf->dwNumEntries );
337           for (i = 0; i < buf->dwNumEntries; i++)
338           {
339               char buffer[40];
340               sprintf( buffer, "dest %s", ntoa( buf->table[i].dwForwardDest ));
341               sprintf( buffer + strlen(buffer), " mask %s", ntoa( buf->table[i].dwForwardMask ));
342               trace( "%u: %s gw %s if %u type %u\n", i, buffer,
343                      ntoa( buf->table[i].dwForwardNextHop ),
344                      buf->table[i].dwForwardIfIndex, buf->table[i].dwForwardType );
345           }
346       }
347       HeapFree(GetProcessHeap(), 0, buf);
348     }
349   }
350 }
351
352 static void testGetIpNetTable(void)
353 {
354   if (gGetIpNetTable) {
355     DWORD apiReturn;
356     ULONG dwSize = 0;
357
358     apiReturn = gGetIpNetTable(NULL, NULL, FALSE);
359     if (apiReturn == ERROR_NOT_SUPPORTED) {
360       skip("GetIpNetTable is not supported\n");
361       return;
362     }
363     ok(apiReturn == ERROR_INVALID_PARAMETER,
364      "GetIpNetTable(NULL, NULL, FALSE) returned %d, expected ERROR_INVALID_PARAMETER\n",
365      apiReturn);
366     apiReturn = gGetIpNetTable(NULL, &dwSize, FALSE);
367     ok(apiReturn == ERROR_NO_DATA || apiReturn == ERROR_INSUFFICIENT_BUFFER,
368      "GetIpNetTable(NULL, &dwSize, FALSE) returned %d, expected ERROR_NO_DATA or ERROR_INSUFFICIENT_BUFFER\n",
369      apiReturn);
370     if (apiReturn == ERROR_NO_DATA)
371       ; /* empty ARP table's okay */
372     else if (apiReturn == ERROR_INSUFFICIENT_BUFFER) {
373       PMIB_IPNETTABLE buf = HeapAlloc(GetProcessHeap(), 0, dwSize);
374
375       apiReturn = gGetIpNetTable(buf, &dwSize, FALSE);
376       ok(apiReturn == NO_ERROR ||
377          apiReturn == ERROR_NO_DATA, /* empty ARP table's okay */
378        "GetIpNetTable(buf, &dwSize, FALSE) returned %d, expected NO_ERROR\n",
379        apiReturn);
380
381       if (apiReturn == NO_ERROR && winetest_debug > 1)
382       {
383           DWORD i, j;
384
385           trace( "IP net table: %u entries\n", buf->dwNumEntries );
386           for (i = 0; i < buf->dwNumEntries; i++)
387           {
388               trace( "%u: idx %u type %u addr %s phys",
389                      i, buf->table[i].dwIndex, buf->table[i].dwType, ntoa( buf->table[i].dwAddr ));
390               for (j = 0; j < buf->table[i].dwPhysAddrLen; j++)
391                   printf( " %02x", buf->table[i].bPhysAddr[j] );
392               printf( "\n" );
393           }
394       }
395       HeapFree(GetProcessHeap(), 0, buf);
396     }
397   }
398 }
399
400 static void testGetIcmpStatistics(void)
401 {
402   if (gGetIcmpStatistics) {
403     DWORD apiReturn;
404     MIB_ICMP stats;
405
406     /* Crashes on Vista */
407     if (0) {
408       apiReturn = gGetIcmpStatistics(NULL);
409       if (apiReturn == ERROR_NOT_SUPPORTED)
410         return;
411       ok(apiReturn == ERROR_INVALID_PARAMETER,
412        "GetIcmpStatistics(NULL) returned %d, expected ERROR_INVALID_PARAMETER\n",
413        apiReturn);
414     }
415
416     apiReturn = gGetIcmpStatistics(&stats);
417     if (apiReturn == ERROR_NOT_SUPPORTED)
418     {
419       skip("GetIcmpStatistics is not supported\n");
420       return;
421     }
422     ok(apiReturn == NO_ERROR,
423      "GetIcmpStatistics returned %d, expected NO_ERROR\n", apiReturn);
424     if (apiReturn == NO_ERROR && winetest_debug > 1)
425     {
426         trace( "ICMP stats:          %8s %8s\n", "in", "out" );
427         trace( "    dwMsgs:          %8u %8u\n", stats.stats.icmpInStats.dwMsgs, stats.stats.icmpOutStats.dwMsgs );
428         trace( "    dwErrors:        %8u %8u\n", stats.stats.icmpInStats.dwErrors, stats.stats.icmpOutStats.dwErrors );
429         trace( "    dwDestUnreachs:  %8u %8u\n", stats.stats.icmpInStats.dwDestUnreachs, stats.stats.icmpOutStats.dwDestUnreachs );
430         trace( "    dwTimeExcds:     %8u %8u\n", stats.stats.icmpInStats.dwTimeExcds, stats.stats.icmpOutStats.dwTimeExcds );
431         trace( "    dwParmProbs:     %8u %8u\n", stats.stats.icmpInStats.dwParmProbs, stats.stats.icmpOutStats.dwParmProbs );
432         trace( "    dwSrcQuenchs:    %8u %8u\n", stats.stats.icmpInStats.dwSrcQuenchs, stats.stats.icmpOutStats.dwSrcQuenchs );
433         trace( "    dwRedirects:     %8u %8u\n", stats.stats.icmpInStats.dwRedirects, stats.stats.icmpOutStats.dwRedirects );
434         trace( "    dwEchos:         %8u %8u\n", stats.stats.icmpInStats.dwEchos, stats.stats.icmpOutStats.dwEchos );
435         trace( "    dwEchoReps:      %8u %8u\n", stats.stats.icmpInStats.dwEchoReps, stats.stats.icmpOutStats.dwEchoReps );
436         trace( "    dwTimestamps:    %8u %8u\n", stats.stats.icmpInStats.dwTimestamps, stats.stats.icmpOutStats.dwTimestamps );
437         trace( "    dwTimestampReps: %8u %8u\n", stats.stats.icmpInStats.dwTimestampReps, stats.stats.icmpOutStats.dwTimestampReps );
438         trace( "    dwAddrMasks:     %8u %8u\n", stats.stats.icmpInStats.dwAddrMasks, stats.stats.icmpOutStats.dwAddrMasks );
439         trace( "    dwAddrMaskReps:  %8u %8u\n", stats.stats.icmpInStats.dwAddrMaskReps, stats.stats.icmpOutStats.dwAddrMaskReps );
440     }
441   }
442 }
443
444 static void testGetIpStatistics(void)
445 {
446   if (gGetIpStatistics) {
447     DWORD apiReturn;
448     MIB_IPSTATS stats;
449
450     apiReturn = gGetIpStatistics(NULL);
451     if (apiReturn == ERROR_NOT_SUPPORTED) {
452       skip("GetIpStatistics is not supported\n");
453       return;
454     }
455     ok(apiReturn == ERROR_INVALID_PARAMETER,
456      "GetIpStatistics(NULL) returned %d, expected ERROR_INVALID_PARAMETER\n",
457      apiReturn);
458     apiReturn = gGetIpStatistics(&stats);
459     ok(apiReturn == NO_ERROR,
460       "GetIpStatistics returned %d, expected NO_ERROR\n", apiReturn);
461     if (apiReturn == NO_ERROR && winetest_debug > 1)
462     {
463         trace( "IP stats:\n" );
464         trace( "    dwForwarding:      %u\n", stats.dwForwarding );
465         trace( "    dwDefaultTTL:      %u\n", stats.dwDefaultTTL );
466         trace( "    dwInReceives:      %u\n", stats.dwInReceives );
467         trace( "    dwInHdrErrors:     %u\n", stats.dwInHdrErrors );
468         trace( "    dwInAddrErrors:    %u\n", stats.dwInAddrErrors );
469         trace( "    dwForwDatagrams:   %u\n", stats.dwForwDatagrams );
470         trace( "    dwInUnknownProtos: %u\n", stats.dwInUnknownProtos );
471         trace( "    dwInDiscards:      %u\n", stats.dwInDiscards );
472         trace( "    dwInDelivers:      %u\n", stats.dwInDelivers );
473         trace( "    dwOutRequests:     %u\n", stats.dwOutRequests );
474         trace( "    dwRoutingDiscards: %u\n", stats.dwRoutingDiscards );
475         trace( "    dwOutDiscards:     %u\n", stats.dwOutDiscards );
476         trace( "    dwOutNoRoutes:     %u\n", stats.dwOutNoRoutes );
477         trace( "    dwReasmTimeout:    %u\n", stats.dwReasmTimeout );
478         trace( "    dwReasmReqds:      %u\n", stats.dwReasmReqds );
479         trace( "    dwReasmOks:        %u\n", stats.dwReasmOks );
480         trace( "    dwReasmFails:      %u\n", stats.dwReasmFails );
481         trace( "    dwFragOks:         %u\n", stats.dwFragOks );
482         trace( "    dwFragFails:       %u\n", stats.dwFragFails );
483         trace( "    dwFragCreates:     %u\n", stats.dwFragCreates );
484         trace( "    dwNumIf:           %u\n", stats.dwNumIf );
485         trace( "    dwNumAddr:         %u\n", stats.dwNumAddr );
486         trace( "    dwNumRoutes:       %u\n", stats.dwNumRoutes );
487     }
488   }
489 }
490
491 static void testGetTcpStatistics(void)
492 {
493   if (gGetTcpStatistics) {
494     DWORD apiReturn;
495     MIB_TCPSTATS stats;
496
497     apiReturn = gGetTcpStatistics(NULL);
498     if (apiReturn == ERROR_NOT_SUPPORTED) {
499       skip("GetTcpStatistics is not supported\n");
500       return;
501     }
502     ok(apiReturn == ERROR_INVALID_PARAMETER,
503      "GetTcpStatistics(NULL) returned %d, expected ERROR_INVALID_PARAMETER\n",
504      apiReturn);
505     apiReturn = gGetTcpStatistics(&stats);
506     ok(apiReturn == NO_ERROR,
507       "GetTcpStatistics returned %d, expected NO_ERROR\n", apiReturn);
508     if (apiReturn == NO_ERROR && winetest_debug > 1)
509     {
510         trace( "TCP stats:\n" );
511         trace( "    dwRtoAlgorithm: %u\n", stats.dwRtoAlgorithm );
512         trace( "    dwRtoMin:       %u\n", stats.dwRtoMin );
513         trace( "    dwRtoMax:       %u\n", stats.dwRtoMax );
514         trace( "    dwMaxConn:      %u\n", stats.dwMaxConn );
515         trace( "    dwActiveOpens:  %u\n", stats.dwActiveOpens );
516         trace( "    dwPassiveOpens: %u\n", stats.dwPassiveOpens );
517         trace( "    dwAttemptFails: %u\n", stats.dwAttemptFails );
518         trace( "    dwEstabResets:  %u\n", stats.dwEstabResets );
519         trace( "    dwCurrEstab:    %u\n", stats.dwCurrEstab );
520         trace( "    dwInSegs:       %u\n", stats.dwInSegs );
521         trace( "    dwOutSegs:      %u\n", stats.dwOutSegs );
522         trace( "    dwRetransSegs:  %u\n", stats.dwRetransSegs );
523         trace( "    dwInErrs:       %u\n", stats.dwInErrs );
524         trace( "    dwOutRsts:      %u\n", stats.dwOutRsts );
525         trace( "    dwNumConns:     %u\n", stats.dwNumConns );
526     }
527   }
528 }
529
530 static void testGetUdpStatistics(void)
531 {
532   if (gGetUdpStatistics) {
533     DWORD apiReturn;
534     MIB_UDPSTATS stats;
535
536     apiReturn = gGetUdpStatistics(NULL);
537     if (apiReturn == ERROR_NOT_SUPPORTED) {
538       skip("GetUdpStatistics is not supported\n");
539       return;
540     }
541     ok(apiReturn == ERROR_INVALID_PARAMETER,
542      "GetUdpStatistics(NULL) returned %d, expected ERROR_INVALID_PARAMETER\n",
543      apiReturn);
544     apiReturn = gGetUdpStatistics(&stats);
545     ok(apiReturn == NO_ERROR,
546      "GetUdpStatistics returned %d, expected NO_ERROR\n", apiReturn);
547     if (apiReturn == NO_ERROR && winetest_debug > 1)
548     {
549         trace( "UDP stats:\n" );
550         trace( "    dwInDatagrams:  %u\n", stats.dwInDatagrams );
551         trace( "    dwNoPorts:      %u\n", stats.dwNoPorts );
552         trace( "    dwInErrors:     %u\n", stats.dwInErrors );
553         trace( "    dwOutDatagrams: %u\n", stats.dwOutDatagrams );
554         trace( "    dwNumAddrs:     %u\n", stats.dwNumAddrs );
555     }
556   }
557 }
558
559 static void testGetTcpTable(void)
560 {
561   if (gGetTcpTable) {
562     DWORD apiReturn;
563     ULONG dwSize = 0;
564
565     apiReturn = gGetTcpTable(NULL, &dwSize, FALSE);
566     if (apiReturn == ERROR_NOT_SUPPORTED) {
567       skip("GetTcpTable is not supported\n");
568       return;
569     }
570     ok(apiReturn == ERROR_INSUFFICIENT_BUFFER ||
571        broken(apiReturn == ERROR_NO_DATA), /* win95 */
572      "GetTcpTable(NULL, &dwSize, FALSE) returned %d, expected ERROR_INSUFFICIENT_BUFFER\n",
573      apiReturn);
574     if (apiReturn == ERROR_INSUFFICIENT_BUFFER) {
575       PMIB_TCPTABLE buf = HeapAlloc(GetProcessHeap(), 0, dwSize);
576
577       apiReturn = gGetTcpTable(buf, &dwSize, FALSE);
578       ok(apiReturn == NO_ERROR,
579        "GetTcpTable(buf, &dwSize, FALSE) returned %d, expected NO_ERROR\n",
580        apiReturn);
581
582       if (apiReturn == NO_ERROR && winetest_debug > 1)
583       {
584           DWORD i;
585           trace( "TCP table: %u entries\n", buf->dwNumEntries );
586           for (i = 0; i < buf->dwNumEntries; i++)
587           {
588               char buffer[40];
589               sprintf( buffer, "local %s:%u",
590                        ntoa(buf->table[i].dwLocalAddr), ntohs(buf->table[i].dwLocalPort) );
591               trace( "%u: %s remote %s:%u state %u\n",
592                      i, buffer, ntoa( buf->table[i].dwRemoteAddr ),
593                      ntohs(buf->table[i].dwRemotePort), buf->table[i].dwState );
594           }
595       }
596       HeapFree(GetProcessHeap(), 0, buf);
597     }
598   }
599 }
600
601 static void testGetUdpTable(void)
602 {
603   if (gGetUdpTable) {
604     DWORD apiReturn;
605     ULONG dwSize = 0;
606
607     apiReturn = gGetUdpTable(NULL, &dwSize, FALSE);
608     if (apiReturn == ERROR_NOT_SUPPORTED) {
609       skip("GetUdpTable is not supported\n");
610       return;
611     }
612     ok(apiReturn == ERROR_INSUFFICIENT_BUFFER,
613      "GetUdpTable(NULL, &dwSize, FALSE) returned %d, expected ERROR_INSUFFICIENT_BUFFER\n",
614      apiReturn);
615     if (apiReturn == ERROR_INSUFFICIENT_BUFFER) {
616       PMIB_UDPTABLE buf = HeapAlloc(GetProcessHeap(), 0, dwSize);
617
618       apiReturn = gGetUdpTable(buf, &dwSize, FALSE);
619       ok(apiReturn == NO_ERROR,
620        "GetUdpTable(buf, &dwSize, FALSE) returned %d, expected NO_ERROR\n",
621        apiReturn);
622
623       if (apiReturn == NO_ERROR && winetest_debug > 1)
624       {
625           DWORD i;
626           trace( "UDP table: %u entries\n", buf->dwNumEntries );
627           for (i = 0; i < buf->dwNumEntries; i++)
628               trace( "%u: %s:%u\n",
629                      i, ntoa( buf->table[i].dwLocalAddr ), ntohs(buf->table[i].dwLocalPort) );
630       }
631       HeapFree(GetProcessHeap(), 0, buf);
632     }
633   }
634 }
635
636 /*
637 still-to-be-tested NT4-onward functions:
638 CreateIpForwardEntry
639 DeleteIpForwardEntry
640 CreateIpNetEntry
641 DeleteIpNetEntry
642 GetFriendlyIfIndex
643 GetRTTAndHopCount
644 SetIfEntry
645 SetIpForwardEntry
646 SetIpNetEntry
647 SetIpStatistics
648 SetIpTTL
649 SetTcpEntry
650 */
651 static void testWinNT4Functions(void)
652 {
653   testGetNumberOfInterfaces();
654   testGetIpAddrTable();
655   testGetIfTable();
656   testGetIpForwardTable();
657   testGetIpNetTable();
658   testGetIcmpStatistics();
659   testGetIpStatistics();
660   testGetTcpStatistics();
661   testGetUdpStatistics();
662   testGetTcpTable();
663   testGetUdpTable();
664 }
665
666 static void testGetInterfaceInfo(void)
667 {
668   if (gGetInterfaceInfo) {
669     DWORD apiReturn;
670     ULONG len = 0;
671
672     apiReturn = gGetInterfaceInfo(NULL, NULL);
673     if (apiReturn == ERROR_NOT_SUPPORTED) {
674       skip("GetInterfaceInfo is not supported\n");
675       return;
676     }
677     ok(apiReturn == ERROR_INVALID_PARAMETER,
678      "GetInterfaceInfo returned %d, expected ERROR_INVALID_PARAMETER\n",
679      apiReturn);
680     apiReturn = gGetInterfaceInfo(NULL, &len);
681     ok(apiReturn == ERROR_INSUFFICIENT_BUFFER,
682      "GetInterfaceInfo returned %d, expected ERROR_INSUFFICIENT_BUFFER\n",
683      apiReturn);
684     if (apiReturn == ERROR_INSUFFICIENT_BUFFER) {
685       PIP_INTERFACE_INFO buf = HeapAlloc(GetProcessHeap(), 0, len);
686
687       apiReturn = gGetInterfaceInfo(buf, &len);
688       ok(apiReturn == NO_ERROR,
689        "GetInterfaceInfo(buf, &dwSize) returned %d, expected NO_ERROR\n",
690        apiReturn);
691       HeapFree(GetProcessHeap(), 0, buf);
692     }
693   }
694 }
695
696 static void testGetAdaptersInfo(void)
697 {
698   if (gGetAdaptersInfo) {
699     DWORD apiReturn;
700     ULONG len = 0;
701
702     apiReturn = gGetAdaptersInfo(NULL, NULL);
703     if (apiReturn == ERROR_NOT_SUPPORTED) {
704       skip("GetAdaptersInfo is not supported\n");
705       return;
706     }
707     ok(apiReturn == ERROR_INVALID_PARAMETER,
708      "GetAdaptersInfo returned %d, expected ERROR_INVALID_PARAMETER\n",
709      apiReturn);
710     apiReturn = gGetAdaptersInfo(NULL, &len);
711     ok(apiReturn == ERROR_NO_DATA || apiReturn == ERROR_BUFFER_OVERFLOW,
712      "GetAdaptersInfo returned %d, expected ERROR_NO_DATA or ERROR_BUFFER_OVERFLOW\n",
713      apiReturn);
714     if (apiReturn == ERROR_NO_DATA)
715       ; /* no adapter's, that's okay */
716     else if (apiReturn == ERROR_BUFFER_OVERFLOW) {
717       PIP_ADAPTER_INFO buf = HeapAlloc(GetProcessHeap(), 0, len);
718
719       apiReturn = gGetAdaptersInfo(buf, &len);
720       ok(apiReturn == NO_ERROR,
721        "GetAdaptersInfo(buf, &dwSize) returned %d, expected NO_ERROR\n",
722        apiReturn);
723       HeapFree(GetProcessHeap(), 0, buf);
724     }
725   }
726 }
727
728 static void testGetNetworkParams(void)
729 {
730   if (gGetNetworkParams) {
731     DWORD apiReturn;
732     ULONG len = 0;
733
734     apiReturn = gGetNetworkParams(NULL, NULL);
735     if (apiReturn == ERROR_NOT_SUPPORTED) {
736       skip("GetNetworkParams is not supported\n");
737       return;
738     }
739     ok(apiReturn == ERROR_INVALID_PARAMETER,
740      "GetNetworkParams returned %d, expected ERROR_INVALID_PARAMETER\n",
741      apiReturn);
742     apiReturn = gGetNetworkParams(NULL, &len);
743     ok(apiReturn == ERROR_BUFFER_OVERFLOW,
744      "GetNetworkParams returned %d, expected ERROR_BUFFER_OVERFLOW\n",
745      apiReturn);
746     if (apiReturn == ERROR_BUFFER_OVERFLOW) {
747       PFIXED_INFO buf = HeapAlloc(GetProcessHeap(), 0, len);
748
749       apiReturn = gGetNetworkParams(buf, &len);
750       ok(apiReturn == NO_ERROR,
751        "GetNetworkParams(buf, &dwSize) returned %d, expected NO_ERROR\n",
752        apiReturn);
753       HeapFree(GetProcessHeap(), 0, buf);
754     }
755   }
756 }
757
758 /*
759 still-to-be-tested 98-onward functions:
760 GetBestInterface
761 GetBestRoute
762 IpReleaseAddress
763 IpRenewAddress
764 */
765 static void testWin98Functions(void)
766 {
767   testGetInterfaceInfo();
768   testGetAdaptersInfo();
769   testGetNetworkParams();
770 }
771
772 static void testGetPerAdapterInfo(void)
773 {
774     DWORD ret, needed;
775     void *buffer;
776
777     if (!gGetPerAdapterInfo) return;
778     ret = gGetPerAdapterInfo(1, NULL, NULL);
779     if (ret == ERROR_NOT_SUPPORTED) {
780       skip("GetPerAdapterInfo is not supported\n");
781       return;
782     }
783     ok( ret == ERROR_INVALID_PARAMETER, "got %u instead of ERROR_INVALID_PARAMETER\n", ret );
784     needed = 0xdeadbeef;
785     ret = gGetPerAdapterInfo(1, NULL, &needed);
786     if (ret == ERROR_NO_DATA) return;  /* no such adapter */
787     ok( ret == ERROR_BUFFER_OVERFLOW, "got %u instead of ERROR_BUFFER_OVERFLOW\n", ret );
788     ok( needed != 0xdeadbeef, "needed not set\n" );
789     buffer = HeapAlloc( GetProcessHeap(), 0, needed );
790     ret = gGetPerAdapterInfo(1, buffer, &needed);
791     ok( ret == NO_ERROR, "got %u instead of NO_ERROR\n", ret );
792     HeapFree( GetProcessHeap(), 0, buffer );
793 }
794
795 /*
796 still-to-be-tested 2K-onward functions:
797 AddIPAddress
798 CreateProxyArpEntry
799 DeleteIPAddress
800 DeleteProxyArpEntry
801 EnableRouter
802 FlushIpNetTable
803 GetAdapterIndex
804 NotifyAddrChange
805 NotifyRouteChange
806 SendARP
807 UnenableRouter
808 */
809 static void testWin2KFunctions(void)
810 {
811     testGetPerAdapterInfo();
812 }
813
814 START_TEST(iphlpapi)
815 {
816
817   loadIPHlpApi();
818   if (hLibrary) {
819     testWin98OnlyFunctions();
820     testWinNT4Functions();
821     testWin98Functions();
822     testWin2KFunctions();
823     freeIPHlpApi();
824   }
825 }