iphlpapi: Remove one IP address per interface restriction.
[wine] / dlls / iphlpapi / ifenum.c
1 /* Copyright (C) 2003,2006 Juan Lang
2  *
3  * This library is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU Lesser General Public
5  * License as published by the Free Software Foundation; either
6  * version 2.1 of the License, or (at your option) any later version.
7  *
8  * This library is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public
14  * License along with this library; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  *
17  * Implementation notes
18  * Interface index fun:
19  * - Windows may rely on an index being cleared in the topmost 8 bits in some
20  *   APIs; see GetFriendlyIfIndex and the mention of "backward compatible"
21  *   indexes.  It isn't clear which APIs might fail with non-backward-compatible
22  *   indexes, but I'll keep them bits clear just in case.
23  * - Even though if_nametoindex and if_indextoname seem to be pretty portable,
24  *   Linux, at any rate, uses the same interface index for all virtual
25  *   interfaces of a real interface as well as for the real interface itself.
26  *   If I used the Linux index as my index, this would break my statement that
27  *   an index is a key, and that an interface has 0 or 1 IP addresses.
28  *   If that behavior were consistent across UNIXen (I don't know), it could
29  *   help me implement multiple IP addresses more in the Windows way.
30  * I used to assert I could not use UNIX interface indexes as my iphlpapi
31  * indexes due to restrictions in netapi32 and wsock32, but I have removed
32  * those restrictions, so using if_nametoindex and if_indextoname rather
33  * than my current mess would probably be better.
34  * FIXME:
35  * - I don't support IPv6 addresses here, since SIOCGIFCONF can't return them
36  *
37  * There are three implemented methods for determining the MAC address of an
38  * interface:
39  * - a specific IOCTL (Linux)
40  * - looking in the ARP cache (at least Solaris)
41  * - using the sysctl interface (FreeBSD and MacOSX)
42  * Solaris and some others have SIOCGENADDR, but I haven't gotten that to work
43  * on the Solaris boxes at SourceForge's compile farm, whereas SIOCGARP does.
44  */
45
46 #include "config.h"
47
48 #include <stdarg.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52
53 #ifdef HAVE_UNISTD_H
54 #include <unistd.h>
55 #endif
56
57 #include <sys/types.h>
58
59 #ifdef HAVE_SYS_SOCKET_H
60 #include <sys/socket.h>
61 #endif
62
63 #ifdef HAVE_NETINET_IN_H
64 #include <netinet/in.h>
65 #endif
66
67 #ifdef HAVE_ARPA_INET_H
68 #include <arpa/inet.h>
69 #endif
70
71 #ifdef HAVE_NET_IF_H
72 #include <net/if.h>
73 #endif
74
75 #ifdef HAVE_NET_IF_ARP_H
76 #include <net/if_arp.h>
77 #endif
78
79 #ifdef HAVE_NET_ROUTE_H
80 #include <net/route.h>
81 #endif
82
83 #ifdef HAVE_SYS_IOCTL_H
84 #include <sys/ioctl.h>
85 #endif
86
87 #ifdef HAVE_SYS_SYSCTL_H
88 #include <sys/sysctl.h>
89 #endif
90
91 #ifdef HAVE_SYS_SOCKIO_H
92 #include <sys/sockio.h>
93 #endif
94
95 #ifdef HAVE_NET_IF_DL_H
96 #include <net/if_dl.h>
97 #endif
98
99 #ifdef HAVE_NET_IF_TYPES_H
100 #include <net/if_types.h>
101 #endif
102
103 #include "ifenum.h"
104
105 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
106 #define ifreq_len(ifr) \
107  max(sizeof(struct ifreq), sizeof((ifr)->ifr_name)+(ifr)->ifr_addr.sa_len)
108 #else
109 #define ifreq_len(ifr) sizeof(struct ifreq)
110 #endif
111
112 #ifndef ETH_ALEN
113 #define ETH_ALEN 6
114 #endif
115
116 #ifndef INADDR_NONE
117 #define INADDR_NONE (~0U)
118 #endif
119
120 #define INITIAL_INTERFACES_ASSUMED 4
121
122 #define INDEX_IS_LOOPBACK 0x00800000
123
124 /* Type declarations */
125
126 typedef struct _InterfaceNameMapEntry {
127   char  name[IFNAMSIZ];
128   BOOL  inUse;
129   BOOL  usedLastPass;
130 } InterfaceNameMapEntry;
131
132 typedef struct _InterfaceNameMap {
133   DWORD numInterfaces;
134   DWORD nextAvailable;
135   DWORD numAllocated;
136   InterfaceNameMapEntry table[1];
137 } InterfaceNameMap;
138
139 /* Global variables */
140
141 static CRITICAL_SECTION mapCS;
142 static InterfaceNameMap *gNonLoopbackInterfaceMap = NULL;
143 static InterfaceNameMap *gLoopbackInterfaceMap = NULL;
144
145 /* Functions */
146
147 void interfaceMapInit(void)
148 {
149     InitializeCriticalSection(&mapCS);
150 }
151
152 void interfaceMapFree(void)
153 {
154     DeleteCriticalSection(&mapCS);
155     HeapFree(GetProcessHeap(), 0, gNonLoopbackInterfaceMap);
156     HeapFree(GetProcessHeap(), 0, gLoopbackInterfaceMap);
157 }
158
159 /* Sizes the passed-in map to have enough space for numInterfaces interfaces.
160  * If map is NULL, allocates a new map.  If it is not, may reallocate the
161  * existing map and return a map of increased size.  Returns the allocated map,
162  * or NULL if it could not allocate a map of the requested size.
163  */
164 static InterfaceNameMap *sizeMap(InterfaceNameMap *map, DWORD numInterfaces)
165 {
166   if (!map) {
167     numInterfaces = max(numInterfaces, INITIAL_INTERFACES_ASSUMED);
168     map = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
169      sizeof(InterfaceNameMap) +
170      (numInterfaces - 1) * sizeof(InterfaceNameMapEntry));
171     if (map)
172       map->numAllocated = numInterfaces;
173   }
174   else {
175     if (map->numAllocated < numInterfaces) {
176       map = HeapReAlloc(GetProcessHeap(), 0, map,
177        sizeof(InterfaceNameMap) +
178        (numInterfaces - 1) * sizeof(InterfaceNameMapEntry));
179       if (map)
180         memset(&map->table[map->numAllocated], 0,
181          (numInterfaces - map->numAllocated) * sizeof(InterfaceNameMapEntry));
182     }
183   }
184   return map;
185 }
186
187 static int isLoopbackInterface(int fd, const char *name)
188 {
189   int ret = 0;
190
191   if (name) {
192     struct ifreq ifr;
193
194     lstrcpynA(ifr.ifr_name, name, IFNAMSIZ);
195     if (ioctl(fd, SIOCGIFFLAGS, &ifr) == 0)
196       ret = ifr.ifr_flags & IFF_LOOPBACK;
197   }
198   return ret;
199 }
200
201 static void countInterfaces(int fd, caddr_t buf, size_t len)
202 {
203   caddr_t ifPtr = buf;
204   DWORD numNonLoopbackInterfaces = 0, numLoopbackInterfaces = 0;
205
206   while (ifPtr && ifPtr < buf + len) {
207     struct ifreq *ifr = (struct ifreq *)ifPtr;
208
209     if (isLoopbackInterface(fd, ifr->ifr_name))
210       numLoopbackInterfaces++;
211     else
212       numNonLoopbackInterfaces++;
213     ifPtr += ifreq_len(ifr);
214   }
215   gNonLoopbackInterfaceMap = sizeMap(gNonLoopbackInterfaceMap,
216    numNonLoopbackInterfaces);
217   gLoopbackInterfaceMap = sizeMap(gLoopbackInterfaceMap,
218    numLoopbackInterfaces);
219 }
220
221 /* Stores the name in the given map, and increments the map's numInterfaces
222  * member if stored successfully.  Will store in the same slot as previously if
223  * usedLastPass is set, otherwise will store in a new slot.
224  * Assumes map and name are not NULL, and the usedLastPass flag is set
225  * correctly for each entry in the map, and that map->numInterfaces <
226  * map->numAllocated.
227  * FIXME: this is kind of expensive, doing a linear scan of the map with a
228  * string comparison of each entry to find the old slot.
229  */
230 static void storeInterfaceInMap(InterfaceNameMap *map, const char *name)
231 {
232   if (map && name) {
233     DWORD ndx;
234     BOOL stored = FALSE;
235
236     /* look for previous slot, mark in use if so */
237     for (ndx = 0; !stored && ndx < map->nextAvailable; ndx++) {
238       if (map->table[ndx].usedLastPass && !strncmp(map->table[ndx].name, name,
239        sizeof(map->table[ndx].name))) {
240         map->table[ndx].inUse = TRUE;
241         stored = TRUE;
242       }
243     }
244     /* look for new slot */
245     for (ndx = 0; !stored && ndx < map->numAllocated; ndx++) {
246       if (!map->table[ndx].inUse) {
247         lstrcpynA(map->table[ndx].name, name, IFNAMSIZ);
248         map->table[ndx].inUse = TRUE;
249         stored = TRUE;
250         if (ndx >= map->nextAvailable)
251           map->nextAvailable = ndx + 1;
252       }
253     }
254     if (stored)
255       map->numInterfaces++;
256   }
257 }
258
259 /* Sets all used entries' usedLastPass flag to their inUse flag, clears
260  * their inUse flag, and clears their numInterfaces member.
261  */
262 static void markOldInterfaces(InterfaceNameMap *map)
263 {
264   if (map) {
265     DWORD ndx;
266
267     map->numInterfaces = 0;
268     for (ndx = 0; ndx < map->nextAvailable; ndx++) {
269       map->table[ndx].usedLastPass = map->table[ndx].inUse;
270       map->table[ndx].inUse = FALSE;
271     }
272   }
273 }
274
275 static void classifyInterfaces(int fd, caddr_t buf, size_t len)
276 {
277   caddr_t ifPtr = buf;
278
279   markOldInterfaces(gNonLoopbackInterfaceMap);
280   markOldInterfaces(gLoopbackInterfaceMap);
281   while (ifPtr && ifPtr < buf + len) {
282     struct ifreq *ifr = (struct ifreq *)ifPtr;
283
284     if (ifr->ifr_addr.sa_family == AF_INET) {
285       if (isLoopbackInterface(fd, ifr->ifr_name))
286         storeInterfaceInMap(gLoopbackInterfaceMap, ifr->ifr_name);
287       else
288         storeInterfaceInMap(gNonLoopbackInterfaceMap, ifr->ifr_name);
289     }
290     ifPtr += ifreq_len(ifr);
291   }
292 }
293
294 static void enumerateInterfaces(void)
295 {
296   int fd;
297
298   fd = socket(PF_INET, SOCK_DGRAM, 0);
299   if (fd != -1) {
300     int ret, guessedNumInterfaces;
301     struct ifconf ifc;
302
303     /* try to avoid silly heap action by starting with the right size buffer */
304     guessedNumInterfaces = 0;
305     if (gNonLoopbackInterfaceMap)
306       guessedNumInterfaces += gNonLoopbackInterfaceMap->numInterfaces;
307     if (gLoopbackInterfaceMap)
308       guessedNumInterfaces += gLoopbackInterfaceMap->numInterfaces;
309
310     ret = 0;
311     memset(&ifc, 0, sizeof(ifc));
312     /* there is no way to know the interface count beforehand,
313        so we need to loop again and again upping our max each time
314        until returned < max */
315     do {
316       if (guessedNumInterfaces == 0)
317         guessedNumInterfaces = INITIAL_INTERFACES_ASSUMED;
318       else
319         guessedNumInterfaces *= 2;
320       HeapFree(GetProcessHeap(), 0, ifc.ifc_buf);
321       ifc.ifc_len = sizeof(struct ifreq) * guessedNumInterfaces;
322       ifc.ifc_buf = HeapAlloc(GetProcessHeap(), 0, ifc.ifc_len);
323       ret = ioctl(fd, SIOCGIFCONF, &ifc);
324     } while (ret == 0 &&
325      ifc.ifc_len == (sizeof(struct ifreq) * guessedNumInterfaces));
326
327     if (ret == 0) {
328       EnterCriticalSection(&mapCS);
329       countInterfaces(fd, ifc.ifc_buf, ifc.ifc_len);
330       classifyInterfaces(fd, ifc.ifc_buf, ifc.ifc_len);
331       LeaveCriticalSection(&mapCS);
332     }
333
334     HeapFree(GetProcessHeap(), 0, ifc.ifc_buf);
335     close(fd);
336   }
337 }
338
339 DWORD getNumNonLoopbackInterfaces(void)
340 {
341   enumerateInterfaces();
342   return gNonLoopbackInterfaceMap ? gNonLoopbackInterfaceMap->numInterfaces : 0;
343 }
344
345 DWORD getNumInterfaces(void)
346 {
347   DWORD ret = getNumNonLoopbackInterfaces();
348
349   ret += gLoopbackInterfaceMap ? gLoopbackInterfaceMap->numInterfaces : 0;
350   return ret;
351 }
352
353 const char *getInterfaceNameByIndex(DWORD index)
354 {
355   DWORD realIndex;
356   InterfaceNameMap *map;
357   const char *ret = NULL;
358
359   EnterCriticalSection(&mapCS);
360   if (index & INDEX_IS_LOOPBACK) {
361     realIndex = index ^ INDEX_IS_LOOPBACK;
362     map = gLoopbackInterfaceMap;
363   }
364   else {
365     realIndex = index;
366     map = gNonLoopbackInterfaceMap;
367   }
368   if (map && realIndex < map->nextAvailable)
369     ret = map->table[realIndex].name;
370   LeaveCriticalSection(&mapCS);
371   return ret;
372 }
373
374 DWORD getInterfaceIndexByName(const char *name, PDWORD index)
375 {
376   DWORD ndx, ret;
377   BOOL found = FALSE;
378
379   if (!name)
380     return ERROR_INVALID_PARAMETER;
381   if (!index)
382     return ERROR_INVALID_PARAMETER;
383
384   EnterCriticalSection(&mapCS);
385   for (ndx = 0; !found && gNonLoopbackInterfaceMap &&
386    ndx < gNonLoopbackInterfaceMap->nextAvailable; ndx++)
387     if (!strncmp(gNonLoopbackInterfaceMap->table[ndx].name, name, IFNAMSIZ)) {
388       found = TRUE;
389       *index = ndx;
390     }
391   for (ndx = 0; !found && gLoopbackInterfaceMap &&
392    ndx < gLoopbackInterfaceMap->nextAvailable; ndx++)
393     if (!strncmp(gLoopbackInterfaceMap->table[ndx].name, name, IFNAMSIZ)) {
394       found = TRUE;
395       *index = ndx | INDEX_IS_LOOPBACK;
396     }
397   LeaveCriticalSection(&mapCS);
398   if (found)
399     ret = NO_ERROR;
400   else
401     ret = ERROR_INVALID_DATA;
402   return ret;
403 }
404
405 static void addMapEntriesToIndexTable(InterfaceIndexTable *table,
406  const InterfaceNameMap *map)
407 {
408   if (table && map) {
409     DWORD ndx;
410
411     for (ndx = 0; ndx < map->nextAvailable &&
412      table->numIndexes < table->numAllocated; ndx++)
413       if (map->table[ndx].inUse) {
414         DWORD externalNdx = ndx;
415
416         if (map == gLoopbackInterfaceMap)
417           externalNdx |= INDEX_IS_LOOPBACK;
418         table->indexes[table->numIndexes++] = externalNdx;
419       }
420   }
421 }
422
423 InterfaceIndexTable *getInterfaceIndexTable(void)
424 {
425   DWORD numInterfaces;
426   InterfaceIndexTable *ret;
427  
428   EnterCriticalSection(&mapCS);
429   numInterfaces = getNumInterfaces();
430   ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
431    sizeof(InterfaceIndexTable) + (numInterfaces - 1) * sizeof(DWORD));
432   if (ret) {
433     ret->numAllocated = numInterfaces;
434     addMapEntriesToIndexTable(ret, gNonLoopbackInterfaceMap);
435     addMapEntriesToIndexTable(ret, gLoopbackInterfaceMap);
436   }
437   LeaveCriticalSection(&mapCS);
438   return ret;
439 }
440
441 InterfaceIndexTable *getNonLoopbackInterfaceIndexTable(void)
442 {
443   DWORD numInterfaces;
444   InterfaceIndexTable *ret;
445
446   EnterCriticalSection(&mapCS);
447   numInterfaces = getNumNonLoopbackInterfaces();
448   ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
449    sizeof(InterfaceIndexTable) + (numInterfaces - 1) * sizeof(DWORD));
450   if (ret) {
451     ret->numAllocated = numInterfaces;
452     addMapEntriesToIndexTable(ret, gNonLoopbackInterfaceMap);
453   }
454   LeaveCriticalSection(&mapCS);
455   return ret;
456 }
457
458 static DWORD getInterfaceBCastAddrByName(const char *name)
459 {
460   DWORD ret = INADDR_ANY;
461
462   if (name) {
463     int fd = socket(PF_INET, SOCK_DGRAM, 0);
464
465     if (fd != -1) {
466       struct ifreq ifr;
467
468       lstrcpynA(ifr.ifr_name, name, IFNAMSIZ);
469       if (ioctl(fd, SIOCGIFBRDADDR, &ifr) == 0)
470         memcpy(&ret, ifr.ifr_addr.sa_data + 2, sizeof(DWORD));
471       close(fd);
472     }
473   }
474   return ret;
475 }
476
477 static DWORD getInterfaceMaskByName(const char *name)
478 {
479   DWORD ret = INADDR_NONE;
480
481   if (name) {
482     int fd = socket(PF_INET, SOCK_DGRAM, 0);
483
484     if (fd != -1) {
485       struct ifreq ifr;
486
487       lstrcpynA(ifr.ifr_name, name, IFNAMSIZ);
488       if (ioctl(fd, SIOCGIFNETMASK, &ifr) == 0)
489         memcpy(&ret, ifr.ifr_addr.sa_data + 2, sizeof(DWORD));
490       close(fd);
491     }
492   }
493   return ret;
494 }
495
496 #if defined (SIOCGIFHWADDR)
497 DWORD getInterfacePhysicalByName(const char *name, PDWORD len, PBYTE addr,
498  PDWORD type)
499 {
500   DWORD ret;
501   int fd;
502
503   if (!name || !len || !addr || !type)
504     return ERROR_INVALID_PARAMETER;
505
506   fd = socket(PF_INET, SOCK_DGRAM, 0);
507   if (fd != -1) {
508     struct ifreq ifr;
509
510     memset(&ifr, 0, sizeof(struct ifreq));
511     lstrcpynA(ifr.ifr_name, name, IFNAMSIZ);
512     if ((ioctl(fd, SIOCGIFHWADDR, &ifr)))
513       ret = ERROR_INVALID_DATA;
514     else {
515       unsigned int addrLen;
516
517       switch (ifr.ifr_hwaddr.sa_family)
518       {
519 #ifdef ARPHRD_LOOPBACK
520         case ARPHRD_LOOPBACK:
521           addrLen = 0;
522           *type = MIB_IF_TYPE_LOOPBACK;
523           break;
524 #endif
525 #ifdef ARPHRD_ETHER
526         case ARPHRD_ETHER:
527           addrLen = ETH_ALEN;
528           *type = MIB_IF_TYPE_ETHERNET;
529           break;
530 #endif
531 #ifdef ARPHRD_FDDI
532         case ARPHRD_FDDI:
533           addrLen = ETH_ALEN;
534           *type = MIB_IF_TYPE_FDDI;
535           break;
536 #endif
537 #ifdef ARPHRD_IEEE802
538         case ARPHRD_IEEE802: /* 802.2 Ethernet && Token Ring, guess TR? */
539           addrLen = ETH_ALEN;
540           *type = MIB_IF_TYPE_TOKENRING;
541           break;
542 #endif
543 #ifdef ARPHRD_IEEE802_TR
544         case ARPHRD_IEEE802_TR: /* also Token Ring? */
545           addrLen = ETH_ALEN;
546           *type = MIB_IF_TYPE_TOKENRING;
547           break;
548 #endif
549 #ifdef ARPHRD_SLIP
550         case ARPHRD_SLIP:
551           addrLen = 0;
552           *type = MIB_IF_TYPE_SLIP;
553           break;
554 #endif
555 #ifdef ARPHRD_PPP
556         case ARPHRD_PPP:
557           addrLen = 0;
558           *type = MIB_IF_TYPE_PPP;
559           break;
560 #endif
561         default:
562           addrLen = min(MAX_INTERFACE_PHYSADDR, sizeof(ifr.ifr_hwaddr.sa_data));
563           *type = MIB_IF_TYPE_OTHER;
564       }
565       if (addrLen > *len) {
566         ret = ERROR_INSUFFICIENT_BUFFER;
567         *len = addrLen;
568       }
569       else {
570         if (addrLen > 0)
571           memcpy(addr, ifr.ifr_hwaddr.sa_data, addrLen);
572         /* zero out remaining bytes for broken implementations */
573         memset(addr + addrLen, 0, *len - addrLen);
574         *len = addrLen;
575         ret = NO_ERROR;
576       }
577     }
578     close(fd);
579   }
580   else
581     ret = ERROR_NO_MORE_FILES;
582   return ret;
583 }
584 #elif defined (SIOCGARP)
585 DWORD getInterfacePhysicalByName(const char *name, PDWORD len, PBYTE addr,
586  PDWORD type)
587 {
588   DWORD ret;
589   int fd;
590
591   if (!name || !len || !addr || !type)
592     return ERROR_INVALID_PARAMETER;
593
594   fd = socket(PF_INET, SOCK_DGRAM, 0);
595   if (fd != -1) {
596     if (isLoopbackInterface(fd, name)) {
597       *type = MIB_IF_TYPE_LOOPBACK;
598       memset(addr, 0, *len);
599       *len = 0;
600       ret=NOERROR;
601     }
602     else {
603       struct arpreq arp;
604       struct sockaddr_in *saddr;
605       struct ifreq ifr;
606
607       /* get IP addr */
608       lstrcpynA(ifr.ifr_name, name, IFNAMSIZ);
609       ioctl(fd, SIOCGIFADDR, &ifr);
610       memset(&arp, 0, sizeof(struct arpreq));
611       arp.arp_pa.sa_family = AF_INET;
612       saddr = (struct sockaddr_in *)&arp; /* proto addr is first member */
613       saddr->sin_family = AF_INET;
614       memcpy(&saddr->sin_addr.s_addr, ifr.ifr_addr.sa_data + 2, sizeof(DWORD));
615       if ((ioctl(fd, SIOCGARP, &arp)))
616         ret = ERROR_INVALID_DATA;
617       else {
618         /* FIXME:  heh:  who said it was ethernet? */
619         int addrLen = ETH_ALEN;
620
621         if (addrLen > *len) {
622           ret = ERROR_INSUFFICIENT_BUFFER;
623           *len = addrLen;
624         }
625         else {
626           if (addrLen > 0)
627             memcpy(addr, &arp.arp_ha.sa_data[0], addrLen);
628           /* zero out remaining bytes for broken implementations */
629           memset(addr + addrLen, 0, *len - addrLen);
630           *len = addrLen;
631           *type = MIB_IF_TYPE_ETHERNET;
632           ret = NO_ERROR;
633         }
634       }
635     }
636     close(fd);
637   }
638     else
639       ret = ERROR_NO_MORE_FILES;
640
641   return ret;
642 }
643 #elif defined (HAVE_SYS_SYSCTL_H) && defined (HAVE_NET_IF_DL_H)
644 DWORD getInterfacePhysicalByName(const char *name, PDWORD len, PBYTE addr,
645  PDWORD type)
646 {
647   DWORD ret;
648   struct if_msghdr *ifm;
649   struct sockaddr_dl *sdl;
650   u_char *p, *buf;
651   size_t mibLen;
652   int mib[] = { CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0 };
653   int addrLen;
654   BOOL found = FALSE;
655
656   if (!name || !len || !addr || !type)
657     return ERROR_INVALID_PARAMETER;
658
659   if (sysctl(mib, 6, NULL, &mibLen, NULL, 0) < 0)
660     return ERROR_NO_MORE_FILES;
661
662   buf = HeapAlloc(GetProcessHeap(), 0, mibLen);
663   if (!buf)
664     return ERROR_NOT_ENOUGH_MEMORY;
665
666   if (sysctl(mib, 6, buf, &mibLen, NULL, 0) < 0) {
667     HeapFree(GetProcessHeap(), 0, buf);
668     return ERROR_NO_MORE_FILES;
669   }
670
671   ret = ERROR_INVALID_DATA;
672   for (p = buf; !found && p < buf + mibLen; p += ifm->ifm_msglen) {
673     ifm = (struct if_msghdr *)p;
674     sdl = (struct sockaddr_dl *)(ifm + 1);
675
676     if (ifm->ifm_type != RTM_IFINFO || (ifm->ifm_addrs & RTA_IFP) == 0)
677       continue;
678
679     if (sdl->sdl_family != AF_LINK || sdl->sdl_nlen == 0 ||
680      memcmp(sdl->sdl_data, name, max(sdl->sdl_nlen, strlen(name))) != 0)
681       continue;
682
683     found = TRUE;
684     addrLen = min(MAX_INTERFACE_PHYSADDR, sdl->sdl_alen);
685     if (addrLen > *len) {
686       ret = ERROR_INSUFFICIENT_BUFFER;
687       *len = addrLen;
688     }
689     else {
690       if (addrLen > 0)
691         memcpy(addr, LLADDR(sdl), addrLen);
692       /* zero out remaining bytes for broken implementations */
693       memset(addr + addrLen, 0, *len - addrLen);
694       *len = addrLen;
695 #if defined(HAVE_NET_IF_TYPES_H)
696       switch (sdl->sdl_type)
697       {
698         case IFT_ETHER:
699           *type = MIB_IF_TYPE_ETHERNET;
700           break;
701         case IFT_FDDI:
702           *type = MIB_IF_TYPE_FDDI;
703           break;
704         case IFT_ISO88024: /* Token Bus */
705           *type = MIB_IF_TYPE_TOKENRING;
706           break;
707         case IFT_ISO88025: /* Token Ring */
708           *type = MIB_IF_TYPE_TOKENRING;
709           break;
710         case IFT_PPP:
711           *type = MIB_IF_TYPE_PPP;
712           break;
713         case IFT_SLIP:
714           *type = MIB_IF_TYPE_SLIP;
715           break;
716         case IFT_LOOP:
717           *type = MIB_IF_TYPE_LOOPBACK;
718           break;
719         default:
720           *type = MIB_IF_TYPE_OTHER;
721       }
722 #else
723       /* default if we don't know */
724       *type = MIB_IF_TYPE_ETHERNET;
725 #endif
726       ret = NO_ERROR;
727     }
728   }
729   HeapFree(GetProcessHeap(), 0, buf);
730   return ret;
731 }
732 #endif
733
734 DWORD getInterfacePhysicalByIndex(DWORD index, PDWORD len, PBYTE addr,
735  PDWORD type)
736 {
737   const char *name = getInterfaceNameByIndex(index);
738
739   if (name)
740     return getInterfacePhysicalByName(name, len, addr, type);
741   else
742     return ERROR_INVALID_DATA;
743 }
744
745 static DWORD getInterfaceMtuByName(const char *name, PDWORD mtu)
746 {
747   DWORD ret;
748   int fd;
749
750   if (!name)
751     return ERROR_INVALID_PARAMETER;
752   if (!mtu)
753     return ERROR_INVALID_PARAMETER;
754
755   fd = socket(PF_INET, SOCK_DGRAM, 0);
756   if (fd != -1) {
757     struct ifreq ifr;
758
759     lstrcpynA(ifr.ifr_name, name, IFNAMSIZ);
760     if ((ioctl(fd, SIOCGIFMTU, &ifr)))
761       ret = ERROR_INVALID_DATA;
762     else {
763 #ifndef __sun
764       *mtu = ifr.ifr_mtu;
765 #else
766       *mtu = ifr.ifr_metric;
767 #endif
768       ret = NO_ERROR;
769     }
770     close(fd);
771   }
772   else
773     ret = ERROR_NO_MORE_FILES;
774   return ret;
775 }
776
777 static DWORD getInterfaceStatusByName(const char *name, PDWORD status)
778 {
779   DWORD ret;
780   int fd;
781
782   if (!name)
783     return ERROR_INVALID_PARAMETER;
784   if (!status)
785     return ERROR_INVALID_PARAMETER;
786
787   fd = socket(PF_INET, SOCK_DGRAM, 0);
788   if (fd != -1) {
789     struct ifreq ifr;
790
791     lstrcpynA(ifr.ifr_name, name, IFNAMSIZ);
792     if ((ioctl(fd, SIOCGIFFLAGS, &ifr)))
793       ret = ERROR_INVALID_DATA;
794     else {
795       if (ifr.ifr_flags & IFF_UP)
796         *status = MIB_IF_OPER_STATUS_OPERATIONAL;
797       else
798         *status = MIB_IF_OPER_STATUS_NON_OPERATIONAL;
799       ret = NO_ERROR;
800     }
801     close(fd);
802   }
803   else
804     ret = ERROR_NO_MORE_FILES;
805   return ret;
806 }
807
808 DWORD getInterfaceEntryByName(const char *name, PMIB_IFROW entry)
809 {
810   BYTE addr[MAX_INTERFACE_PHYSADDR];
811   DWORD ret, len = sizeof(addr), type;
812
813   if (!name)
814     return ERROR_INVALID_PARAMETER;
815   if (!entry)
816     return ERROR_INVALID_PARAMETER;
817
818   if (getInterfacePhysicalByName(name, &len, addr, &type) == NO_ERROR) {
819     WCHAR *assigner;
820     const char *walker;
821
822     memset(entry, 0, sizeof(MIB_IFROW));
823     for (assigner = entry->wszName, walker = name; *walker; 
824      walker++, assigner++)
825       *assigner = *walker;
826     *assigner = 0;
827     getInterfaceIndexByName(name, &entry->dwIndex);
828     entry->dwPhysAddrLen = len;
829     memcpy(entry->bPhysAddr, addr, len);
830     memset(entry->bPhysAddr + len, 0, sizeof(entry->bPhysAddr) - len);
831     entry->dwType = type;
832     /* FIXME: how to calculate real speed? */
833     getInterfaceMtuByName(name, &entry->dwMtu);
834     /* lie, there's no "administratively down" here */
835     entry->dwAdminStatus = MIB_IF_ADMIN_STATUS_UP;
836     getInterfaceStatusByName(name, &entry->dwOperStatus);
837     /* punt on dwLastChange? */
838     entry->dwDescrLen = min(strlen(name), MAX_INTERFACE_DESCRIPTION - 1);
839     memcpy(entry->bDescr, name, entry->dwDescrLen);
840     entry->bDescr[entry->dwDescrLen] = '\0';
841     entry->dwDescrLen++;
842     ret = NO_ERROR;
843   }
844   else
845     ret = ERROR_INVALID_DATA;
846   return ret;
847 }
848
849 DWORD getInterfaceEntryByIndex(DWORD index, PMIB_IFROW entry)
850 {
851   const char *name = getInterfaceNameByIndex(index);
852
853   if (name)
854     return getInterfaceEntryByName(name, entry);
855   else
856     return ERROR_INVALID_DATA;
857 }
858
859 /* Enumerates the IP addresses in the system using SIOCGIFCONF, returning
860  * the count to you in *pcAddresses.  It also returns to you the struct ifconf
861  * used by the call to ioctl, so that you may process the addresses further.
862  * Free ifc->ifc_buf using HeapFree.
863  * Returns NO_ERROR on success, something else on failure.
864  */
865 static DWORD enumIPAddresses(PDWORD pcAddresses, struct ifconf *ifc)
866 {
867   DWORD ret;
868   int fd;
869
870   fd = socket(PF_INET, SOCK_DGRAM, 0);
871   if (fd != -1) {
872     int ioctlRet = 0;
873     DWORD guessedNumAddresses = 0, numAddresses = 0;
874     caddr_t ifPtr;
875
876     ret = NO_ERROR;
877     ifc->ifc_len = 0;
878     ifc->ifc_buf = NULL;
879     /* there is no way to know the interface count beforehand,
880        so we need to loop again and again upping our max each time
881        until returned < max */
882     do {
883       HeapFree(GetProcessHeap(), 0, ifc->ifc_buf);
884       if (guessedNumAddresses == 0)
885         guessedNumAddresses = INITIAL_INTERFACES_ASSUMED;
886       else
887         guessedNumAddresses *= 2;
888       ifc->ifc_len = sizeof(struct ifreq) * guessedNumAddresses;
889       ifc->ifc_buf = HeapAlloc(GetProcessHeap(), 0, ifc->ifc_len);
890       ioctlRet = ioctl(fd, SIOCGIFCONF, ifc);
891     } while (ioctlRet == 0 &&
892      ifc->ifc_len == (sizeof(struct ifreq) * guessedNumAddresses));
893
894     if (ioctlRet == 0) {
895       ifPtr = ifc->ifc_buf;
896       while (ifPtr && ifPtr < ifc->ifc_buf + ifc->ifc_len) {
897         numAddresses++;
898         ifPtr += ifreq_len((struct ifreq *)ifPtr);
899       }
900     }
901     else
902       ret = ERROR_INVALID_PARAMETER; /* FIXME: map from errno to Win32 */
903     if (!ret)
904       *pcAddresses = numAddresses;
905     else
906     {
907       HeapFree(GetProcessHeap(), 0, ifc->ifc_buf);
908       ifc->ifc_buf = NULL;
909     }
910     close(fd);
911   }
912   else
913     ret = ERROR_NO_SYSTEM_RESOURCES;
914   return ret;
915 }
916
917 DWORD getNumIPAddresses(void)
918 {
919   DWORD numAddresses = 0;
920   struct ifconf ifc;
921
922   if (!enumIPAddresses(&numAddresses, &ifc))
923     HeapFree(GetProcessHeap(), 0, ifc.ifc_buf);
924   return numAddresses;
925 }
926
927 DWORD getIPAddrTable(PMIB_IPADDRTABLE *ppIpAddrTable, HANDLE heap, DWORD flags)
928 {
929   DWORD ret;
930
931   if (!ppIpAddrTable)
932     ret = ERROR_INVALID_PARAMETER;
933   else
934   {
935     DWORD numAddresses = 0;
936     struct ifconf ifc;
937
938     ret = enumIPAddresses(&numAddresses, &ifc);
939     if (!ret)
940     {
941       *ppIpAddrTable = HeapAlloc(heap, flags, sizeof(MIB_IPADDRTABLE) +
942        (numAddresses - 1) * sizeof(MIB_IPADDRROW));
943       if (*ppIpAddrTable) {
944         DWORD i = 0, bcast;
945         caddr_t ifPtr;
946
947         ret = NO_ERROR;
948         (*ppIpAddrTable)->dwNumEntries = numAddresses;
949         ifPtr = ifc.ifc_buf;
950         while (!ret && ifPtr && ifPtr < ifc.ifc_buf + ifc.ifc_len) {
951           struct ifreq *ifr = (struct ifreq *)ifPtr;
952
953           ret = getInterfaceIndexByName(ifr->ifr_name,
954            &(*ppIpAddrTable)->table[i].dwIndex);
955           memcpy(&(*ppIpAddrTable)->table[i].dwAddr, ifr->ifr_addr.sa_data + 2,
956            sizeof(DWORD));
957           (*ppIpAddrTable)->table[i].dwMask =
958            getInterfaceMaskByName(ifr->ifr_name);
959           /* the dwBCastAddr member isn't the broadcast address, it indicates
960            * whether the interface uses the 1's broadcast address (1) or the
961            * 0's broadcast address (0).
962            */
963           bcast = getInterfaceBCastAddrByName(ifr->ifr_name);
964           (*ppIpAddrTable)->table[i].dwBCastAddr =
965            (bcast & (*ppIpAddrTable)->table[i].dwMask) ? 1 : 0;
966           /* FIXME: hardcoded reasm size, not sure where to get it */
967           (*ppIpAddrTable)->table[i].dwReasmSize = 65535;
968
969           (*ppIpAddrTable)->table[i].unused1 = 0;
970           (*ppIpAddrTable)->table[i].wType = 0;
971           ifPtr += ifreq_len(ifr);
972           i++;
973         }
974       }
975       else
976         ret = ERROR_OUTOFMEMORY;
977       HeapFree(GetProcessHeap(), 0, ifc.ifc_buf);
978     }
979   }
980   return ret;
981 }
982
983 char *toIPAddressString(unsigned int addr, char string[16])
984 {
985   if (string) {
986     struct in_addr iAddr;
987
988     iAddr.s_addr = addr;
989     /* extra-anal, just to make auditors happy */
990     lstrcpynA(string, inet_ntoa(iAddr), 16);
991   }
992   return string;
993 }