secur32: Update ntlm_auth version detection to detect new samba4 version numbers.
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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         if (ipAddrTable)
759           HeapFree(GetProcessHeap(), 0, ipAddrTable);
760       }
761     }
762     else
763       ret = ERROR_NO_DATA;
764   }
765   TRACE("returning %ld\n", ret);
766   return ret;
767 }
768
769
770 /******************************************************************
771  *    GetBestInterface (IPHLPAPI.@)
772  *
773  * Get the interface, with the best route for the given IP address.
774  *
775  * PARAMS
776  *  dwDestAddr     [In]  IP address to search the interface for
777  *  pdwBestIfIndex [Out] found best interface
778  *
779  * RETURNS
780  *  Success: NO_ERROR
781  *  Failure: error code from winerror.h
782  */
783 DWORD WINAPI GetBestInterface(IPAddr dwDestAddr, PDWORD pdwBestIfIndex)
784 {
785   DWORD ret;
786
787   TRACE("dwDestAddr 0x%08lx, pdwBestIfIndex %p\n", dwDestAddr, pdwBestIfIndex);
788   if (!pdwBestIfIndex)
789     ret = ERROR_INVALID_PARAMETER;
790   else {
791     MIB_IPFORWARDROW ipRow;
792
793     ret = GetBestRoute(dwDestAddr, 0, &ipRow);
794     if (ret == ERROR_SUCCESS)
795       *pdwBestIfIndex = ipRow.dwForwardIfIndex;
796   }
797   TRACE("returning %ld\n", ret);
798   return ret;
799 }
800
801
802 /******************************************************************
803  *    GetBestRoute (IPHLPAPI.@)
804  *
805  * Get the best route for the given IP address.
806  *
807  * PARAMS
808  *  dwDestAddr   [In]  IP address to search the best route for
809  *  dwSourceAddr [In]  optional source IP address
810  *  pBestRoute   [Out] found best route
811  *
812  * RETURNS
813  *  Success: NO_ERROR
814  *  Failure: error code from winerror.h
815  */
816 DWORD WINAPI GetBestRoute(DWORD dwDestAddr, DWORD dwSourceAddr, PMIB_IPFORWARDROW pBestRoute)
817 {
818   PMIB_IPFORWARDTABLE table;
819   DWORD ret;
820
821   TRACE("dwDestAddr 0x%08lx, dwSourceAddr 0x%08lx, pBestRoute %p\n", dwDestAddr,
822    dwSourceAddr, pBestRoute);
823   if (!pBestRoute)
824     return ERROR_INVALID_PARAMETER;
825
826   AllocateAndGetIpForwardTableFromStack(&table, FALSE, GetProcessHeap(), 0);
827   if (table) {
828     DWORD ndx, matchedBits, matchedNdx = 0;
829
830     for (ndx = 0, matchedBits = 0; ndx < table->dwNumEntries; ndx++) {
831       if (table->table[ndx].dwForwardType != MIB_IPROUTE_TYPE_INVALID &&
832        (dwDestAddr & table->table[ndx].dwForwardMask) ==
833        (table->table[ndx].dwForwardDest & table->table[ndx].dwForwardMask)) {
834         DWORD numShifts, mask;
835
836         for (numShifts = 0, mask = table->table[ndx].dwForwardMask;
837          mask && !(mask & 1); mask >>= 1, numShifts++)
838           ;
839         if (numShifts > matchedBits) {
840           matchedBits = numShifts;
841           matchedNdx = ndx;
842         }
843       }
844     }
845     if (matchedNdx < table->dwNumEntries) {
846       memcpy(pBestRoute, &table->table[matchedNdx], sizeof(MIB_IPFORWARDROW));
847       ret = ERROR_SUCCESS;
848     }
849     else {
850       /* No route matches, which can happen if there's no default route. */
851       ret = ERROR_HOST_UNREACHABLE;
852     }
853     HeapFree(GetProcessHeap(), 0, table);
854   }
855   else
856     ret = ERROR_OUTOFMEMORY;
857   TRACE("returning %ld\n", ret);
858   return ret;
859 }
860
861
862 /******************************************************************
863  *    GetFriendlyIfIndex (IPHLPAPI.@)
864  *
865  * Get a "friendly" version of IfIndex, which is one that doesn't
866  * have the top byte set.  Doesn't validate whether IfIndex is a valid
867  * adapter index.
868  *
869  * PARAMS
870  *  IfIndex [In] interface index to get the friendly one for
871  *
872  * RETURNS
873  *  A friendly version of IfIndex.
874  */
875 DWORD WINAPI GetFriendlyIfIndex(DWORD IfIndex)
876 {
877   /* windows doesn't validate these, either, just makes sure the top byte is
878      cleared.  I assume my ifenum module never gives an index with the top
879      byte set. */
880   TRACE("returning %ld\n", IfIndex);
881   return IfIndex;
882 }
883
884
885 /******************************************************************
886  *    GetIcmpStatistics (IPHLPAPI.@)
887  *
888  * Get the ICMP statistics for the local computer.
889  *
890  * PARAMS
891  *  pStats [Out] buffer for ICMP statistics
892  *
893  * RETURNS
894  *  Success: NO_ERROR
895  *  Failure: error code from winerror.h
896  */
897 DWORD WINAPI GetIcmpStatistics(PMIB_ICMP pStats)
898 {
899   DWORD ret;
900
901   TRACE("pStats %p\n", pStats);
902   ret = getICMPStats(pStats);
903   TRACE("returning %ld\n", ret);
904   return ret;
905 }
906
907
908 /******************************************************************
909  *    GetIfEntry (IPHLPAPI.@)
910  *
911  * Get information about an interface.
912  *
913  * PARAMS
914  *  pIfRow [In/Out] In:  dwIndex of MIB_IFROW selects the interface.
915  *                  Out: interface information
916  *
917  * RETURNS
918  *  Success: NO_ERROR
919  *  Failure: error code from winerror.h
920  */
921 DWORD WINAPI GetIfEntry(PMIB_IFROW pIfRow)
922 {
923   DWORD ret;
924   char nameBuf[MAX_ADAPTER_NAME];
925   char *name;
926
927   TRACE("pIfRow %p\n", pIfRow);
928   if (!pIfRow)
929     return ERROR_INVALID_PARAMETER;
930
931   name = getInterfaceNameByIndex(pIfRow->dwIndex, nameBuf);
932   if (name) {
933     ret = getInterfaceEntryByName(name, pIfRow);
934     if (ret == NO_ERROR)
935       ret = getInterfaceStatsByName(name, pIfRow);
936   }
937   else
938     ret = ERROR_INVALID_DATA;
939   TRACE("returning %ld\n", ret);
940   return ret;
941 }
942
943
944 static int IfTableSorter(const void *a, const void *b)
945 {
946   int ret;
947
948   if (a && b)
949     ret = ((const MIB_IFROW*)a)->dwIndex - ((const MIB_IFROW*)b)->dwIndex;
950   else
951     ret = 0;
952   return ret;
953 }
954
955
956 /******************************************************************
957  *    GetIfTable (IPHLPAPI.@)
958  *
959  * Get a table of local interfaces.
960  *
961  * PARAMS
962  *  pIfTable [Out]    buffer for local interfaces table
963  *  pdwSize  [In/Out] length of output buffer
964  *  bOrder   [In]     whether to sort the table
965  *
966  * RETURNS
967  *  Success: NO_ERROR
968  *  Failure: error code from winerror.h
969  *
970  * NOTES
971  *  If pdwSize is less than required, the function will return
972  *  ERROR_INSUFFICIENT_BUFFER, and *pdwSize will be set to the required byte
973  *  size.
974  *  If bOrder is true, the returned table will be sorted by interface index.
975  */
976 DWORD WINAPI GetIfTable(PMIB_IFTABLE pIfTable, PULONG pdwSize, BOOL bOrder)
977 {
978   DWORD ret;
979
980   TRACE("pIfTable %p, pdwSize %p, bOrder %ld\n", pdwSize, pdwSize,
981    (DWORD)bOrder);
982   if (!pdwSize)
983     ret = ERROR_INVALID_PARAMETER;
984   else {
985     DWORD numInterfaces = getNumInterfaces();
986     ULONG size = sizeof(MIB_IFTABLE) + (numInterfaces - 1) * sizeof(MIB_IFROW);
987
988     if (!pIfTable || *pdwSize < size) {
989       *pdwSize = size;
990       ret = ERROR_INSUFFICIENT_BUFFER;
991     }
992     else {
993       InterfaceIndexTable *table = getInterfaceIndexTable();
994
995       if (table) {
996         size = sizeof(MIB_IFTABLE) + (table->numIndexes - 1) *
997          sizeof(MIB_IFROW);
998         if (*pdwSize < size) {
999           *pdwSize = size;
1000           ret = ERROR_INSUFFICIENT_BUFFER;
1001         }
1002         else {
1003           DWORD ndx;
1004
1005           *pdwSize = size;
1006           pIfTable->dwNumEntries = 0;
1007           for (ndx = 0; ndx < table->numIndexes; ndx++) {
1008             pIfTable->table[ndx].dwIndex = table->indexes[ndx];
1009             GetIfEntry(&pIfTable->table[ndx]);
1010             pIfTable->dwNumEntries++;
1011           }
1012           if (bOrder)
1013             qsort(pIfTable->table, pIfTable->dwNumEntries, sizeof(MIB_IFROW),
1014              IfTableSorter);
1015           ret = NO_ERROR;
1016         }
1017         HeapFree(GetProcessHeap(), 0, table);
1018       }
1019       else
1020         ret = ERROR_OUTOFMEMORY;
1021     }
1022   }
1023   TRACE("returning %ld\n", ret);
1024   return ret;
1025 }
1026
1027
1028 /******************************************************************
1029  *    GetInterfaceInfo (IPHLPAPI.@)
1030  *
1031  * Get a list of network interface adapters.
1032  *
1033  * PARAMS
1034  *  pIfTable    [Out] buffer for interface adapters
1035  *  dwOutBufLen [Out] if buffer is too small, returns required size
1036  *
1037  * RETURNS
1038  *  Success: NO_ERROR
1039  *  Failure: error code from winerror.h
1040  *
1041  * BUGS
1042  *  MSDN states this should return non-loopback interfaces only.
1043  */
1044 DWORD WINAPI GetInterfaceInfo(PIP_INTERFACE_INFO pIfTable, PULONG dwOutBufLen)
1045 {
1046   DWORD ret;
1047
1048   TRACE("pIfTable %p, dwOutBufLen %p\n", pIfTable, dwOutBufLen);
1049   if (!dwOutBufLen)
1050     ret = ERROR_INVALID_PARAMETER;
1051   else {
1052     DWORD numInterfaces = getNumInterfaces();
1053     ULONG size = sizeof(IP_INTERFACE_INFO) + (numInterfaces - 1) *
1054      sizeof(IP_ADAPTER_INDEX_MAP);
1055
1056     if (!pIfTable || *dwOutBufLen < size) {
1057       *dwOutBufLen = size;
1058       ret = ERROR_INSUFFICIENT_BUFFER;
1059     }
1060     else {
1061       InterfaceIndexTable *table = getInterfaceIndexTable();
1062
1063       if (table) {
1064         size = sizeof(IP_INTERFACE_INFO) + (table->numIndexes - 1) *
1065          sizeof(IP_ADAPTER_INDEX_MAP);
1066         if (*dwOutBufLen < size) {
1067           *dwOutBufLen = size;
1068           ret = ERROR_INSUFFICIENT_BUFFER;
1069         }
1070         else {
1071           DWORD ndx;
1072           char nameBuf[MAX_ADAPTER_NAME];
1073
1074           *dwOutBufLen = size;
1075           pIfTable->NumAdapters = 0;
1076           for (ndx = 0; ndx < table->numIndexes; ndx++) {
1077             const char *walker, *name;
1078             WCHAR *assigner;
1079
1080             pIfTable->Adapter[ndx].Index = table->indexes[ndx];
1081             name = getInterfaceNameByIndex(table->indexes[ndx], nameBuf);
1082             for (walker = name, assigner = pIfTable->Adapter[ndx].Name;
1083              walker && *walker &&
1084              assigner - pIfTable->Adapter[ndx].Name < MAX_ADAPTER_NAME - 1;
1085              walker++, assigner++)
1086               *assigner = *walker;
1087             *assigner = 0;
1088             pIfTable->NumAdapters++;
1089           }
1090           ret = NO_ERROR;
1091         }
1092         HeapFree(GetProcessHeap(), 0, table);
1093       }
1094       else
1095         ret = ERROR_OUTOFMEMORY;
1096     }
1097   }
1098   TRACE("returning %ld\n", ret);
1099   return ret;
1100 }
1101
1102
1103 /******************************************************************
1104  *    GetIpAddrTable (IPHLPAPI.@)
1105  *
1106  * Get interface-to-IP address mapping table. 
1107  *
1108  * PARAMS
1109  *  pIpAddrTable [Out]    buffer for mapping table
1110  *  pdwSize      [In/Out] length of output buffer
1111  *  bOrder       [In]     whether to sort the table
1112  *
1113  * RETURNS
1114  *  Success: NO_ERROR
1115  *  Failure: error code from winerror.h
1116  *
1117  * NOTES
1118  *  If pdwSize is less than required, the function will return
1119  *  ERROR_INSUFFICIENT_BUFFER, and *pdwSize will be set to the required byte
1120  *  size.
1121  *  If bOrder is true, the returned table will be sorted by the next hop and
1122  *  an assortment of arbitrary parameters.
1123  */
1124 DWORD WINAPI GetIpAddrTable(PMIB_IPADDRTABLE pIpAddrTable, PULONG pdwSize, BOOL bOrder)
1125 {
1126   DWORD ret;
1127
1128   TRACE("pIpAddrTable %p, pdwSize %p, bOrder %ld\n", pIpAddrTable, pdwSize,
1129    (DWORD)bOrder);
1130   if (!pdwSize)
1131     ret = ERROR_INVALID_PARAMETER;
1132   else {
1133     PMIB_IPADDRTABLE table;
1134
1135     ret = getIPAddrTable(&table, GetProcessHeap(), 0);
1136     if (ret == NO_ERROR)
1137     {
1138       ULONG size = sizeof(MIB_IPADDRTABLE) + (table->dwNumEntries - 1) *
1139        sizeof(MIB_IPADDRROW);
1140
1141       if (!pIpAddrTable || *pdwSize < size) {
1142         *pdwSize = size;
1143         ret = ERROR_INSUFFICIENT_BUFFER;
1144       }
1145       else {
1146         *pdwSize = size;
1147         memcpy(pIpAddrTable, table, sizeof(MIB_IPADDRTABLE) +
1148          (table->dwNumEntries - 1) * sizeof(MIB_IPADDRROW));
1149         if (bOrder)
1150           qsort(pIpAddrTable->table, pIpAddrTable->dwNumEntries,
1151            sizeof(MIB_IPADDRROW), IpAddrTableSorter);
1152         ret = NO_ERROR;
1153       }
1154       HeapFree(GetProcessHeap(), 0, table);
1155     }
1156   }
1157   TRACE("returning %ld\n", ret);
1158   return ret;
1159 }
1160
1161
1162 /******************************************************************
1163  *    GetIpForwardTable (IPHLPAPI.@)
1164  *
1165  * Get the route table.
1166  *
1167  * PARAMS
1168  *  pIpForwardTable [Out]    buffer for route table
1169  *  pdwSize         [In/Out] length of output buffer
1170  *  bOrder          [In]     whether to sort the table
1171  *
1172  * RETURNS
1173  *  Success: NO_ERROR
1174  *  Failure: error code from winerror.h
1175  *
1176  * NOTES
1177  *  If pdwSize is less than required, the function will return
1178  *  ERROR_INSUFFICIENT_BUFFER, and *pdwSize will be set to the required byte
1179  *  size.
1180  *  If bOrder is true, the returned table will be sorted by the next hop and
1181  *  an assortment of arbitrary parameters.
1182  */
1183 DWORD WINAPI GetIpForwardTable(PMIB_IPFORWARDTABLE pIpForwardTable, PULONG pdwSize, BOOL bOrder)
1184 {
1185   DWORD ret;
1186
1187   TRACE("pIpForwardTable %p, pdwSize %p, bOrder %ld\n", pIpForwardTable,
1188    pdwSize, (DWORD)bOrder);
1189   if (!pdwSize)
1190     ret = ERROR_INVALID_PARAMETER;
1191   else {
1192     DWORD numRoutes = getNumRoutes();
1193     ULONG sizeNeeded = sizeof(MIB_IPFORWARDTABLE) + (numRoutes - 1) *
1194      sizeof(MIB_IPFORWARDROW);
1195
1196     if (!pIpForwardTable || *pdwSize < sizeNeeded) {
1197       *pdwSize = sizeNeeded;
1198       ret = ERROR_INSUFFICIENT_BUFFER;
1199     }
1200     else {
1201       PMIB_IPFORWARDTABLE table;
1202
1203       ret = getRouteTable(&table, GetProcessHeap(), 0);
1204       if (!ret) {
1205         sizeNeeded = sizeof(MIB_IPFORWARDTABLE) + (table->dwNumEntries - 1) *
1206          sizeof(MIB_IPFORWARDROW);
1207         if (*pdwSize < sizeNeeded) {
1208           *pdwSize = sizeNeeded;
1209           ret = ERROR_INSUFFICIENT_BUFFER;
1210         }
1211         else {
1212           *pdwSize = sizeNeeded;
1213           memcpy(pIpForwardTable, table, sizeNeeded);
1214           if (bOrder)
1215             qsort(pIpForwardTable->table, pIpForwardTable->dwNumEntries,
1216              sizeof(MIB_IPFORWARDROW), IpForwardTableSorter);
1217           ret = NO_ERROR;
1218         }
1219         HeapFree(GetProcessHeap(), 0, table);
1220       }
1221     }
1222   }
1223   TRACE("returning %ld\n", ret);
1224   return ret;
1225 }
1226
1227
1228 /******************************************************************
1229  *    GetIpNetTable (IPHLPAPI.@)
1230  *
1231  * Get the IP-to-physical address mapping table.
1232  *
1233  * PARAMS
1234  *  pIpNetTable [Out]    buffer for mapping table
1235  *  pdwSize     [In/Out] length of output buffer
1236  *  bOrder      [In]     whether to sort the table
1237  *
1238  * RETURNS
1239  *  Success: NO_ERROR
1240  *  Failure: error code from winerror.h
1241  *
1242  * NOTES
1243  *  If pdwSize is less than required, the function will return
1244  *  ERROR_INSUFFICIENT_BUFFER, and *pdwSize will be set to the required byte
1245  *  size.
1246  *  If bOrder is true, the returned table will be sorted by IP address.
1247  */
1248 DWORD WINAPI GetIpNetTable(PMIB_IPNETTABLE pIpNetTable, PULONG pdwSize, BOOL bOrder)
1249 {
1250   DWORD ret;
1251
1252   TRACE("pIpNetTable %p, pdwSize %p, bOrder %ld\n", pIpNetTable, pdwSize,
1253    (DWORD)bOrder);
1254   if (!pdwSize)
1255     ret = ERROR_INVALID_PARAMETER;
1256   else {
1257     DWORD numEntries = getNumArpEntries();
1258     ULONG size = sizeof(MIB_IPNETTABLE) + (numEntries - 1) *
1259      sizeof(MIB_IPNETROW);
1260
1261     if (!pIpNetTable || *pdwSize < size) {
1262       *pdwSize = size;
1263       ret = ERROR_INSUFFICIENT_BUFFER;
1264     }
1265     else {
1266       PMIB_IPNETTABLE table;
1267
1268       ret = getArpTable(&table, GetProcessHeap(), 0);
1269       if (!ret) {
1270         size = sizeof(MIB_IPNETTABLE) + (table->dwNumEntries - 1) *
1271          sizeof(MIB_IPNETROW);
1272         if (*pdwSize < size) {
1273           *pdwSize = size;
1274           ret = ERROR_INSUFFICIENT_BUFFER;
1275         }
1276         else {
1277           *pdwSize = size;
1278           memcpy(pIpNetTable, table, size);
1279           if (bOrder)
1280             qsort(pIpNetTable->table, pIpNetTable->dwNumEntries,
1281              sizeof(MIB_IPNETROW), IpNetTableSorter);
1282           ret = NO_ERROR;
1283         }
1284         HeapFree(GetProcessHeap(), 0, table);
1285       }
1286     }
1287   }
1288   TRACE("returning %ld\n", ret);
1289   return ret;
1290 }
1291
1292
1293 /******************************************************************
1294  *    GetIpStatistics (IPHLPAPI.@)
1295  *
1296  * Get the IP statistics for the local computer.
1297  *
1298  * PARAMS
1299  *  pStats [Out] buffer for IP statistics
1300  *
1301  * RETURNS
1302  *  Success: NO_ERROR
1303  *  Failure: error code from winerror.h
1304  */
1305 DWORD WINAPI GetIpStatistics(PMIB_IPSTATS pStats)
1306 {
1307   DWORD ret;
1308
1309   TRACE("pStats %p\n", pStats);
1310   ret = getIPStats(pStats);
1311   TRACE("returning %ld\n", ret);
1312   return ret;
1313 }
1314
1315
1316 /******************************************************************
1317  *    GetNetworkParams (IPHLPAPI.@)
1318  *
1319  * Get the network parameters for the local computer.
1320  *
1321  * PARAMS
1322  *  pFixedInfo [Out]    buffer for network parameters
1323  *  pOutBufLen [In/Out] length of output buffer
1324  *
1325  * RETURNS
1326  *  Success: NO_ERROR
1327  *  Failure: error code from winerror.h
1328  *
1329  * NOTES
1330  *  If pOutBufLen is less than required, the function will return
1331  *  ERROR_INSUFFICIENT_BUFFER, and pOutBufLen will be set to the required byte
1332  *  size.
1333  */
1334 DWORD WINAPI GetNetworkParams(PFIXED_INFO pFixedInfo, PULONG pOutBufLen)
1335 {
1336   DWORD ret, size;
1337   LONG regReturn;
1338   HKEY hKey;
1339
1340   TRACE("pFixedInfo %p, pOutBufLen %p\n", pFixedInfo, pOutBufLen);
1341   if (!pOutBufLen)
1342     return ERROR_INVALID_PARAMETER;
1343
1344   res_init();
1345   size = sizeof(FIXED_INFO) + (_res.nscount > 0 ? (_res.nscount  - 1) *
1346    sizeof(IP_ADDR_STRING) : 0);
1347   if (!pFixedInfo || *pOutBufLen < size) {
1348     *pOutBufLen = size;
1349     return ERROR_BUFFER_OVERFLOW;
1350   }
1351
1352   memset(pFixedInfo, 0, size);
1353   size = sizeof(pFixedInfo->HostName);
1354   GetComputerNameExA(ComputerNameDnsHostname, pFixedInfo->HostName, &size);
1355   size = sizeof(pFixedInfo->DomainName);
1356   GetComputerNameExA(ComputerNameDnsDomain, pFixedInfo->DomainName, &size);
1357   if (_res.nscount > 0) {
1358     PIP_ADDR_STRING ptr;
1359     int i;
1360
1361     for (i = 0, ptr = &pFixedInfo->DnsServerList; i < _res.nscount && ptr;
1362      i++, ptr = ptr->Next) {
1363       toIPAddressString(_res.nsaddr_list[i].sin_addr.s_addr,
1364        ptr->IpAddress.String);
1365       if (i == _res.nscount - 1)
1366         ptr->Next = NULL;
1367       else if (i == 0)
1368         ptr->Next = (PIP_ADDR_STRING)((LPBYTE)pFixedInfo + sizeof(FIXED_INFO));
1369       else
1370         ptr->Next = (PIP_ADDR_STRING)((PBYTE)ptr + sizeof(IP_ADDR_STRING));
1371     }
1372   }
1373   pFixedInfo->NodeType = HYBRID_NODETYPE;
1374   regReturn = RegOpenKeyExA(HKEY_LOCAL_MACHINE,
1375    "SYSTEM\\CurrentControlSet\\Services\\VxD\\MSTCP", 0, KEY_READ, &hKey);
1376   if (regReturn != ERROR_SUCCESS)
1377     regReturn = RegOpenKeyExA(HKEY_LOCAL_MACHINE,
1378      "SYSTEM\\CurrentControlSet\\Services\\NetBT\\Parameters", 0, KEY_READ,
1379      &hKey);
1380   if (regReturn == ERROR_SUCCESS)
1381   {
1382     DWORD size = sizeof(pFixedInfo->ScopeId);
1383
1384     RegQueryValueExA(hKey, "ScopeID", NULL, NULL, (LPBYTE)pFixedInfo->ScopeId, &size);
1385     RegCloseKey(hKey);
1386   }
1387
1388   /* FIXME: can check whether routing's enabled in /proc/sys/net/ipv4/ip_forward
1389      I suppose could also check for a listener on port 53 to set EnableDns */
1390   ret = NO_ERROR;
1391   TRACE("returning %ld\n", ret);
1392   return ret;
1393 }
1394
1395
1396 /******************************************************************
1397  *    GetNumberOfInterfaces (IPHLPAPI.@)
1398  *
1399  * Get the number of interfaces.
1400  *
1401  * PARAMS
1402  *  pdwNumIf [Out] number of interfaces
1403  *
1404  * RETURNS
1405  *  NO_ERROR on success, ERROR_INVALID_PARAMETER if pdwNumIf is NULL.
1406  */
1407 DWORD WINAPI GetNumberOfInterfaces(PDWORD pdwNumIf)
1408 {
1409   DWORD ret;
1410
1411   TRACE("pdwNumIf %p\n", pdwNumIf);
1412   if (!pdwNumIf)
1413     ret = ERROR_INVALID_PARAMETER;
1414   else {
1415     *pdwNumIf = getNumInterfaces();
1416     ret = NO_ERROR;
1417   }
1418   TRACE("returning %ld\n", ret);
1419   return ret;
1420 }
1421
1422
1423 /******************************************************************
1424  *    GetPerAdapterInfo (IPHLPAPI.@)
1425  *
1426  * Get information about an adapter corresponding to an interface.
1427  *
1428  * PARAMS
1429  *  IfIndex         [In]     interface info
1430  *  pPerAdapterInfo [Out]    buffer for per adapter info
1431  *  pOutBufLen      [In/Out] length of output buffer
1432  *
1433  * RETURNS
1434  *  Success: NO_ERROR
1435  *  Failure: error code from winerror.h
1436  *
1437  * FIXME
1438  *  Stub, returns ERROR_NOT_SUPPORTED.
1439  */
1440 DWORD WINAPI GetPerAdapterInfo(ULONG IfIndex, PIP_PER_ADAPTER_INFO pPerAdapterInfo, PULONG pOutBufLen)
1441 {
1442   TRACE("(IfIndex %ld, pPerAdapterInfo %p, pOutBufLen %p)\n", IfIndex,
1443    pPerAdapterInfo, pOutBufLen);
1444   return ERROR_NOT_SUPPORTED;
1445 }
1446
1447
1448 /******************************************************************
1449  *    GetRTTAndHopCount (IPHLPAPI.@)
1450  *
1451  * Get round-trip time (RTT) and hop count.
1452  *
1453  * PARAMS
1454  *
1455  *  DestIpAddress [In]  destination address to get the info for
1456  *  HopCount      [Out] retrieved hop count
1457  *  MaxHops       [In]  maximum hops to search for the destination
1458  *  RTT           [Out] RTT in milliseconds
1459  *
1460  * RETURNS
1461  *  Success: TRUE
1462  *  Failure: FALSE
1463  *
1464  * FIXME
1465  *  Stub, returns FALSE.
1466  */
1467 BOOL WINAPI GetRTTAndHopCount(IPAddr DestIpAddress, PULONG HopCount, ULONG MaxHops, PULONG RTT)
1468 {
1469   FIXME("(DestIpAddress 0x%08lx, HopCount %p, MaxHops %ld, RTT %p): stub\n",
1470    DestIpAddress, HopCount, MaxHops, RTT);
1471   return FALSE;
1472 }
1473
1474
1475 /******************************************************************
1476  *    GetTcpStatistics (IPHLPAPI.@)
1477  *
1478  * Get the TCP statistics for the local computer.
1479  *
1480  * PARAMS
1481  *  pStats [Out] buffer for TCP statistics
1482  *
1483  * RETURNS
1484  *  Success: NO_ERROR
1485  *  Failure: error code from winerror.h
1486  */
1487 DWORD WINAPI GetTcpStatistics(PMIB_TCPSTATS pStats)
1488 {
1489   DWORD ret;
1490
1491   TRACE("pStats %p\n", pStats);
1492   ret = getTCPStats(pStats);
1493   TRACE("returning %ld\n", ret);
1494   return ret;
1495 }
1496
1497
1498 /******************************************************************
1499  *    GetTcpTable (IPHLPAPI.@)
1500  *
1501  * Get the table of active TCP connections.
1502  *
1503  * PARAMS
1504  *  pTcpTable [Out]    buffer for TCP connections table
1505  *  pdwSize   [In/Out] length of output buffer
1506  *  bOrder    [In]     whether to order the table
1507  *
1508  * RETURNS
1509  *  Success: NO_ERROR
1510  *  Failure: error code from winerror.h
1511  *
1512  * NOTES
1513  *  If pdwSize is less than required, the function will return 
1514  *  ERROR_INSUFFICIENT_BUFFER, and *pdwSize will be set to 
1515  *  the required byte size.
1516  *  If bOrder is true, the returned table will be sorted, first by
1517  *  local address and port number, then by remote address and port
1518  *  number.
1519  */
1520 DWORD WINAPI GetTcpTable(PMIB_TCPTABLE pTcpTable, PDWORD pdwSize, BOOL bOrder)
1521 {
1522   DWORD ret;
1523
1524   TRACE("pTcpTable %p, pdwSize %p, bOrder %ld\n", pTcpTable, pdwSize,
1525    (DWORD)bOrder);
1526   if (!pdwSize)
1527     ret = ERROR_INVALID_PARAMETER;
1528   else {
1529     DWORD numEntries = getNumTcpEntries();
1530     DWORD size = sizeof(MIB_TCPTABLE) + (numEntries - 1) * sizeof(MIB_TCPROW);
1531
1532     if (!pTcpTable || *pdwSize < size) {
1533       *pdwSize = size;
1534       ret = ERROR_INSUFFICIENT_BUFFER;
1535     }
1536     else {
1537       PMIB_TCPTABLE table;
1538
1539       ret = getTcpTable(&table, GetProcessHeap(), 0);
1540       if (!ret) {
1541         size = sizeof(MIB_TCPTABLE) + (table->dwNumEntries - 1) *
1542          sizeof(MIB_TCPROW);
1543         if (*pdwSize < size) {
1544           *pdwSize = size;
1545           ret = ERROR_INSUFFICIENT_BUFFER;
1546         }
1547         else {
1548           *pdwSize = size;
1549           memcpy(pTcpTable, table, size);
1550           if (bOrder)
1551             qsort(pTcpTable->table, pTcpTable->dwNumEntries,
1552              sizeof(MIB_TCPROW), TcpTableSorter);
1553           ret = NO_ERROR;
1554         }
1555         HeapFree(GetProcessHeap(), 0, table);
1556       }
1557       else
1558         ret = ERROR_OUTOFMEMORY;
1559     }
1560   }
1561   TRACE("returning %ld\n", ret);
1562   return ret;
1563 }
1564
1565
1566 /******************************************************************
1567  *    GetUdpStatistics (IPHLPAPI.@)
1568  *
1569  * Get the UDP statistics for the local computer.
1570  *
1571  * PARAMS
1572  *  pStats [Out] buffer for UDP statistics
1573  *
1574  * RETURNS
1575  *  Success: NO_ERROR
1576  *  Failure: error code from winerror.h
1577  */
1578 DWORD WINAPI GetUdpStatistics(PMIB_UDPSTATS pStats)
1579 {
1580   DWORD ret;
1581
1582   TRACE("pStats %p\n", pStats);
1583   ret = getUDPStats(pStats);
1584   TRACE("returning %ld\n", ret);
1585   return ret;
1586 }
1587
1588
1589 /******************************************************************
1590  *    GetUdpTable (IPHLPAPI.@)
1591  *
1592  * Get a table of active UDP connections.
1593  *
1594  * PARAMS
1595  *  pUdpTable [Out]    buffer for UDP connections table
1596  *  pdwSize   [In/Out] length of output buffer
1597  *  bOrder    [In]     whether to order the table
1598  *
1599  * RETURNS
1600  *  Success: NO_ERROR
1601  *  Failure: error code from winerror.h
1602  *
1603  * NOTES
1604  *  If pdwSize is less than required, the function will return 
1605  *  ERROR_INSUFFICIENT_BUFFER, and *pdwSize will be set to the
1606  *  required byte size.
1607  *  If bOrder is true, the returned table will be sorted, first by
1608  *  local address, then by local port number.
1609  */
1610 DWORD WINAPI GetUdpTable(PMIB_UDPTABLE pUdpTable, PDWORD pdwSize, BOOL bOrder)
1611 {
1612   DWORD ret;
1613
1614   TRACE("pUdpTable %p, pdwSize %p, bOrder %ld\n", pUdpTable, pdwSize,
1615    (DWORD)bOrder);
1616   if (!pdwSize)
1617     ret = ERROR_INVALID_PARAMETER;
1618   else {
1619     DWORD numEntries = getNumUdpEntries();
1620     DWORD size = sizeof(MIB_UDPTABLE) + (numEntries - 1) * sizeof(MIB_UDPROW);
1621
1622     if (!pUdpTable || *pdwSize < size) {
1623       *pdwSize = size;
1624       ret = ERROR_INSUFFICIENT_BUFFER;
1625     }
1626     else {
1627       PMIB_UDPTABLE table;
1628
1629       ret = getUdpTable(&table, GetProcessHeap(), 0);
1630       if (!ret) {
1631         size = sizeof(MIB_UDPTABLE) + (table->dwNumEntries - 1) *
1632          sizeof(MIB_UDPROW);
1633         if (*pdwSize < size) {
1634           *pdwSize = size;
1635           ret = ERROR_INSUFFICIENT_BUFFER;
1636         }
1637         else {
1638           *pdwSize = size;
1639           memcpy(pUdpTable, table, size);
1640           if (bOrder)
1641             qsort(pUdpTable->table, pUdpTable->dwNumEntries,
1642              sizeof(MIB_UDPROW), UdpTableSorter);
1643           ret = NO_ERROR;
1644         }
1645         HeapFree(GetProcessHeap(), 0, table);
1646       }
1647       else
1648         ret = ERROR_OUTOFMEMORY;
1649     }
1650   }
1651   TRACE("returning %ld\n", ret);
1652   return ret;
1653 }
1654
1655
1656 /******************************************************************
1657  *    GetUniDirectionalAdapterInfo (IPHLPAPI.@)
1658  *
1659  * This is a Win98-only function to get information on "unidirectional"
1660  * adapters.  Since this is pretty nonsensical in other contexts, it
1661  * never returns anything.
1662  *
1663  * PARAMS
1664  *  pIPIfInfo   [Out] buffer for adapter infos
1665  *  dwOutBufLen [Out] length of the output buffer
1666  *
1667  * RETURNS
1668  *  Success: NO_ERROR
1669  *  Failure: error code from winerror.h
1670  *
1671  * FIXME
1672  *  Stub, returns ERROR_NOT_SUPPORTED.
1673  */
1674 DWORD WINAPI GetUniDirectionalAdapterInfo(PIP_UNIDIRECTIONAL_ADAPTER_ADDRESS pIPIfInfo, PULONG dwOutBufLen)
1675 {
1676   TRACE("pIPIfInfo %p, dwOutBufLen %p\n", pIPIfInfo, dwOutBufLen);
1677   /* a unidirectional adapter?? not bloody likely! */
1678   return ERROR_NOT_SUPPORTED;
1679 }
1680
1681
1682 /******************************************************************
1683  *    IpReleaseAddress (IPHLPAPI.@)
1684  *
1685  * Release an IP optained through DHCP,
1686  *
1687  * PARAMS
1688  *  AdapterInfo [In] adapter to release IP address
1689  *
1690  * RETURNS
1691  *  Success: NO_ERROR
1692  *  Failure: error code from winerror.h
1693  *
1694  * NOTES
1695  *  Since GetAdaptersInfo never returns adapters that have DHCP enabled,
1696  *  this function does nothing.
1697  *
1698  * FIXME
1699  *  Stub, returns ERROR_NOT_SUPPORTED.
1700  */
1701 DWORD WINAPI IpReleaseAddress(PIP_ADAPTER_INDEX_MAP AdapterInfo)
1702 {
1703   TRACE("AdapterInfo %p\n", AdapterInfo);
1704   /* not a stub, never going to support this (and I never mark an adapter as
1705      DHCP enabled, see GetAdaptersInfo, so this should never get called) */
1706   return ERROR_NOT_SUPPORTED;
1707 }
1708
1709
1710 /******************************************************************
1711  *    IpRenewAddress (IPHLPAPI.@)
1712  *
1713  * Renew an IP optained through DHCP.
1714  *
1715  * PARAMS
1716  *  AdapterInfo [In] adapter to renew IP address
1717  *
1718  * RETURNS
1719  *  Success: NO_ERROR
1720  *  Failure: error code from winerror.h
1721  *
1722  * NOTES
1723  *  Since GetAdaptersInfo never returns adapters that have DHCP enabled,
1724  *  this function does nothing.
1725  *
1726  * FIXME
1727  *  Stub, returns ERROR_NOT_SUPPORTED.
1728  */
1729 DWORD WINAPI IpRenewAddress(PIP_ADAPTER_INDEX_MAP AdapterInfo)
1730 {
1731   TRACE("AdapterInfo %p\n", AdapterInfo);
1732   /* not a stub, never going to support this (and I never mark an adapter as
1733      DHCP enabled, see GetAdaptersInfo, so this should never get called) */
1734   return ERROR_NOT_SUPPORTED;
1735 }
1736
1737
1738 /******************************************************************
1739  *    NotifyAddrChange (IPHLPAPI.@)
1740  *
1741  * Notify caller whenever the ip-interface map is changed.
1742  *
1743  * PARAMS
1744  *  Handle     [Out] handle useable in asynchronus notification
1745  *  overlapped [In]  overlapped structure that notifies the caller
1746  *
1747  * RETURNS
1748  *  Success: NO_ERROR
1749  *  Failure: error code from winerror.h
1750  *
1751  * FIXME
1752  *  Stub, returns ERROR_NOT_SUPPORTED.
1753  */
1754 DWORD WINAPI NotifyAddrChange(PHANDLE Handle, LPOVERLAPPED overlapped)
1755 {
1756   FIXME("(Handle %p, overlapped %p): stub\n", Handle, overlapped);
1757   return ERROR_NOT_SUPPORTED;
1758 }
1759
1760
1761 /******************************************************************
1762  *    NotifyRouteChange (IPHLPAPI.@)
1763  *
1764  * Notify caller whenever the ip routing table is changed.
1765  *
1766  * PARAMS
1767  *  Handle     [Out] handle useable in asynchronus notification
1768  *  overlapped [In]  overlapped structure that notifies the caller
1769  *
1770  * RETURNS
1771  *  Success: NO_ERROR
1772  *  Failure: error code from winerror.h
1773  *
1774  * FIXME
1775  *  Stub, returns ERROR_NOT_SUPPORTED.
1776  */
1777 DWORD WINAPI NotifyRouteChange(PHANDLE Handle, LPOVERLAPPED overlapped)
1778 {
1779   FIXME("(Handle %p, overlapped %p): stub\n", Handle, overlapped);
1780   return ERROR_NOT_SUPPORTED;
1781 }
1782
1783
1784 /******************************************************************
1785  *    SendARP (IPHLPAPI.@)
1786  *
1787  * Send an ARP request.
1788  *
1789  * PARAMS
1790  *  DestIP     [In]     attempt to obtain this IP
1791  *  SrcIP      [In]     optional sender IP address
1792  *  pMacAddr   [Out]    buffer for the mac address
1793  *  PhyAddrLen [In/Out] length of the output buffer
1794  *
1795  * RETURNS
1796  *  Success: NO_ERROR
1797  *  Failure: error code from winerror.h
1798  *
1799  * FIXME
1800  *  Stub, returns ERROR_NOT_SUPPORTED.
1801  */
1802 DWORD WINAPI SendARP(IPAddr DestIP, IPAddr SrcIP, PULONG pMacAddr, PULONG PhyAddrLen)
1803 {
1804   FIXME("(DestIP 0x%08lx, SrcIP 0x%08lx, pMacAddr %p, PhyAddrLen %p): stub\n",
1805    DestIP, SrcIP, pMacAddr, PhyAddrLen);
1806   return ERROR_NOT_SUPPORTED;
1807 }
1808
1809
1810 /******************************************************************
1811  *    SetIfEntry (IPHLPAPI.@)
1812  *
1813  * Set the administrative status of an interface.
1814  *
1815  * PARAMS
1816  *  pIfRow [In] dwAdminStatus member specifies the new status.
1817  *
1818  * RETURNS
1819  *  Success: NO_ERROR
1820  *  Failure: error code from winerror.h
1821  *
1822  * FIXME
1823  *  Stub, returns ERROR_NOT_SUPPORTED.
1824  */
1825 DWORD WINAPI SetIfEntry(PMIB_IFROW pIfRow)
1826 {
1827   FIXME("(pIfRow %p): stub\n", pIfRow);
1828   /* this is supposed to set an interface administratively up or down.
1829      Could do SIOCSIFFLAGS and set/clear IFF_UP, but, not sure I want to, and
1830      this sort of down is indistinguishable from other sorts of down (e.g. no
1831      link). */
1832   return ERROR_NOT_SUPPORTED;
1833 }
1834
1835
1836 /******************************************************************
1837  *    SetIpForwardEntry (IPHLPAPI.@)
1838  *
1839  * Modify an existing route.
1840  *
1841  * PARAMS
1842  *  pRoute [In] route with the new information
1843  *
1844  * RETURNS
1845  *  Success: NO_ERROR
1846  *  Failure: error code from winerror.h
1847  *
1848  * FIXME
1849  *  Stub, returns NO_ERROR.
1850  */
1851 DWORD WINAPI SetIpForwardEntry(PMIB_IPFORWARDROW pRoute)
1852 {
1853   FIXME("(pRoute %p): stub\n", pRoute);
1854   /* this is to add a route entry, how's it distinguishable from
1855      CreateIpForwardEntry?
1856      could use SIOCADDRT, not sure I want to */
1857   return (DWORD) 0;
1858 }
1859
1860
1861 /******************************************************************
1862  *    SetIpNetEntry (IPHLPAPI.@)
1863  *
1864  * Modify an existing ARP entry.
1865  *
1866  * PARAMS
1867  *  pArpEntry [In] ARP entry with the new information
1868  *
1869  * RETURNS
1870  *  Success: NO_ERROR
1871  *  Failure: error code from winerror.h
1872  *
1873  * FIXME
1874  *  Stub, returns NO_ERROR.
1875  */
1876 DWORD WINAPI SetIpNetEntry(PMIB_IPNETROW pArpEntry)
1877 {
1878   FIXME("(pArpEntry %p): stub\n", pArpEntry);
1879   /* same as CreateIpNetEntry here, could use SIOCSARP, not sure I want to */
1880   return (DWORD) 0;
1881 }
1882
1883
1884 /******************************************************************
1885  *    SetIpStatistics (IPHLPAPI.@)
1886  *
1887  * Toggle IP forwarding and det the default TTL value.
1888  *
1889  * PARAMS
1890  *  pIpStats [In] IP statistics with the new information
1891  *
1892  * RETURNS
1893  *  Success: NO_ERROR
1894  *  Failure: error code from winerror.h
1895  *
1896  * FIXME
1897  *  Stub, returns NO_ERROR.
1898  */
1899 DWORD WINAPI SetIpStatistics(PMIB_IPSTATS pIpStats)
1900 {
1901   FIXME("(pIpStats %p): stub\n", pIpStats);
1902   return (DWORD) 0;
1903 }
1904
1905
1906 /******************************************************************
1907  *    SetIpTTL (IPHLPAPI.@)
1908  *
1909  * Set the default TTL value.
1910  *
1911  * PARAMS
1912  *  nTTL [In] new TTL value
1913  *
1914  * RETURNS
1915  *  Success: NO_ERROR
1916  *  Failure: error code from winerror.h
1917  *
1918  * FIXME
1919  *  Stub, returns NO_ERROR.
1920  */
1921 DWORD WINAPI SetIpTTL(UINT nTTL)
1922 {
1923   FIXME("(nTTL %d): stub\n", nTTL);
1924   /* could echo nTTL > /proc/net/sys/net/ipv4/ip_default_ttl, not sure I
1925      want to.  Could map EACCESS to ERROR_ACCESS_DENIED, I suppose */
1926   return (DWORD) 0;
1927 }
1928
1929
1930 /******************************************************************
1931  *    SetTcpEntry (IPHLPAPI.@)
1932  *
1933  * Set the state of a TCP connection.
1934  *
1935  * PARAMS
1936  *  pTcpRow [In] specifies connection with new state
1937  *
1938  * RETURNS
1939  *  Success: NO_ERROR
1940  *  Failure: error code from winerror.h
1941  *
1942  * FIXME
1943  *  Stub, returns NO_ERROR.
1944  */
1945 DWORD WINAPI SetTcpEntry(PMIB_TCPROW pTcpRow)
1946 {
1947   FIXME("(pTcpRow %p): stub\n", pTcpRow);
1948   return (DWORD) 0;
1949 }
1950
1951
1952 /******************************************************************
1953  *    UnenableRouter (IPHLPAPI.@)
1954  *
1955  * Decrement the IP-forwarding reference count. Turn off IP-forwarding
1956  * if it reaches zero.
1957  *
1958  * PARAMS
1959  *  pOverlapped     [In/Out] should be the same as in EnableRouter()
1960  *  lpdwEnableCount [Out]    optional, receives reference count
1961  *
1962  * RETURNS
1963  *  Success: NO_ERROR
1964  *  Failure: error code from winerror.h
1965  *
1966  * FIXME
1967  *  Stub, returns ERROR_NOT_SUPPORTED.
1968  */
1969 DWORD WINAPI UnenableRouter(OVERLAPPED * pOverlapped, LPDWORD lpdwEnableCount)
1970 {
1971   FIXME("(pOverlapped %p, lpdwEnableCount %p): stub\n", pOverlapped,
1972    lpdwEnableCount);
1973   /* could echo "0" > /proc/net/sys/net/ipv4/ip_forward, not sure I want to
1974      could map EACCESS to ERROR_ACCESS_DENIED, I suppose
1975    */
1976   return ERROR_NOT_SUPPORTED;
1977 }