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