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