usp10: Fix glyph comparsion between tests.
[wine] / dlls / iphlpapi / iphlpapi_main.c
1 /*
2  * iphlpapi dll implementation
3  *
4  * Copyright (C) 2003,2006 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 #include "config.h"
22
23 #include <stdarg.h>
24 #include <stdlib.h>
25 #include <sys/types.h>
26 #ifdef HAVE_NETINET_IN_H
27 # include <netinet/in.h>
28 #endif
29 #ifdef HAVE_ARPA_INET_H
30 # include <arpa/inet.h>
31 #endif
32 #ifdef HAVE_ARPA_NAMESER_H
33 # include <arpa/nameser.h>
34 #endif
35 #ifdef HAVE_RESOLV_H
36 # include <resolv.h>
37 #endif
38
39 #include "windef.h"
40 #include "winbase.h"
41 #include "winreg.h"
42 #include "iphlpapi.h"
43 #include "ifenum.h"
44 #include "ipstats.h"
45 #include "wine/debug.h"
46
47 WINE_DEFAULT_DEBUG_CHANNEL(iphlpapi);
48
49 #ifndef INADDR_NONE
50 #define INADDR_NONE ~0UL
51 #endif
52
53 BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
54 {
55   switch (fdwReason) {
56     case DLL_PROCESS_ATTACH:
57       DisableThreadLibraryCalls( hinstDLL );
58       break;
59
60     case DLL_PROCESS_DETACH:
61       break;
62   }
63   return TRUE;
64 }
65
66 /******************************************************************
67  *    AddIPAddress (IPHLPAPI.@)
68  *
69  * Add an IP address to an adapter.
70  *
71  * PARAMS
72  *  Address     [In]  IP address to add to the adapter
73  *  IpMask      [In]  subnet mask for the IP address
74  *  IfIndex     [In]  adapter index to add the address
75  *  NTEContext  [Out] Net Table Entry (NTE) context for the IP address
76  *  NTEInstance [Out] NTE instance for the IP address
77  *
78  * RETURNS
79  *  Success: NO_ERROR
80  *  Failure: error code from winerror.h
81  *
82  * FIXME
83  *  Stub. Currently returns ERROR_NOT_SUPPORTED.
84  */
85 DWORD WINAPI AddIPAddress(IPAddr Address, IPMask IpMask, DWORD IfIndex, PULONG NTEContext, PULONG NTEInstance)
86 {
87   FIXME(":stub\n");
88   return ERROR_NOT_SUPPORTED;
89 }
90
91
92 /******************************************************************
93  *    AllocateAndGetIfTableFromStack (IPHLPAPI.@)
94  *
95  * Get table of local interfaces.
96  * Like GetIfTable(), but allocate the returned table from heap.
97  *
98  * PARAMS
99  *  ppIfTable [Out] pointer into which the MIB_IFTABLE is
100  *                  allocated and returned.
101  *  bOrder    [In]  whether to sort the table
102  *  heap      [In]  heap from which the table is allocated
103  *  flags     [In]  flags to HeapAlloc
104  *
105  * RETURNS
106  *  ERROR_INVALID_PARAMETER if ppIfTable is NULL, whatever
107  *  GetIfTable() returns otherwise.
108  */
109 DWORD WINAPI AllocateAndGetIfTableFromStack(PMIB_IFTABLE *ppIfTable,
110  BOOL bOrder, HANDLE heap, DWORD flags)
111 {
112   DWORD ret;
113
114   TRACE("ppIfTable %p, bOrder %d, heap %p, flags 0x%08lx\n", ppIfTable,
115         bOrder, heap, flags);
116   if (!ppIfTable)
117     ret = ERROR_INVALID_PARAMETER;
118   else {
119     DWORD dwSize = 0;
120
121     ret = GetIfTable(*ppIfTable, &dwSize, bOrder);
122     if (ret == ERROR_INSUFFICIENT_BUFFER) {
123       *ppIfTable = HeapAlloc(heap, flags, dwSize);
124       ret = GetIfTable(*ppIfTable, &dwSize, bOrder);
125     }
126   }
127   TRACE("returning %ld\n", ret);
128   return ret;
129 }
130
131
132 static int IpAddrTableSorter(const void *a, const void *b)
133 {
134   int ret;
135
136   if (a && b)
137     ret = ((const MIB_IPADDRROW*)a)->dwAddr - ((const MIB_IPADDRROW*)b)->dwAddr;
138   else
139     ret = 0;
140   return ret;
141 }
142
143
144 /******************************************************************
145  *    AllocateAndGetIpAddrTableFromStack (IPHLPAPI.@)
146  *
147  * Get interface-to-IP address mapping table. 
148  * Like GetIpAddrTable(), but allocate the returned table from heap.
149  *
150  * PARAMS
151  *  ppIpAddrTable [Out] pointer into which the MIB_IPADDRTABLE is
152  *                      allocated and returned.
153  *  bOrder        [In]  whether to sort the table
154  *  heap          [In]  heap from which the table is allocated
155  *  flags         [In]  flags to HeapAlloc
156  *
157  * RETURNS
158  *  ERROR_INVALID_PARAMETER if ppIpAddrTable is NULL, other error codes on
159  *  failure, NO_ERROR on success.
160  */
161 DWORD WINAPI AllocateAndGetIpAddrTableFromStack(PMIB_IPADDRTABLE *ppIpAddrTable,
162  BOOL bOrder, HANDLE heap, DWORD flags)
163 {
164   DWORD ret;
165
166   TRACE("ppIpAddrTable %p, bOrder %d, heap %p, flags 0x%08lx\n",
167    ppIpAddrTable, bOrder, heap, flags);
168   ret = getIPAddrTable(ppIpAddrTable, heap, flags);
169   if (!ret && bOrder)
170     qsort((*ppIpAddrTable)->table, (*ppIpAddrTable)->dwNumEntries,
171      sizeof(MIB_IPADDRROW), IpAddrTableSorter);
172   TRACE("returning %ld\n", ret);
173   return ret;
174 }
175
176
177 static int IpForwardTableSorter(const void *a, const void *b)
178 {
179   int ret;
180
181   if (a && b) {
182    const MIB_IPFORWARDROW* rowA = (const MIB_IPFORWARDROW*)a;
183    const MIB_IPFORWARDROW* rowB = (const MIB_IPFORWARDROW*)b;
184
185     ret = rowA->dwForwardDest - rowB->dwForwardDest;
186     if (ret == 0) {
187       ret = rowA->dwForwardProto - rowB->dwForwardProto;
188       if (ret == 0) {
189         ret = rowA->dwForwardPolicy - rowB->dwForwardPolicy;
190         if (ret == 0)
191           ret = rowA->dwForwardNextHop - rowB->dwForwardNextHop;
192       }
193     }
194   }
195   else
196     ret = 0;
197   return ret;
198 }
199
200
201 /******************************************************************
202  *    AllocateAndGetIpForwardTableFromStack (IPHLPAPI.@)
203  *
204  * Get the route table.
205  * Like GetIpForwardTable(), but allocate the returned table from heap.
206  *
207  * PARAMS
208  *  ppIpForwardTable [Out] pointer into which the MIB_IPFORWARDTABLE is
209  *                         allocated and returned.
210  *  bOrder           [In]  whether to sort the table
211  *  heap             [In]  heap from which the table is allocated
212  *  flags            [In]  flags to HeapAlloc
213  *
214  * RETURNS
215  *  ERROR_INVALID_PARAMETER if ppIfTable is NULL, other error codes
216  *  on failure, NO_ERROR on success.
217  */
218 DWORD WINAPI AllocateAndGetIpForwardTableFromStack(PMIB_IPFORWARDTABLE *
219  ppIpForwardTable, BOOL bOrder, HANDLE heap, DWORD flags)
220 {
221   DWORD ret;
222
223   TRACE("ppIpForwardTable %p, bOrder %d, heap %p, flags 0x%08lx\n",
224    ppIpForwardTable, bOrder, heap, flags);
225   ret = getRouteTable(ppIpForwardTable, heap, flags);
226   if (!ret && bOrder)
227     qsort((*ppIpForwardTable)->table, (*ppIpForwardTable)->dwNumEntries,
228      sizeof(MIB_IPFORWARDROW), IpForwardTableSorter);
229   TRACE("returning %ld\n", ret);
230   return ret;
231 }
232
233
234 static int IpNetTableSorter(const void *a, const void *b)
235 {
236   int ret;
237
238   if (a && b)
239     ret = ((const MIB_IPNETROW*)a)->dwAddr - ((const MIB_IPNETROW*)b)->dwAddr;
240   else
241     ret = 0;
242   return ret;
243 }
244
245
246 /******************************************************************
247  *    AllocateAndGetIpNetTableFromStack (IPHLPAPI.@)
248  *
249  * Get the IP-to-physical address mapping table.
250  * Like GetIpNetTable(), but allocate the returned table from heap.
251  *
252  * PARAMS
253  *  ppIpNetTable [Out] pointer into which the MIB_IPNETTABLE is
254  *                     allocated and returned.
255  *  bOrder       [In]  whether to sort the table
256  *  heap         [In]  heap from which the table is allocated
257  *  flags        [In]  flags to HeapAlloc
258  *
259  * RETURNS
260  *  ERROR_INVALID_PARAMETER if ppIpNetTable is NULL, other error codes
261  *  on failure, NO_ERROR on success.
262  */
263 DWORD WINAPI AllocateAndGetIpNetTableFromStack(PMIB_IPNETTABLE *ppIpNetTable,
264  BOOL bOrder, HANDLE heap, DWORD flags)
265 {
266   DWORD ret;
267
268   TRACE("ppIpNetTable %p, bOrder %d, heap %p, flags 0x%08lx\n",
269    ppIpNetTable, bOrder, heap, flags);
270   ret = getArpTable(ppIpNetTable, heap, flags);
271   if (!ret && bOrder)
272     qsort((*ppIpNetTable)->table, (*ppIpNetTable)->dwNumEntries,
273      sizeof(MIB_IPADDRROW), IpNetTableSorter);
274   TRACE("returning %ld\n", ret);
275   return ret;
276 }
277
278
279 static int TcpTableSorter(const void *a, const void *b)
280 {
281   int ret;
282
283   if (a && b) {
284     const MIB_TCPROW* rowA = a;
285     const MIB_TCPROW* rowB = b;
286
287     ret = rowA->dwLocalAddr - rowB->dwLocalAddr;
288     if (ret == 0) {
289       ret = rowA->dwLocalPort - rowB->dwLocalPort;
290       if (ret == 0) {
291         ret = rowA->dwRemoteAddr - rowB->dwRemoteAddr;
292         if (ret == 0)
293           ret = rowA->dwRemotePort - rowB->dwRemotePort;
294       }
295     }
296   }
297   else
298     ret = 0;
299   return ret;
300 }
301
302
303 /******************************************************************
304  *    AllocateAndGetTcpTableFromStack (IPHLPAPI.@)
305  *
306  * Get the TCP connection table.
307  * Like GetTcpTable(), but allocate the returned table from heap.
308  *
309  * PARAMS
310  *  ppTcpTable [Out] pointer into which the MIB_TCPTABLE is
311  *                   allocated and returned.
312  *  bOrder     [In]  whether to sort the table
313  *  heap       [In]  heap from which the table is allocated
314  *  flags      [In]  flags to HeapAlloc
315  *
316  * RETURNS
317  *  ERROR_INVALID_PARAMETER if ppTcpTable is NULL, whatever GetTcpTable()
318  *  returns otherwise.
319  */
320 DWORD WINAPI AllocateAndGetTcpTableFromStack(PMIB_TCPTABLE *ppTcpTable,
321  BOOL bOrder, HANDLE heap, DWORD flags)
322 {
323   DWORD ret;
324
325   TRACE("ppTcpTable %p, bOrder %d, heap %p, flags 0x%08lx\n",
326    ppTcpTable, bOrder, heap, flags);
327   ret = getTcpTable(ppTcpTable, heap, flags);
328   if (!ret && bOrder)
329     qsort((*ppTcpTable)->table, (*ppTcpTable)->dwNumEntries,
330      sizeof(MIB_TCPROW), TcpTableSorter);
331   TRACE("returning %ld\n", ret);
332   return ret;
333 }
334
335
336 static int UdpTableSorter(const void *a, const void *b)
337 {
338   int ret;
339
340   if (a && b) {
341     const MIB_UDPROW* rowA = (const MIB_UDPROW*)a;
342     const MIB_UDPROW* rowB = (const MIB_UDPROW*)b;
343
344     ret = rowA->dwLocalAddr - rowB->dwLocalAddr;
345     if (ret == 0)
346       ret = rowA->dwLocalPort - rowB->dwLocalPort;
347   }
348   else
349     ret = 0;
350   return ret;
351 }
352
353
354 /******************************************************************
355  *    AllocateAndGetUdpTableFromStack (IPHLPAPI.@)
356  *
357  * Get the UDP listener table.
358  * Like GetUdpTable(), but allocate the returned table from heap.
359  *
360  * PARAMS
361  *  ppUdpTable [Out] pointer into which the MIB_UDPTABLE is
362  *                   allocated and returned.
363  *  bOrder     [In]  whether to sort the table
364  *  heap       [In]  heap from which the table is allocated
365  *  flags      [In]  flags to HeapAlloc
366  *
367  * RETURNS
368  *  ERROR_INVALID_PARAMETER if ppUdpTable is NULL, whatever GetUdpTable()
369  *  returns otherwise.
370  */
371 DWORD WINAPI AllocateAndGetUdpTableFromStack(PMIB_UDPTABLE *ppUdpTable,
372  BOOL bOrder, HANDLE heap, DWORD flags)
373 {
374   DWORD ret;
375
376   TRACE("ppUdpTable %p, bOrder %d, heap %p, flags 0x%08lx\n",
377    ppUdpTable, bOrder, heap, flags);
378   ret = getUdpTable(ppUdpTable, heap, flags);
379   if (!ret && bOrder)
380     qsort((*ppUdpTable)->table, (*ppUdpTable)->dwNumEntries,
381      sizeof(MIB_UDPROW), UdpTableSorter);
382   TRACE("returning %ld\n", ret);
383   return ret;
384 }
385
386
387 /******************************************************************
388  *    CreateIpForwardEntry (IPHLPAPI.@)
389  *
390  * Create a route in the local computer's IP table.
391  *
392  * PARAMS
393  *  pRoute [In] new route information
394  *
395  * RETURNS
396  *  Success: NO_ERROR
397  *  Failure: error code from winerror.h
398  *
399  * FIXME
400  *  Stub, always returns NO_ERROR.
401  */
402 DWORD WINAPI CreateIpForwardEntry(PMIB_IPFORWARDROW pRoute)
403 {
404   FIXME("(pRoute %p): stub\n", pRoute);
405   /* could use SIOCADDRT, not sure I want to */
406   return (DWORD) 0;
407 }
408
409
410 /******************************************************************
411  *    CreateIpNetEntry (IPHLPAPI.@)
412  *
413  * Create entry in the ARP table.
414  *
415  * PARAMS
416  *  pArpEntry [In] new ARP entry
417  *
418  * RETURNS
419  *  Success: NO_ERROR
420  *  Failure: error code from winerror.h
421  *
422  * FIXME
423  *  Stub, always returns NO_ERROR.
424  */
425 DWORD WINAPI CreateIpNetEntry(PMIB_IPNETROW pArpEntry)
426 {
427   FIXME("(pArpEntry %p)\n", pArpEntry);
428   /* could use SIOCSARP on systems that support it, not sure I want to */
429   return (DWORD) 0;
430 }
431
432
433 /******************************************************************
434  *    CreateProxyArpEntry (IPHLPAPI.@)
435  *
436  * Create a Proxy ARP (PARP) entry for an IP address.
437  *
438  * PARAMS
439  *  dwAddress [In] IP address for which this computer acts as a proxy. 
440  *  dwMask    [In] subnet mask for dwAddress
441  *  dwIfIndex [In] interface index
442  *
443  * RETURNS
444  *  Success: NO_ERROR
445  *  Failure: error code from winerror.h
446  *
447  * FIXME
448  *  Stub, returns ERROR_NOT_SUPPORTED.
449  */
450 DWORD WINAPI CreateProxyArpEntry(DWORD dwAddress, DWORD dwMask, DWORD dwIfIndex)
451 {
452   FIXME("(dwAddress 0x%08lx, dwMask 0x%08lx, dwIfIndex 0x%08lx): stub\n",
453    dwAddress, dwMask, dwIfIndex);
454   return ERROR_NOT_SUPPORTED;
455 }
456
457
458 /******************************************************************
459  *    DeleteIPAddress (IPHLPAPI.@)
460  *
461  * Delete an IP address added with AddIPAddress().
462  *
463  * PARAMS
464  *  NTEContext [In] NTE context from AddIPAddress();
465  *
466  * RETURNS
467  *  Success: NO_ERROR
468  *  Failure: error code from winerror.h
469  *
470  * FIXME
471  *  Stub, returns ERROR_NOT_SUPPORTED.
472  */
473 DWORD WINAPI DeleteIPAddress(ULONG NTEContext)
474 {
475   FIXME("(NTEContext %ld): stub\n", NTEContext);
476   return ERROR_NOT_SUPPORTED;
477 }
478
479
480 /******************************************************************
481  *    DeleteIpForwardEntry (IPHLPAPI.@)
482  *
483  * Delete a route.
484  *
485  * PARAMS
486  *  pRoute [In] route to delete
487  *
488  * RETURNS
489  *  Success: NO_ERROR
490  *  Failure: error code from winerror.h
491  *
492  * FIXME
493  *  Stub, returns NO_ERROR.
494  */
495 DWORD WINAPI DeleteIpForwardEntry(PMIB_IPFORWARDROW pRoute)
496 {
497   FIXME("(pRoute %p): stub\n", pRoute);
498   /* could use SIOCDELRT, not sure I want to */
499   return (DWORD) 0;
500 }
501
502
503 /******************************************************************
504  *    DeleteIpNetEntry (IPHLPAPI.@)
505  *
506  * Delete an ARP entry.
507  *
508  * PARAMS
509  *  pArpEntry [In] ARP entry to delete
510  *
511  * RETURNS
512  *  Success: NO_ERROR
513  *  Failure: error code from winerror.h
514  *
515  * FIXME
516  *  Stub, returns NO_ERROR.
517  */
518 DWORD WINAPI DeleteIpNetEntry(PMIB_IPNETROW pArpEntry)
519 {
520   FIXME("(pArpEntry %p): stub\n", pArpEntry);
521   /* could use SIOCDARP on systems that support it, not sure I want to */
522   return (DWORD) 0;
523 }
524
525
526 /******************************************************************
527  *    DeleteProxyArpEntry (IPHLPAPI.@)
528  *
529  * Delete a Proxy ARP entry.
530  *
531  * PARAMS
532  *  dwAddress [In] IP address for which this computer acts as a proxy. 
533  *  dwMask    [In] subnet mask for dwAddress
534  *  dwIfIndex [In] interface index
535  *
536  * RETURNS
537  *  Success: NO_ERROR
538  *  Failure: error code from winerror.h
539  *
540  * FIXME
541  *  Stub, returns ERROR_NOT_SUPPORTED.
542  */
543 DWORD WINAPI DeleteProxyArpEntry(DWORD dwAddress, DWORD dwMask, DWORD dwIfIndex)
544 {
545   FIXME("(dwAddress 0x%08lx, dwMask 0x%08lx, dwIfIndex 0x%08lx): stub\n",
546    dwAddress, dwMask, dwIfIndex);
547   return ERROR_NOT_SUPPORTED;
548 }
549
550
551 /******************************************************************
552  *    EnableRouter (IPHLPAPI.@)
553  *
554  * Turn on ip forwarding.
555  *
556  * PARAMS
557  *  pHandle     [In/Out]
558  *  pOverlapped [In/Out] hEvent member should contain a valid handle.
559  *
560  * RETURNS
561  *  Success: ERROR_IO_PENDING
562  *  Failure: error code from winerror.h
563  *
564  * FIXME
565  *  Stub, returns ERROR_NOT_SUPPORTED.
566  */
567 DWORD WINAPI EnableRouter(HANDLE * pHandle, OVERLAPPED * pOverlapped)
568 {
569   FIXME("(pHandle %p, pOverlapped %p): stub\n", pHandle, pOverlapped);
570   /* could echo "1" > /proc/net/sys/net/ipv4/ip_forward, not sure I want to
571      could map EACCESS to ERROR_ACCESS_DENIED, I suppose
572    */
573   return ERROR_NOT_SUPPORTED;
574 }
575
576
577 /******************************************************************
578  *    FlushIpNetTable (IPHLPAPI.@)
579  *
580  * Delete all ARP entries of an interface
581  *
582  * PARAMS
583  *  dwIfIndex [In] interface index
584  *
585  * RETURNS
586  *  Success: NO_ERROR
587  *  Failure: error code from winerror.h
588  *
589  * FIXME
590  *  Stub, returns ERROR_NOT_SUPPORTED.
591  */
592 DWORD WINAPI FlushIpNetTable(DWORD dwIfIndex)
593 {
594   FIXME("(dwIfIndex 0x%08lx): stub\n", dwIfIndex);
595   /* this flushes the arp cache of the given index */
596   return ERROR_NOT_SUPPORTED;
597 }
598
599
600 /******************************************************************
601  *    GetAdapterIndex (IPHLPAPI.@)
602  *
603  * Get interface index from its name.
604  *
605  * PARAMS
606  *  AdapterName [In]  unicode string with the adapter name
607  *  IfIndex     [Out] returns found interface index
608  *
609  * RETURNS
610  *  Success: NO_ERROR
611  *  Failure: error code from winerror.h
612  *
613  * FIXME
614  *  Stub, returns ERROR_NOT_SUPPORTED.
615  */
616 DWORD WINAPI GetAdapterIndex(LPWSTR AdapterName, PULONG IfIndex)
617 {
618   FIXME("(AdapterName %p, IfIndex %p): stub\n", AdapterName, IfIndex);
619   /* FIXME: implement using getInterfaceIndexByName */
620   return ERROR_NOT_SUPPORTED;
621 }
622
623
624 /******************************************************************
625  *    GetAdaptersInfo (IPHLPAPI.@)
626  *
627  * Get information about adapters.
628  *
629  * PARAMS
630  *  pAdapterInfo [Out] buffer for adapter infos
631  *  pOutBufLen   [In]  length of output buffer
632  *
633  * RETURNS
634  *  Success: NO_ERROR
635  *  Failure: error code from winerror.h
636  */
637 DWORD WINAPI GetAdaptersInfo(PIP_ADAPTER_INFO pAdapterInfo, PULONG pOutBufLen)
638 {
639   DWORD ret;
640
641   TRACE("pAdapterInfo %p, pOutBufLen %p\n", pAdapterInfo, pOutBufLen);
642   if (!pOutBufLen)
643     ret = ERROR_INVALID_PARAMETER;
644   else {
645     DWORD numNonLoopbackInterfaces = getNumNonLoopbackInterfaces();
646
647     if (numNonLoopbackInterfaces > 0) {
648       DWORD numIPAddresses = getNumIPAddresses();
649       ULONG size;
650
651       /* This may slightly overestimate the amount of space needed, because
652        * the IP addresses include the loopback address, but it's easier
653        * to make sure there's more than enough space than to make sure there's
654        * precisely enough space.
655        */
656       size = sizeof(IP_ADAPTER_INFO) * numNonLoopbackInterfaces;
657       size += numIPAddresses  * sizeof(IP_ADDR_STRING); 
658       if (!pAdapterInfo || *pOutBufLen < size) {
659         *pOutBufLen = size;
660         ret = ERROR_BUFFER_OVERFLOW;
661       }
662       else {
663         InterfaceIndexTable *table = NULL;
664         PMIB_IPADDRTABLE ipAddrTable = NULL;
665
666         ret = getIPAddrTable(&ipAddrTable, GetProcessHeap(), 0);
667         if (!ret)
668           table = getNonLoopbackInterfaceIndexTable();
669         if (table) {
670           size = sizeof(IP_ADAPTER_INFO) * table->numIndexes;
671           size += ipAddrTable->dwNumEntries * sizeof(IP_ADDR_STRING); 
672           if (*pOutBufLen < size) {
673             *pOutBufLen = size;
674             ret = ERROR_INSUFFICIENT_BUFFER;
675           }
676           else {
677             DWORD ndx;
678             HKEY hKey;
679             BOOL winsEnabled = FALSE;
680             IP_ADDRESS_STRING primaryWINS, secondaryWINS;
681             PIP_ADDR_STRING nextIPAddr = (PIP_ADDR_STRING)((LPBYTE)pAdapterInfo
682              + numNonLoopbackInterfaces * sizeof(IP_ADAPTER_INFO));
683
684             memset(pAdapterInfo, 0, size);
685             /* @@ Wine registry key: HKCU\Software\Wine\Network */
686             if (RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Network",
687              &hKey) == ERROR_SUCCESS) {
688               DWORD size = sizeof(primaryWINS.String);
689               unsigned long addr;
690
691               RegQueryValueExA(hKey, "WinsServer", NULL, NULL,
692                (LPBYTE)primaryWINS.String, &size);
693               addr = inet_addr(primaryWINS.String);
694               if (addr != INADDR_NONE && addr != INADDR_ANY)
695                 winsEnabled = TRUE;
696               size = sizeof(secondaryWINS.String);
697               RegQueryValueExA(hKey, "BackupWinsServer", NULL, NULL,
698                (LPBYTE)secondaryWINS.String, &size);
699               addr = inet_addr(secondaryWINS.String);
700               if (addr != INADDR_NONE && addr != INADDR_ANY)
701                 winsEnabled = TRUE;
702               RegCloseKey(hKey);
703             }
704             for (ndx = 0; ndx < table->numIndexes; ndx++) {
705               PIP_ADAPTER_INFO ptr = &pAdapterInfo[ndx];
706               DWORD addrLen = sizeof(ptr->Address), type, i;
707               PIP_ADDR_STRING currentIPAddr = &ptr->IpAddressList;
708               BOOL firstIPAddr = TRUE;
709
710               /* on Win98 this is left empty, but whatever */
711               getInterfaceNameByIndex(table->indexes[ndx], ptr->AdapterName);
712               getInterfacePhysicalByIndex(table->indexes[ndx], &addrLen,
713                ptr->Address, &type);
714               /* MS defines address length and type as UINT in some places and
715                  DWORD in others, **sigh**.  Don't want to assume that PUINT and
716                  PDWORD are equiv (64-bit?) */
717               ptr->AddressLength = addrLen;
718               ptr->Type = type;
719               ptr->Index = table->indexes[ndx];
720               for (i = 0; i < ipAddrTable->dwNumEntries; i++) {
721                 if (ipAddrTable->table[i].dwIndex == ptr->Index) {
722                   if (firstIPAddr) {
723                     toIPAddressString(ipAddrTable->table[i].dwAddr,
724                      ptr->IpAddressList.IpAddress.String);
725                     toIPAddressString(ipAddrTable->table[i].dwBCastAddr,
726                      ptr->IpAddressList.IpMask.String);
727                     firstIPAddr = FALSE;
728                   }
729                   else {
730                     currentIPAddr->Next = nextIPAddr;
731                     currentIPAddr = nextIPAddr;
732                     toIPAddressString(ipAddrTable->table[i].dwAddr,
733                      currentIPAddr->IpAddress.String);
734                     toIPAddressString(ipAddrTable->table[i].dwBCastAddr,
735                      currentIPAddr->IpMask.String);
736                     nextIPAddr++;
737                   }
738                 }
739               }
740               if (winsEnabled) {
741                 ptr->HaveWins = TRUE;
742                 memcpy(ptr->PrimaryWinsServer.IpAddress.String,
743                  primaryWINS.String, sizeof(primaryWINS.String));
744                 memcpy(ptr->SecondaryWinsServer.IpAddress.String,
745                  secondaryWINS.String, sizeof(secondaryWINS.String));
746               }
747               if (ndx < table->numIndexes - 1)
748                 ptr->Next = &pAdapterInfo[ndx + 1];
749               else
750                 ptr->Next = NULL;
751             }
752             ret = NO_ERROR;
753           }
754           HeapFree(GetProcessHeap(), 0, table);
755         }
756         else
757           ret = ERROR_OUTOFMEMORY;
758         HeapFree(GetProcessHeap(), 0, ipAddrTable);
759       }
760     }
761     else
762       ret = ERROR_NO_DATA;
763   }
764   TRACE("returning %ld\n", ret);
765   return ret;
766 }
767
768
769 /******************************************************************
770  *    GetBestInterface (IPHLPAPI.@)
771  *
772  * Get the interface, with the best route for the given IP address.
773  *
774  * PARAMS
775  *  dwDestAddr     [In]  IP address to search the interface for
776  *  pdwBestIfIndex [Out] found best interface
777  *
778  * RETURNS
779  *  Success: NO_ERROR
780  *  Failure: error code from winerror.h
781  */
782 DWORD WINAPI GetBestInterface(IPAddr dwDestAddr, PDWORD pdwBestIfIndex)
783 {
784   DWORD ret;
785
786   TRACE("dwDestAddr 0x%08lx, pdwBestIfIndex %p\n", dwDestAddr, pdwBestIfIndex);
787   if (!pdwBestIfIndex)
788     ret = ERROR_INVALID_PARAMETER;
789   else {
790     MIB_IPFORWARDROW ipRow;
791
792     ret = GetBestRoute(dwDestAddr, 0, &ipRow);
793     if (ret == ERROR_SUCCESS)
794       *pdwBestIfIndex = ipRow.dwForwardIfIndex;
795   }
796   TRACE("returning %ld\n", ret);
797   return ret;
798 }
799
800
801 /******************************************************************
802  *    GetBestRoute (IPHLPAPI.@)
803  *
804  * Get the best route for the given IP address.
805  *
806  * PARAMS
807  *  dwDestAddr   [In]  IP address to search the best route for
808  *  dwSourceAddr [In]  optional source IP address
809  *  pBestRoute   [Out] found best route
810  *
811  * RETURNS
812  *  Success: NO_ERROR
813  *  Failure: error code from winerror.h
814  */
815 DWORD WINAPI GetBestRoute(DWORD dwDestAddr, DWORD dwSourceAddr, PMIB_IPFORWARDROW pBestRoute)
816 {
817   PMIB_IPFORWARDTABLE table;
818   DWORD ret;
819
820   TRACE("dwDestAddr 0x%08lx, dwSourceAddr 0x%08lx, pBestRoute %p\n", dwDestAddr,
821    dwSourceAddr, pBestRoute);
822   if (!pBestRoute)
823     return ERROR_INVALID_PARAMETER;
824
825   AllocateAndGetIpForwardTableFromStack(&table, FALSE, GetProcessHeap(), 0);
826   if (table) {
827     DWORD ndx, matchedBits, matchedNdx = 0;
828
829     for (ndx = 0, matchedBits = 0; ndx < table->dwNumEntries; ndx++) {
830       if (table->table[ndx].dwForwardType != MIB_IPROUTE_TYPE_INVALID &&
831        (dwDestAddr & table->table[ndx].dwForwardMask) ==
832        (table->table[ndx].dwForwardDest & table->table[ndx].dwForwardMask)) {
833         DWORD numShifts, mask;
834
835         for (numShifts = 0, mask = table->table[ndx].dwForwardMask;
836          mask && !(mask & 1); mask >>= 1, numShifts++)
837           ;
838         if (numShifts > matchedBits) {
839           matchedBits = numShifts;
840           matchedNdx = ndx;
841         }
842       }
843     }
844     if (matchedNdx < table->dwNumEntries) {
845       memcpy(pBestRoute, &table->table[matchedNdx], sizeof(MIB_IPFORWARDROW));
846       ret = ERROR_SUCCESS;
847     }
848     else {
849       /* No route matches, which can happen if there's no default route. */
850       ret = ERROR_HOST_UNREACHABLE;
851     }
852     HeapFree(GetProcessHeap(), 0, table);
853   }
854   else
855     ret = ERROR_OUTOFMEMORY;
856   TRACE("returning %ld\n", ret);
857   return ret;
858 }
859
860
861 /******************************************************************
862  *    GetFriendlyIfIndex (IPHLPAPI.@)
863  *
864  * Get a "friendly" version of IfIndex, which is one that doesn't
865  * have the top byte set.  Doesn't validate whether IfIndex is a valid
866  * adapter index.
867  *
868  * PARAMS
869  *  IfIndex [In] interface index to get the friendly one for
870  *
871  * RETURNS
872  *  A friendly version of IfIndex.
873  */
874 DWORD WINAPI GetFriendlyIfIndex(DWORD IfIndex)
875 {
876   /* windows doesn't validate these, either, just makes sure the top byte is
877      cleared.  I assume my ifenum module never gives an index with the top
878      byte set. */
879   TRACE("returning %ld\n", IfIndex);
880   return IfIndex;
881 }
882
883
884 /******************************************************************
885  *    GetIcmpStatistics (IPHLPAPI.@)
886  *
887  * Get the ICMP statistics for the local computer.
888  *
889  * PARAMS
890  *  pStats [Out] buffer for ICMP statistics
891  *
892  * RETURNS
893  *  Success: NO_ERROR
894  *  Failure: error code from winerror.h
895  */
896 DWORD WINAPI GetIcmpStatistics(PMIB_ICMP pStats)
897 {
898   DWORD ret;
899
900   TRACE("pStats %p\n", pStats);
901   ret = getICMPStats(pStats);
902   TRACE("returning %ld\n", ret);
903   return ret;
904 }
905
906
907 /******************************************************************
908  *    GetIfEntry (IPHLPAPI.@)
909  *
910  * Get information about an interface.
911  *
912  * PARAMS
913  *  pIfRow [In/Out] In:  dwIndex of MIB_IFROW selects the interface.
914  *                  Out: interface information
915  *
916  * RETURNS
917  *  Success: NO_ERROR
918  *  Failure: error code from winerror.h
919  */
920 DWORD WINAPI GetIfEntry(PMIB_IFROW pIfRow)
921 {
922   DWORD ret;
923   char nameBuf[MAX_ADAPTER_NAME];
924   char *name;
925
926   TRACE("pIfRow %p\n", pIfRow);
927   if (!pIfRow)
928     return ERROR_INVALID_PARAMETER;
929
930   name = getInterfaceNameByIndex(pIfRow->dwIndex, nameBuf);
931   if (name) {
932     ret = getInterfaceEntryByName(name, pIfRow);
933     if (ret == NO_ERROR)
934       ret = getInterfaceStatsByName(name, pIfRow);
935   }
936   else
937     ret = ERROR_INVALID_DATA;
938   TRACE("returning %ld\n", ret);
939   return ret;
940 }
941
942
943 static int IfTableSorter(const void *a, const void *b)
944 {
945   int ret;
946
947   if (a && b)
948     ret = ((const MIB_IFROW*)a)->dwIndex - ((const MIB_IFROW*)b)->dwIndex;
949   else
950     ret = 0;
951   return ret;
952 }
953
954
955 /******************************************************************
956  *    GetIfTable (IPHLPAPI.@)
957  *
958  * Get a table of local interfaces.
959  *
960  * PARAMS
961  *  pIfTable [Out]    buffer for local interfaces table
962  *  pdwSize  [In/Out] length of output buffer
963  *  bOrder   [In]     whether to sort the table
964  *
965  * RETURNS
966  *  Success: NO_ERROR
967  *  Failure: error code from winerror.h
968  *
969  * NOTES
970  *  If pdwSize is less than required, the function will return
971  *  ERROR_INSUFFICIENT_BUFFER, and *pdwSize will be set to the required byte
972  *  size.
973  *  If bOrder is true, the returned table will be sorted by interface index.
974  */
975 DWORD WINAPI GetIfTable(PMIB_IFTABLE pIfTable, PULONG pdwSize, BOOL bOrder)
976 {
977   DWORD ret;
978
979   TRACE("pIfTable %p, pdwSize %p, bOrder %ld\n", pdwSize, pdwSize,
980    (DWORD)bOrder);
981   if (!pdwSize)
982     ret = ERROR_INVALID_PARAMETER;
983   else {
984     DWORD numInterfaces = getNumInterfaces();
985     ULONG size = sizeof(MIB_IFTABLE) + (numInterfaces - 1) * sizeof(MIB_IFROW);
986
987     if (!pIfTable || *pdwSize < size) {
988       *pdwSize = size;
989       ret = ERROR_INSUFFICIENT_BUFFER;
990     }
991     else {
992       InterfaceIndexTable *table = getInterfaceIndexTable();
993
994       if (table) {
995         size = sizeof(MIB_IFTABLE) + (table->numIndexes - 1) *
996          sizeof(MIB_IFROW);
997         if (*pdwSize < size) {
998           *pdwSize = size;
999           ret = ERROR_INSUFFICIENT_BUFFER;
1000         }
1001         else {
1002           DWORD ndx;
1003
1004           *pdwSize = size;
1005           pIfTable->dwNumEntries = 0;
1006           for (ndx = 0; ndx < table->numIndexes; ndx++) {
1007             pIfTable->table[ndx].dwIndex = table->indexes[ndx];
1008             GetIfEntry(&pIfTable->table[ndx]);
1009             pIfTable->dwNumEntries++;
1010           }
1011           if (bOrder)
1012             qsort(pIfTable->table, pIfTable->dwNumEntries, sizeof(MIB_IFROW),
1013              IfTableSorter);
1014           ret = NO_ERROR;
1015         }
1016         HeapFree(GetProcessHeap(), 0, table);
1017       }
1018       else
1019         ret = ERROR_OUTOFMEMORY;
1020     }
1021   }
1022   TRACE("returning %ld\n", ret);
1023   return ret;
1024 }
1025
1026
1027 /******************************************************************
1028  *    GetInterfaceInfo (IPHLPAPI.@)
1029  *
1030  * Get a list of network interface adapters.
1031  *
1032  * PARAMS
1033  *  pIfTable    [Out] buffer for interface adapters
1034  *  dwOutBufLen [Out] if buffer is too small, returns required size
1035  *
1036  * RETURNS
1037  *  Success: NO_ERROR
1038  *  Failure: error code from winerror.h
1039  *
1040  * BUGS
1041  *  MSDN states this should return non-loopback interfaces only.
1042  */
1043 DWORD WINAPI GetInterfaceInfo(PIP_INTERFACE_INFO pIfTable, PULONG dwOutBufLen)
1044 {
1045   DWORD ret;
1046
1047   TRACE("pIfTable %p, dwOutBufLen %p\n", pIfTable, dwOutBufLen);
1048   if (!dwOutBufLen)
1049     ret = ERROR_INVALID_PARAMETER;
1050   else {
1051     DWORD numInterfaces = getNumInterfaces();
1052     ULONG size = sizeof(IP_INTERFACE_INFO) + (numInterfaces - 1) *
1053      sizeof(IP_ADAPTER_INDEX_MAP);
1054
1055     if (!pIfTable || *dwOutBufLen < size) {
1056       *dwOutBufLen = size;
1057       ret = ERROR_INSUFFICIENT_BUFFER;
1058     }
1059     else {
1060       InterfaceIndexTable *table = getInterfaceIndexTable();
1061
1062       if (table) {
1063         size = sizeof(IP_INTERFACE_INFO) + (table->numIndexes - 1) *
1064          sizeof(IP_ADAPTER_INDEX_MAP);
1065         if (*dwOutBufLen < size) {
1066           *dwOutBufLen = size;
1067           ret = ERROR_INSUFFICIENT_BUFFER;
1068         }
1069         else {
1070           DWORD ndx;
1071           char nameBuf[MAX_ADAPTER_NAME];
1072
1073           *dwOutBufLen = size;
1074           pIfTable->NumAdapters = 0;
1075           for (ndx = 0; ndx < table->numIndexes; ndx++) {
1076             const char *walker, *name;
1077             WCHAR *assigner;
1078
1079             pIfTable->Adapter[ndx].Index = table->indexes[ndx];
1080             name = getInterfaceNameByIndex(table->indexes[ndx], nameBuf);
1081             for (walker = name, assigner = pIfTable->Adapter[ndx].Name;
1082              walker && *walker &&
1083              assigner - pIfTable->Adapter[ndx].Name < MAX_ADAPTER_NAME - 1;
1084              walker++, assigner++)
1085               *assigner = *walker;
1086             *assigner = 0;
1087             pIfTable->NumAdapters++;
1088           }
1089           ret = NO_ERROR;
1090         }
1091         HeapFree(GetProcessHeap(), 0, table);
1092       }
1093       else
1094         ret = ERROR_OUTOFMEMORY;
1095     }
1096   }
1097   TRACE("returning %ld\n", ret);
1098   return ret;
1099 }
1100
1101
1102 /******************************************************************
1103  *    GetIpAddrTable (IPHLPAPI.@)
1104  *
1105  * Get interface-to-IP address mapping table. 
1106  *
1107  * PARAMS
1108  *  pIpAddrTable [Out]    buffer for mapping table
1109  *  pdwSize      [In/Out] length of output buffer
1110  *  bOrder       [In]     whether to sort the table
1111  *
1112  * RETURNS
1113  *  Success: NO_ERROR
1114  *  Failure: error code from winerror.h
1115  *
1116  * NOTES
1117  *  If pdwSize is less than required, the function will return
1118  *  ERROR_INSUFFICIENT_BUFFER, and *pdwSize will be set to the required byte
1119  *  size.
1120  *  If bOrder is true, the returned table will be sorted by the next hop and
1121  *  an assortment of arbitrary parameters.
1122  */
1123 DWORD WINAPI GetIpAddrTable(PMIB_IPADDRTABLE pIpAddrTable, PULONG pdwSize, BOOL bOrder)
1124 {
1125   DWORD ret;
1126
1127   TRACE("pIpAddrTable %p, pdwSize %p, bOrder %ld\n", pIpAddrTable, pdwSize,
1128    (DWORD)bOrder);
1129   if (!pdwSize)
1130     ret = ERROR_INVALID_PARAMETER;
1131   else {
1132     PMIB_IPADDRTABLE table;
1133
1134     ret = getIPAddrTable(&table, GetProcessHeap(), 0);
1135     if (ret == NO_ERROR)
1136     {
1137       ULONG size = sizeof(MIB_IPADDRTABLE) + (table->dwNumEntries - 1) *
1138        sizeof(MIB_IPADDRROW);
1139
1140       if (!pIpAddrTable || *pdwSize < size) {
1141         *pdwSize = size;
1142         ret = ERROR_INSUFFICIENT_BUFFER;
1143       }
1144       else {
1145         *pdwSize = size;
1146         memcpy(pIpAddrTable, table, sizeof(MIB_IPADDRTABLE) +
1147          (table->dwNumEntries - 1) * sizeof(MIB_IPADDRROW));
1148         if (bOrder)
1149           qsort(pIpAddrTable->table, pIpAddrTable->dwNumEntries,
1150            sizeof(MIB_IPADDRROW), IpAddrTableSorter);
1151         ret = NO_ERROR;
1152       }
1153       HeapFree(GetProcessHeap(), 0, table);
1154     }
1155   }
1156   TRACE("returning %ld\n", ret);
1157   return ret;
1158 }
1159
1160
1161 /******************************************************************
1162  *    GetIpForwardTable (IPHLPAPI.@)
1163  *
1164  * Get the route table.
1165  *
1166  * PARAMS
1167  *  pIpForwardTable [Out]    buffer for route table
1168  *  pdwSize         [In/Out] length of output buffer
1169  *  bOrder          [In]     whether to sort the table
1170  *
1171  * RETURNS
1172  *  Success: NO_ERROR
1173  *  Failure: error code from winerror.h
1174  *
1175  * NOTES
1176  *  If pdwSize is less than required, the function will return
1177  *  ERROR_INSUFFICIENT_BUFFER, and *pdwSize will be set to the required byte
1178  *  size.
1179  *  If bOrder is true, the returned table will be sorted by the next hop and
1180  *  an assortment of arbitrary parameters.
1181  */
1182 DWORD WINAPI GetIpForwardTable(PMIB_IPFORWARDTABLE pIpForwardTable, PULONG pdwSize, BOOL bOrder)
1183 {
1184   DWORD ret;
1185
1186   TRACE("pIpForwardTable %p, pdwSize %p, bOrder %ld\n", pIpForwardTable,
1187    pdwSize, (DWORD)bOrder);
1188   if (!pdwSize)
1189     ret = ERROR_INVALID_PARAMETER;
1190   else {
1191     DWORD numRoutes = getNumRoutes();
1192     ULONG sizeNeeded = sizeof(MIB_IPFORWARDTABLE) + (numRoutes - 1) *
1193      sizeof(MIB_IPFORWARDROW);
1194
1195     if (!pIpForwardTable || *pdwSize < sizeNeeded) {
1196       *pdwSize = sizeNeeded;
1197       ret = ERROR_INSUFFICIENT_BUFFER;
1198     }
1199     else {
1200       PMIB_IPFORWARDTABLE table;
1201
1202       ret = getRouteTable(&table, GetProcessHeap(), 0);
1203       if (!ret) {
1204         sizeNeeded = sizeof(MIB_IPFORWARDTABLE) + (table->dwNumEntries - 1) *
1205          sizeof(MIB_IPFORWARDROW);
1206         if (*pdwSize < sizeNeeded) {
1207           *pdwSize = sizeNeeded;
1208           ret = ERROR_INSUFFICIENT_BUFFER;
1209         }
1210         else {
1211           *pdwSize = sizeNeeded;
1212           memcpy(pIpForwardTable, table, sizeNeeded);
1213           if (bOrder)
1214             qsort(pIpForwardTable->table, pIpForwardTable->dwNumEntries,
1215              sizeof(MIB_IPFORWARDROW), IpForwardTableSorter);
1216           ret = NO_ERROR;
1217         }
1218         HeapFree(GetProcessHeap(), 0, table);
1219       }
1220     }
1221   }
1222   TRACE("returning %ld\n", ret);
1223   return ret;
1224 }
1225
1226
1227 /******************************************************************
1228  *    GetIpNetTable (IPHLPAPI.@)
1229  *
1230  * Get the IP-to-physical address mapping table.
1231  *
1232  * PARAMS
1233  *  pIpNetTable [Out]    buffer for mapping table
1234  *  pdwSize     [In/Out] length of output buffer
1235  *  bOrder      [In]     whether to sort the table
1236  *
1237  * RETURNS
1238  *  Success: NO_ERROR
1239  *  Failure: error code from winerror.h
1240  *
1241  * NOTES
1242  *  If pdwSize is less than required, the function will return
1243  *  ERROR_INSUFFICIENT_BUFFER, and *pdwSize will be set to the required byte
1244  *  size.
1245  *  If bOrder is true, the returned table will be sorted by IP address.
1246  */
1247 DWORD WINAPI GetIpNetTable(PMIB_IPNETTABLE pIpNetTable, PULONG pdwSize, BOOL bOrder)
1248 {
1249   DWORD ret;
1250
1251   TRACE("pIpNetTable %p, pdwSize %p, bOrder %ld\n", pIpNetTable, pdwSize,
1252    (DWORD)bOrder);
1253   if (!pdwSize)
1254     ret = ERROR_INVALID_PARAMETER;
1255   else {
1256     DWORD numEntries = getNumArpEntries();
1257     ULONG size = sizeof(MIB_IPNETTABLE) + (numEntries - 1) *
1258      sizeof(MIB_IPNETROW);
1259
1260     if (!pIpNetTable || *pdwSize < size) {
1261       *pdwSize = size;
1262       ret = ERROR_INSUFFICIENT_BUFFER;
1263     }
1264     else {
1265       PMIB_IPNETTABLE table;
1266
1267       ret = getArpTable(&table, GetProcessHeap(), 0);
1268       if (!ret) {
1269         size = sizeof(MIB_IPNETTABLE) + (table->dwNumEntries - 1) *
1270          sizeof(MIB_IPNETROW);
1271         if (*pdwSize < size) {
1272           *pdwSize = size;
1273           ret = ERROR_INSUFFICIENT_BUFFER;
1274         }
1275         else {
1276           *pdwSize = size;
1277           memcpy(pIpNetTable, table, size);
1278           if (bOrder)
1279             qsort(pIpNetTable->table, pIpNetTable->dwNumEntries,
1280              sizeof(MIB_IPNETROW), IpNetTableSorter);
1281           ret = NO_ERROR;
1282         }
1283         HeapFree(GetProcessHeap(), 0, table);
1284       }
1285     }
1286   }
1287   TRACE("returning %ld\n", ret);
1288   return ret;
1289 }
1290
1291
1292 /******************************************************************
1293  *    GetIpStatistics (IPHLPAPI.@)
1294  *
1295  * Get the IP statistics for the local computer.
1296  *
1297  * PARAMS
1298  *  pStats [Out] buffer for IP statistics
1299  *
1300  * RETURNS
1301  *  Success: NO_ERROR
1302  *  Failure: error code from winerror.h
1303  */
1304 DWORD WINAPI GetIpStatistics(PMIB_IPSTATS pStats)
1305 {
1306   DWORD ret;
1307
1308   TRACE("pStats %p\n", pStats);
1309   ret = getIPStats(pStats);
1310   TRACE("returning %ld\n", ret);
1311   return ret;
1312 }
1313
1314
1315 /******************************************************************
1316  *    GetNetworkParams (IPHLPAPI.@)
1317  *
1318  * Get the network parameters for the local computer.
1319  *
1320  * PARAMS
1321  *  pFixedInfo [Out]    buffer for network parameters
1322  *  pOutBufLen [In/Out] length of output buffer
1323  *
1324  * RETURNS
1325  *  Success: NO_ERROR
1326  *  Failure: error code from winerror.h
1327  *
1328  * NOTES
1329  *  If pOutBufLen is less than required, the function will return
1330  *  ERROR_INSUFFICIENT_BUFFER, and pOutBufLen will be set to the required byte
1331  *  size.
1332  */
1333 DWORD WINAPI GetNetworkParams(PFIXED_INFO pFixedInfo, PULONG pOutBufLen)
1334 {
1335   DWORD ret, size;
1336   LONG regReturn;
1337   HKEY hKey;
1338
1339   TRACE("pFixedInfo %p, pOutBufLen %p\n", pFixedInfo, pOutBufLen);
1340   if (!pOutBufLen)
1341     return ERROR_INVALID_PARAMETER;
1342
1343   res_init();
1344   size = sizeof(FIXED_INFO) + (_res.nscount > 0 ? (_res.nscount  - 1) *
1345    sizeof(IP_ADDR_STRING) : 0);
1346   if (!pFixedInfo || *pOutBufLen < size) {
1347     *pOutBufLen = size;
1348     return ERROR_BUFFER_OVERFLOW;
1349   }
1350
1351   memset(pFixedInfo, 0, size);
1352   size = sizeof(pFixedInfo->HostName);
1353   GetComputerNameExA(ComputerNameDnsHostname, pFixedInfo->HostName, &size);
1354   size = sizeof(pFixedInfo->DomainName);
1355   GetComputerNameExA(ComputerNameDnsDomain, pFixedInfo->DomainName, &size);
1356   if (_res.nscount > 0) {
1357     PIP_ADDR_STRING ptr;
1358     int i;
1359
1360     for (i = 0, ptr = &pFixedInfo->DnsServerList; i < _res.nscount && ptr;
1361      i++, ptr = ptr->Next) {
1362       toIPAddressString(_res.nsaddr_list[i].sin_addr.s_addr,
1363        ptr->IpAddress.String);
1364       if (i == _res.nscount - 1)
1365         ptr->Next = NULL;
1366       else if (i == 0)
1367         ptr->Next = (PIP_ADDR_STRING)((LPBYTE)pFixedInfo + sizeof(FIXED_INFO));
1368       else
1369         ptr->Next = (PIP_ADDR_STRING)((PBYTE)ptr + sizeof(IP_ADDR_STRING));
1370     }
1371   }
1372   pFixedInfo->NodeType = HYBRID_NODETYPE;
1373   regReturn = RegOpenKeyExA(HKEY_LOCAL_MACHINE,
1374    "SYSTEM\\CurrentControlSet\\Services\\VxD\\MSTCP", 0, KEY_READ, &hKey);
1375   if (regReturn != ERROR_SUCCESS)
1376     regReturn = RegOpenKeyExA(HKEY_LOCAL_MACHINE,
1377      "SYSTEM\\CurrentControlSet\\Services\\NetBT\\Parameters", 0, KEY_READ,
1378      &hKey);
1379   if (regReturn == ERROR_SUCCESS)
1380   {
1381     DWORD size = sizeof(pFixedInfo->ScopeId);
1382
1383     RegQueryValueExA(hKey, "ScopeID", NULL, NULL, (LPBYTE)pFixedInfo->ScopeId, &size);
1384     RegCloseKey(hKey);
1385   }
1386
1387   /* FIXME: can check whether routing's enabled in /proc/sys/net/ipv4/ip_forward
1388      I suppose could also check for a listener on port 53 to set EnableDns */
1389   ret = NO_ERROR;
1390   TRACE("returning %ld\n", ret);
1391   return ret;
1392 }
1393
1394
1395 /******************************************************************
1396  *    GetNumberOfInterfaces (IPHLPAPI.@)
1397  *
1398  * Get the number of interfaces.
1399  *
1400  * PARAMS
1401  *  pdwNumIf [Out] number of interfaces
1402  *
1403  * RETURNS
1404  *  NO_ERROR on success, ERROR_INVALID_PARAMETER if pdwNumIf is NULL.
1405  */
1406 DWORD WINAPI GetNumberOfInterfaces(PDWORD pdwNumIf)
1407 {
1408   DWORD ret;
1409
1410   TRACE("pdwNumIf %p\n", pdwNumIf);
1411   if (!pdwNumIf)
1412     ret = ERROR_INVALID_PARAMETER;
1413   else {
1414     *pdwNumIf = getNumInterfaces();
1415     ret = NO_ERROR;
1416   }
1417   TRACE("returning %ld\n", ret);
1418   return ret;
1419 }
1420
1421
1422 /******************************************************************
1423  *    GetPerAdapterInfo (IPHLPAPI.@)
1424  *
1425  * Get information about an adapter corresponding to an interface.
1426  *
1427  * PARAMS
1428  *  IfIndex         [In]     interface info
1429  *  pPerAdapterInfo [Out]    buffer for per adapter info
1430  *  pOutBufLen      [In/Out] length of output buffer
1431  *
1432  * RETURNS
1433  *  Success: NO_ERROR
1434  *  Failure: error code from winerror.h
1435  *
1436  * FIXME
1437  *  Stub, returns ERROR_NOT_SUPPORTED.
1438  */
1439 DWORD WINAPI GetPerAdapterInfo(ULONG IfIndex, PIP_PER_ADAPTER_INFO pPerAdapterInfo, PULONG pOutBufLen)
1440 {
1441   TRACE("(IfIndex %ld, pPerAdapterInfo %p, pOutBufLen %p)\n", IfIndex,
1442    pPerAdapterInfo, pOutBufLen);
1443   return ERROR_NOT_SUPPORTED;
1444 }
1445
1446
1447 /******************************************************************
1448  *    GetRTTAndHopCount (IPHLPAPI.@)
1449  *
1450  * Get round-trip time (RTT) and hop count.
1451  *
1452  * PARAMS
1453  *
1454  *  DestIpAddress [In]  destination address to get the info for
1455  *  HopCount      [Out] retrieved hop count
1456  *  MaxHops       [In]  maximum hops to search for the destination
1457  *  RTT           [Out] RTT in milliseconds
1458  *
1459  * RETURNS
1460  *  Success: TRUE
1461  *  Failure: FALSE
1462  *
1463  * FIXME
1464  *  Stub, returns FALSE.
1465  */
1466 BOOL WINAPI GetRTTAndHopCount(IPAddr DestIpAddress, PULONG HopCount, ULONG MaxHops, PULONG RTT)
1467 {
1468   FIXME("(DestIpAddress 0x%08lx, HopCount %p, MaxHops %ld, RTT %p): stub\n",
1469    DestIpAddress, HopCount, MaxHops, RTT);
1470   return FALSE;
1471 }
1472
1473
1474 /******************************************************************
1475  *    GetTcpStatistics (IPHLPAPI.@)
1476  *
1477  * Get the TCP statistics for the local computer.
1478  *
1479  * PARAMS
1480  *  pStats [Out] buffer for TCP statistics
1481  *
1482  * RETURNS
1483  *  Success: NO_ERROR
1484  *  Failure: error code from winerror.h
1485  */
1486 DWORD WINAPI GetTcpStatistics(PMIB_TCPSTATS pStats)
1487 {
1488   DWORD ret;
1489
1490   TRACE("pStats %p\n", pStats);
1491   ret = getTCPStats(pStats);
1492   TRACE("returning %ld\n", ret);
1493   return ret;
1494 }
1495
1496
1497 /******************************************************************
1498  *    GetTcpTable (IPHLPAPI.@)
1499  *
1500  * Get the table of active TCP connections.
1501  *
1502  * PARAMS
1503  *  pTcpTable [Out]    buffer for TCP connections table
1504  *  pdwSize   [In/Out] length of output buffer
1505  *  bOrder    [In]     whether to order the table
1506  *
1507  * RETURNS
1508  *  Success: NO_ERROR
1509  *  Failure: error code from winerror.h
1510  *
1511  * NOTES
1512  *  If pdwSize is less than required, the function will return 
1513  *  ERROR_INSUFFICIENT_BUFFER, and *pdwSize will be set to 
1514  *  the required byte size.
1515  *  If bOrder is true, the returned table will be sorted, first by
1516  *  local address and port number, then by remote address and port
1517  *  number.
1518  */
1519 DWORD WINAPI GetTcpTable(PMIB_TCPTABLE pTcpTable, PDWORD pdwSize, BOOL bOrder)
1520 {
1521   DWORD ret;
1522
1523   TRACE("pTcpTable %p, pdwSize %p, bOrder %ld\n", pTcpTable, pdwSize,
1524    (DWORD)bOrder);
1525   if (!pdwSize)
1526     ret = ERROR_INVALID_PARAMETER;
1527   else {
1528     DWORD numEntries = getNumTcpEntries();
1529     DWORD size = sizeof(MIB_TCPTABLE) + (numEntries - 1) * sizeof(MIB_TCPROW);
1530
1531     if (!pTcpTable || *pdwSize < size) {
1532       *pdwSize = size;
1533       ret = ERROR_INSUFFICIENT_BUFFER;
1534     }
1535     else {
1536       PMIB_TCPTABLE table;
1537
1538       ret = getTcpTable(&table, GetProcessHeap(), 0);
1539       if (!ret) {
1540         size = sizeof(MIB_TCPTABLE) + (table->dwNumEntries - 1) *
1541          sizeof(MIB_TCPROW);
1542         if (*pdwSize < size) {
1543           *pdwSize = size;
1544           ret = ERROR_INSUFFICIENT_BUFFER;
1545         }
1546         else {
1547           *pdwSize = size;
1548           memcpy(pTcpTable, table, size);
1549           if (bOrder)
1550             qsort(pTcpTable->table, pTcpTable->dwNumEntries,
1551              sizeof(MIB_TCPROW), TcpTableSorter);
1552           ret = NO_ERROR;
1553         }
1554         HeapFree(GetProcessHeap(), 0, table);
1555       }
1556       else
1557         ret = ERROR_OUTOFMEMORY;
1558     }
1559   }
1560   TRACE("returning %ld\n", ret);
1561   return ret;
1562 }
1563
1564
1565 /******************************************************************
1566  *    GetUdpStatistics (IPHLPAPI.@)
1567  *
1568  * Get the UDP statistics for the local computer.
1569  *
1570  * PARAMS
1571  *  pStats [Out] buffer for UDP statistics
1572  *
1573  * RETURNS
1574  *  Success: NO_ERROR
1575  *  Failure: error code from winerror.h
1576  */
1577 DWORD WINAPI GetUdpStatistics(PMIB_UDPSTATS pStats)
1578 {
1579   DWORD ret;
1580
1581   TRACE("pStats %p\n", pStats);
1582   ret = getUDPStats(pStats);
1583   TRACE("returning %ld\n", ret);
1584   return ret;
1585 }
1586
1587
1588 /******************************************************************
1589  *    GetUdpTable (IPHLPAPI.@)
1590  *
1591  * Get a table of active UDP connections.
1592  *
1593  * PARAMS
1594  *  pUdpTable [Out]    buffer for UDP connections table
1595  *  pdwSize   [In/Out] length of output buffer
1596  *  bOrder    [In]     whether to order the table
1597  *
1598  * RETURNS
1599  *  Success: NO_ERROR
1600  *  Failure: error code from winerror.h
1601  *
1602  * NOTES
1603  *  If pdwSize is less than required, the function will return 
1604  *  ERROR_INSUFFICIENT_BUFFER, and *pdwSize will be set to the
1605  *  required byte size.
1606  *  If bOrder is true, the returned table will be sorted, first by
1607  *  local address, then by local port number.
1608  */
1609 DWORD WINAPI GetUdpTable(PMIB_UDPTABLE pUdpTable, PDWORD pdwSize, BOOL bOrder)
1610 {
1611   DWORD ret;
1612
1613   TRACE("pUdpTable %p, pdwSize %p, bOrder %ld\n", pUdpTable, pdwSize,
1614    (DWORD)bOrder);
1615   if (!pdwSize)
1616     ret = ERROR_INVALID_PARAMETER;
1617   else {
1618     DWORD numEntries = getNumUdpEntries();
1619     DWORD size = sizeof(MIB_UDPTABLE) + (numEntries - 1) * sizeof(MIB_UDPROW);
1620
1621     if (!pUdpTable || *pdwSize < size) {
1622       *pdwSize = size;
1623       ret = ERROR_INSUFFICIENT_BUFFER;
1624     }
1625     else {
1626       PMIB_UDPTABLE table;
1627
1628       ret = getUdpTable(&table, GetProcessHeap(), 0);
1629       if (!ret) {
1630         size = sizeof(MIB_UDPTABLE) + (table->dwNumEntries - 1) *
1631          sizeof(MIB_UDPROW);
1632         if (*pdwSize < size) {
1633           *pdwSize = size;
1634           ret = ERROR_INSUFFICIENT_BUFFER;
1635         }
1636         else {
1637           *pdwSize = size;
1638           memcpy(pUdpTable, table, size);
1639           if (bOrder)
1640             qsort(pUdpTable->table, pUdpTable->dwNumEntries,
1641              sizeof(MIB_UDPROW), UdpTableSorter);
1642           ret = NO_ERROR;
1643         }
1644         HeapFree(GetProcessHeap(), 0, table);
1645       }
1646       else
1647         ret = ERROR_OUTOFMEMORY;
1648     }
1649   }
1650   TRACE("returning %ld\n", ret);
1651   return ret;
1652 }
1653
1654
1655 /******************************************************************
1656  *    GetUniDirectionalAdapterInfo (IPHLPAPI.@)
1657  *
1658  * This is a Win98-only function to get information on "unidirectional"
1659  * adapters.  Since this is pretty nonsensical in other contexts, it
1660  * never returns anything.
1661  *
1662  * PARAMS
1663  *  pIPIfInfo   [Out] buffer for adapter infos
1664  *  dwOutBufLen [Out] length of the output buffer
1665  *
1666  * RETURNS
1667  *  Success: NO_ERROR
1668  *  Failure: error code from winerror.h
1669  *
1670  * FIXME
1671  *  Stub, returns ERROR_NOT_SUPPORTED.
1672  */
1673 DWORD WINAPI GetUniDirectionalAdapterInfo(PIP_UNIDIRECTIONAL_ADAPTER_ADDRESS pIPIfInfo, PULONG dwOutBufLen)
1674 {
1675   TRACE("pIPIfInfo %p, dwOutBufLen %p\n", pIPIfInfo, dwOutBufLen);
1676   /* a unidirectional adapter?? not bloody likely! */
1677   return ERROR_NOT_SUPPORTED;
1678 }
1679
1680
1681 /******************************************************************
1682  *    IpReleaseAddress (IPHLPAPI.@)
1683  *
1684  * Release an IP optained through DHCP,
1685  *
1686  * PARAMS
1687  *  AdapterInfo [In] adapter to release IP address
1688  *
1689  * RETURNS
1690  *  Success: NO_ERROR
1691  *  Failure: error code from winerror.h
1692  *
1693  * NOTES
1694  *  Since GetAdaptersInfo never returns adapters that have DHCP enabled,
1695  *  this function does nothing.
1696  *
1697  * FIXME
1698  *  Stub, returns ERROR_NOT_SUPPORTED.
1699  */
1700 DWORD WINAPI IpReleaseAddress(PIP_ADAPTER_INDEX_MAP AdapterInfo)
1701 {
1702   TRACE("AdapterInfo %p\n", AdapterInfo);
1703   /* not a stub, never going to support this (and I never mark an adapter as
1704      DHCP enabled, see GetAdaptersInfo, so this should never get called) */
1705   return ERROR_NOT_SUPPORTED;
1706 }
1707
1708
1709 /******************************************************************
1710  *    IpRenewAddress (IPHLPAPI.@)
1711  *
1712  * Renew an IP optained through DHCP.
1713  *
1714  * PARAMS
1715  *  AdapterInfo [In] adapter to renew IP address
1716  *
1717  * RETURNS
1718  *  Success: NO_ERROR
1719  *  Failure: error code from winerror.h
1720  *
1721  * NOTES
1722  *  Since GetAdaptersInfo never returns adapters that have DHCP enabled,
1723  *  this function does nothing.
1724  *
1725  * FIXME
1726  *  Stub, returns ERROR_NOT_SUPPORTED.
1727  */
1728 DWORD WINAPI IpRenewAddress(PIP_ADAPTER_INDEX_MAP AdapterInfo)
1729 {
1730   TRACE("AdapterInfo %p\n", AdapterInfo);
1731   /* not a stub, never going to support this (and I never mark an adapter as
1732      DHCP enabled, see GetAdaptersInfo, so this should never get called) */
1733   return ERROR_NOT_SUPPORTED;
1734 }
1735
1736
1737 /******************************************************************
1738  *    NotifyAddrChange (IPHLPAPI.@)
1739  *
1740  * Notify caller whenever the ip-interface map is changed.
1741  *
1742  * PARAMS
1743  *  Handle     [Out] handle useable in asynchronus notification
1744  *  overlapped [In]  overlapped structure that notifies the caller
1745  *
1746  * RETURNS
1747  *  Success: NO_ERROR
1748  *  Failure: error code from winerror.h
1749  *
1750  * FIXME
1751  *  Stub, returns ERROR_NOT_SUPPORTED.
1752  */
1753 DWORD WINAPI NotifyAddrChange(PHANDLE Handle, LPOVERLAPPED overlapped)
1754 {
1755   FIXME("(Handle %p, overlapped %p): stub\n", Handle, overlapped);
1756   return ERROR_NOT_SUPPORTED;
1757 }
1758
1759
1760 /******************************************************************
1761  *    NotifyRouteChange (IPHLPAPI.@)
1762  *
1763  * Notify caller whenever the ip routing table is changed.
1764  *
1765  * PARAMS
1766  *  Handle     [Out] handle useable in asynchronus notification
1767  *  overlapped [In]  overlapped structure that notifies the caller
1768  *
1769  * RETURNS
1770  *  Success: NO_ERROR
1771  *  Failure: error code from winerror.h
1772  *
1773  * FIXME
1774  *  Stub, returns ERROR_NOT_SUPPORTED.
1775  */
1776 DWORD WINAPI NotifyRouteChange(PHANDLE Handle, LPOVERLAPPED overlapped)
1777 {
1778   FIXME("(Handle %p, overlapped %p): stub\n", Handle, overlapped);
1779   return ERROR_NOT_SUPPORTED;
1780 }
1781
1782
1783 /******************************************************************
1784  *    SendARP (IPHLPAPI.@)
1785  *
1786  * Send an ARP request.
1787  *
1788  * PARAMS
1789  *  DestIP     [In]     attempt to obtain this IP
1790  *  SrcIP      [In]     optional sender IP address
1791  *  pMacAddr   [Out]    buffer for the mac address
1792  *  PhyAddrLen [In/Out] length of the output buffer
1793  *
1794  * RETURNS
1795  *  Success: NO_ERROR
1796  *  Failure: error code from winerror.h
1797  *
1798  * FIXME
1799  *  Stub, returns ERROR_NOT_SUPPORTED.
1800  */
1801 DWORD WINAPI SendARP(IPAddr DestIP, IPAddr SrcIP, PULONG pMacAddr, PULONG PhyAddrLen)
1802 {
1803   FIXME("(DestIP 0x%08lx, SrcIP 0x%08lx, pMacAddr %p, PhyAddrLen %p): stub\n",
1804    DestIP, SrcIP, pMacAddr, PhyAddrLen);
1805   return ERROR_NOT_SUPPORTED;
1806 }
1807
1808
1809 /******************************************************************
1810  *    SetIfEntry (IPHLPAPI.@)
1811  *
1812  * Set the administrative status of an interface.
1813  *
1814  * PARAMS
1815  *  pIfRow [In] dwAdminStatus member specifies the new status.
1816  *
1817  * RETURNS
1818  *  Success: NO_ERROR
1819  *  Failure: error code from winerror.h
1820  *
1821  * FIXME
1822  *  Stub, returns ERROR_NOT_SUPPORTED.
1823  */
1824 DWORD WINAPI SetIfEntry(PMIB_IFROW pIfRow)
1825 {
1826   FIXME("(pIfRow %p): stub\n", pIfRow);
1827   /* this is supposed to set an interface administratively up or down.
1828      Could do SIOCSIFFLAGS and set/clear IFF_UP, but, not sure I want to, and
1829      this sort of down is indistinguishable from other sorts of down (e.g. no
1830      link). */
1831   return ERROR_NOT_SUPPORTED;
1832 }
1833
1834
1835 /******************************************************************
1836  *    SetIpForwardEntry (IPHLPAPI.@)
1837  *
1838  * Modify an existing route.
1839  *
1840  * PARAMS
1841  *  pRoute [In] route with the new information
1842  *
1843  * RETURNS
1844  *  Success: NO_ERROR
1845  *  Failure: error code from winerror.h
1846  *
1847  * FIXME
1848  *  Stub, returns NO_ERROR.
1849  */
1850 DWORD WINAPI SetIpForwardEntry(PMIB_IPFORWARDROW pRoute)
1851 {
1852   FIXME("(pRoute %p): stub\n", pRoute);
1853   /* this is to add a route entry, how's it distinguishable from
1854      CreateIpForwardEntry?
1855      could use SIOCADDRT, not sure I want to */
1856   return (DWORD) 0;
1857 }
1858
1859
1860 /******************************************************************
1861  *    SetIpNetEntry (IPHLPAPI.@)
1862  *
1863  * Modify an existing ARP entry.
1864  *
1865  * PARAMS
1866  *  pArpEntry [In] ARP entry with the new information
1867  *
1868  * RETURNS
1869  *  Success: NO_ERROR
1870  *  Failure: error code from winerror.h
1871  *
1872  * FIXME
1873  *  Stub, returns NO_ERROR.
1874  */
1875 DWORD WINAPI SetIpNetEntry(PMIB_IPNETROW pArpEntry)
1876 {
1877   FIXME("(pArpEntry %p): stub\n", pArpEntry);
1878   /* same as CreateIpNetEntry here, could use SIOCSARP, not sure I want to */
1879   return (DWORD) 0;
1880 }
1881
1882
1883 /******************************************************************
1884  *    SetIpStatistics (IPHLPAPI.@)
1885  *
1886  * Toggle IP forwarding and det the default TTL value.
1887  *
1888  * PARAMS
1889  *  pIpStats [In] IP statistics with the new information
1890  *
1891  * RETURNS
1892  *  Success: NO_ERROR
1893  *  Failure: error code from winerror.h
1894  *
1895  * FIXME
1896  *  Stub, returns NO_ERROR.
1897  */
1898 DWORD WINAPI SetIpStatistics(PMIB_IPSTATS pIpStats)
1899 {
1900   FIXME("(pIpStats %p): stub\n", pIpStats);
1901   return (DWORD) 0;
1902 }
1903
1904
1905 /******************************************************************
1906  *    SetIpTTL (IPHLPAPI.@)
1907  *
1908  * Set the default TTL value.
1909  *
1910  * PARAMS
1911  *  nTTL [In] new TTL value
1912  *
1913  * RETURNS
1914  *  Success: NO_ERROR
1915  *  Failure: error code from winerror.h
1916  *
1917  * FIXME
1918  *  Stub, returns NO_ERROR.
1919  */
1920 DWORD WINAPI SetIpTTL(UINT nTTL)
1921 {
1922   FIXME("(nTTL %d): stub\n", nTTL);
1923   /* could echo nTTL > /proc/net/sys/net/ipv4/ip_default_ttl, not sure I
1924      want to.  Could map EACCESS to ERROR_ACCESS_DENIED, I suppose */
1925   return (DWORD) 0;
1926 }
1927
1928
1929 /******************************************************************
1930  *    SetTcpEntry (IPHLPAPI.@)
1931  *
1932  * Set the state of a TCP connection.
1933  *
1934  * PARAMS
1935  *  pTcpRow [In] specifies connection with new state
1936  *
1937  * RETURNS
1938  *  Success: NO_ERROR
1939  *  Failure: error code from winerror.h
1940  *
1941  * FIXME
1942  *  Stub, returns NO_ERROR.
1943  */
1944 DWORD WINAPI SetTcpEntry(PMIB_TCPROW pTcpRow)
1945 {
1946   FIXME("(pTcpRow %p): stub\n", pTcpRow);
1947   return (DWORD) 0;
1948 }
1949
1950
1951 /******************************************************************
1952  *    UnenableRouter (IPHLPAPI.@)
1953  *
1954  * Decrement the IP-forwarding reference count. Turn off IP-forwarding
1955  * if it reaches zero.
1956  *
1957  * PARAMS
1958  *  pOverlapped     [In/Out] should be the same as in EnableRouter()
1959  *  lpdwEnableCount [Out]    optional, receives reference count
1960  *
1961  * RETURNS
1962  *  Success: NO_ERROR
1963  *  Failure: error code from winerror.h
1964  *
1965  * FIXME
1966  *  Stub, returns ERROR_NOT_SUPPORTED.
1967  */
1968 DWORD WINAPI UnenableRouter(OVERLAPPED * pOverlapped, LPDWORD lpdwEnableCount)
1969 {
1970   FIXME("(pOverlapped %p, lpdwEnableCount %p): stub\n", pOverlapped,
1971    lpdwEnableCount);
1972   /* could echo "0" > /proc/net/sys/net/ipv4/ip_forward, not sure I want to
1973      could map EACCESS to ERROR_ACCESS_DENIED, I suppose
1974    */
1975   return ERROR_NOT_SUPPORTED;
1976 }