Avoid excessive heap memory reallocation when generating EMF
[wine] / dlls / iphlpapi / ifenum.c
1 /* Copyright (C) 2003 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  *   But here's a problem with doing so:
31  * - Netbios() has a concept of an LAN adapter number (LANA), which is an 8-bit
32  *   number in the range 0-254, inclusive.  The MSDN pages for Netbios() says
33  *   the original Netbios() spec allowed only 0 or 1 to be used, though "new"
34  *   applications should enumerate available adapters rather than assuming 0
35  *   is the default adapter.
36  *   I'm concerned that some old application might depend on being able to get
37  *   "the" MAC address of a machine by opening LANA 0 and getting its MAC
38  *   address.  This also implies LANA 0 should correspond to a non-loopback
39  *   interface.
40  *   On Linux, the if_nametoindex index is 1-based, and "lo" typically has
41  *   index 1.
42  *   I could make netapi32 do its own LANA map, independent of my index
43  *   assignment, but it seems simpler just to assign 0-based indexes and put
44  *   non-loopback adapters first, so the first 255 indexes (!) on a system will
45  *   automatically map to LANA numbers without difficulty.
46  * - One more argument for doing it this way, if you don't buy the Netbios()
47  *   argument: WsControl() (in wsock32) uses the same index to refer to an IP
48  *   address and an interface.  If I assigned multiple IP addresses to an
49  *   interface, wsock32 would have to maintain a table of IP addresses with its
50  *   own indexing scheme.  No thanks.
51  *
52  * There are three implemened methods for determining the MAC address of an
53  * interface:
54  * - a specific IOCTL (Linux)
55  * - looking in the ARP cache (at least Solaris)
56  * - using the sysctl interface (FreeBSD and MacOSX)
57  * Solaris and some others have SIOCGENADDR, but I haven't gotten that to work
58  * on the Solaris boxes at SourceForge's compile farm, whereas SIOCGARP does.
59  */
60
61 #include "config.h"
62
63 #include <stdarg.h>
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <string.h>
67
68 #ifdef HAVE_UNISTD_H
69 #include <unistd.h>
70 #endif
71
72 #include <sys/types.h>
73
74 #ifdef HAVE_SYS_SOCKET_H
75 #include <sys/socket.h>
76 #endif
77
78 #ifdef HAVE_NETINET_IN_H
79 #include <netinet/in.h>
80 #endif
81
82 #ifdef HAVE_ARPA_INET_H
83 #include <arpa/inet.h>
84 #endif
85
86 #ifdef HAVE_NET_IF_H
87 #include <net/if.h>
88 #endif
89
90 #ifdef HAVE_NET_IF_ARP_H
91 #include <net/if_arp.h>
92 #endif
93
94 #ifdef HAVE_NET_ROUTE_H
95 #include <net/route.h>
96 #endif
97
98 #ifdef HAVE_SYS_IOCTL_H
99 #include <sys/ioctl.h>
100 #endif
101
102 #ifdef HAVE_SYS_SYSCTL_H
103 #include <sys/sysctl.h>
104 #endif
105
106 #ifdef HAVE_SYS_SOCKIO_H
107 #include <sys/sockio.h>
108 #endif
109
110 #ifdef HAVE_NET_IF_DL_H
111 #include <net/if_dl.h>
112 #endif
113
114 #ifdef HAVE_NET_IF_TYPES_H
115 #include <net/if_types.h>
116 #endif
117
118 #include "windef.h"
119 #include "winbase.h"
120 #include "iprtrmib.h"
121 #include "ifenum.h"
122
123 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
124 #define ifreq_len(ifr) \
125  max(sizeof(struct ifreq), sizeof((ifr)->ifr_name)+(ifr)->ifr_addr.sa_len)
126 #else
127 #define ifreq_len(ifr) sizeof(struct ifreq)
128 #endif
129
130 #ifndef ETH_ALEN
131 #define ETH_ALEN 6
132 #endif
133
134 #ifndef INADDR_NONE
135 #define INADDR_NONE (~0U)
136 #endif
137
138 #define INITIAL_INTERFACES_ASSUMED 4
139
140 #define INDEX_IS_LOOPBACK 0x00800000
141
142 /* Type declarations */
143
144 typedef struct _InterfaceNameMapEntry {
145   char  name[IFNAMSIZ];
146   BOOL  inUse;
147   BOOL  usedLastPass;
148 } InterfaceNameMapEntry;
149
150 typedef struct _InterfaceNameMap {
151   DWORD numInterfaces;
152   DWORD nextAvailable;
153   DWORD numAllocated;
154   InterfaceNameMapEntry table[1];
155 } InterfaceNameMap;
156
157 /* Global variables */
158
159 static InterfaceNameMap *gNonLoopbackInterfaceMap = NULL;
160 static InterfaceNameMap *gLoopbackInterfaceMap = NULL;
161
162 /* Functions */
163
164 /* Sizes the passed-in map to have enough space for numInterfaces interfaces.
165  * If map is NULL, allocates a new map.  If it is not, may reallocate the
166  * existing map and return a map of increased size.  Returns the allocated map,
167  * or NULL if it could not allocate a map of the requested size.
168  */
169 InterfaceNameMap *sizeMap(InterfaceNameMap *map, DWORD numInterfaces)
170 {
171   if (!map) {
172     numInterfaces = max(numInterfaces, INITIAL_INTERFACES_ASSUMED);
173     map = (InterfaceNameMap *)calloc(1, sizeof(InterfaceNameMap) +
174      (numInterfaces - 1) * sizeof(InterfaceNameMapEntry));
175     if (map)
176       map->numAllocated = numInterfaces;
177   }
178   else {
179     if (map->numAllocated < numInterfaces) {
180       map = (InterfaceNameMap *)realloc(map, sizeof(InterfaceNameMap) +
181        (numInterfaces - 1) * sizeof(InterfaceNameMapEntry));
182       if (map)
183         memset(&map->table[map->numAllocated], 0,
184          (numInterfaces - map->numAllocated) * sizeof(InterfaceNameMapEntry));
185     }
186   }
187   return map;
188 }
189
190 static int isLoopbackInterface(int fd, const char *name)
191 {
192   int ret = 0;
193
194   if (name) {
195     struct ifreq ifr;
196
197     strncpy(ifr.ifr_name, name, IFNAMSIZ);
198     ifr.ifr_name[IFNAMSIZ-1] = '\0';
199     if (ioctl(fd, SIOCGIFFLAGS, &ifr) == 0)
200       ret = ifr.ifr_flags & IFF_LOOPBACK;
201   }
202   return ret;
203 }
204
205 static void countInterfaces(int fd, caddr_t buf, size_t len)
206 {
207   caddr_t ifPtr = buf;
208   DWORD numNonLoopbackInterfaces = 0, numLoopbackInterfaces = 0;
209
210   while (ifPtr && ifPtr < buf + len) {
211     struct ifreq *ifr = (struct ifreq *)ifPtr;
212
213     if (isLoopbackInterface(fd, ifr->ifr_name))
214       numLoopbackInterfaces++;
215     else
216       numNonLoopbackInterfaces++;
217     ifPtr += ifreq_len(ifr);
218   }
219   gNonLoopbackInterfaceMap = sizeMap(gNonLoopbackInterfaceMap,
220    numNonLoopbackInterfaces);
221   gLoopbackInterfaceMap = sizeMap(gLoopbackInterfaceMap,
222    numLoopbackInterfaces);
223 }
224
225 /* Stores the name in the given map, and increments the map's numInterfaces
226  * member if stored successfully.  Will store in the same slot as previously if
227  * usedLastPass is set, otherwise will store in a new slot.
228  * Assumes map and name are not NULL, and the usedLastPass flag is set
229  * correctly for each entry in the map, and that map->numInterfaces <
230  * map->numAllocated.
231  * FIXME: this is kind of expensive, doing a linear scan of the map with a
232  * string comparison of each entry to find the old slot.
233  */
234 static void storeInterfaceInMap(InterfaceNameMap *map, const char *name)
235 {
236   if (map && name) {
237     DWORD ndx;
238     BOOL stored = FALSE;
239
240     /* look for previous slot, mark in use if so */
241     for (ndx = 0; !stored && ndx < map->nextAvailable; ndx++) {
242       if (map->table[ndx].usedLastPass && !strncmp(map->table[ndx].name, name,
243        sizeof(map->table[ndx].name))) {
244         map->table[ndx].inUse = TRUE;
245         stored = TRUE;
246       }
247     }
248     /* look for new slot */
249     for (ndx = 0; !stored && ndx < map->numAllocated; ndx++) {
250       if (!map->table[ndx].inUse) {
251         strncpy(map->table[ndx].name, name, IFNAMSIZ);
252         map->table[ndx].name[IFNAMSIZ-1] = '\0';
253         map->table[ndx].inUse = TRUE;
254         stored = TRUE;
255         if (ndx >= map->nextAvailable)
256           map->nextAvailable = ndx + 1;
257       }
258     }
259     if (stored)
260       map->numInterfaces++;
261   }
262 }
263
264 /* Sets all used entries' usedLastPass flag to their inUse flag, clears
265  * their inUse flag, and clears their numInterfaces member.
266  */
267 static void markOldInterfaces(InterfaceNameMap *map)
268 {
269   if (map) {
270     DWORD ndx;
271
272     map->numInterfaces = 0;
273     for (ndx = 0; ndx < map->nextAvailable; ndx++) {
274       map->table[ndx].usedLastPass = map->table[ndx].inUse;
275       map->table[ndx].inUse = FALSE;
276     }
277   }
278 }
279
280 static void classifyInterfaces(int fd, caddr_t buf, size_t len)
281 {
282   caddr_t ifPtr = buf;
283
284   markOldInterfaces(gNonLoopbackInterfaceMap);
285   markOldInterfaces(gLoopbackInterfaceMap);
286   while (ifPtr && ifPtr < buf + len) {
287     struct ifreq *ifr = (struct ifreq *)ifPtr;
288
289     if (isLoopbackInterface(fd, ifr->ifr_name))
290       storeInterfaceInMap(gLoopbackInterfaceMap, ifr->ifr_name);
291     else
292       storeInterfaceInMap(gNonLoopbackInterfaceMap, ifr->ifr_name);
293     ifPtr += ifreq_len(ifr);
294   }
295 }
296
297 static void enumerateInterfaces(void)
298 {
299   int fd;
300
301   fd = socket(PF_INET, SOCK_DGRAM, 0);
302   if (fd != -1) {
303     int ret, guessedNumInterfaces;
304     struct ifconf ifc;
305
306     /* try to avoid silly heap action by starting with the right size buffer */
307     guessedNumInterfaces = 0;
308     if (gNonLoopbackInterfaceMap)
309       guessedNumInterfaces += gNonLoopbackInterfaceMap->numInterfaces;
310     if (gLoopbackInterfaceMap)
311       guessedNumInterfaces += gLoopbackInterfaceMap->numInterfaces;
312
313     ret = 0;
314     memset(&ifc, 0, sizeof(ifc));
315     /* there is no way to know the interface count beforehand,
316        so we need to loop again and again upping our max each time
317        until returned < max */
318     do {
319       if (guessedNumInterfaces == 0)
320         guessedNumInterfaces = INITIAL_INTERFACES_ASSUMED;
321       else
322         guessedNumInterfaces *= 2;
323       if (ifc.ifc_buf)
324         free(ifc.ifc_buf);
325       ifc.ifc_len = sizeof(struct ifreq) * guessedNumInterfaces;
326       ifc.ifc_buf = (char *)malloc(ifc.ifc_len);
327       ret = ioctl(fd, SIOCGIFCONF, &ifc);
328     } while (ret == 0 &&
329      ifc.ifc_len == (sizeof(struct ifreq) * guessedNumInterfaces));
330
331     if (ret == 0) {
332       countInterfaces(fd, ifc.ifc_buf, ifc.ifc_len);
333       classifyInterfaces(fd, ifc.ifc_buf, ifc.ifc_len);
334     }
335
336     if (ifc.ifc_buf)
337       free(ifc.ifc_buf);
338     close(fd);
339   }
340 }
341
342 DWORD getNumNonLoopbackInterfaces(void)
343 {
344   enumerateInterfaces();
345   return gNonLoopbackInterfaceMap ? gNonLoopbackInterfaceMap->numInterfaces : 0;
346 }
347
348 DWORD getNumInterfaces(void)
349 {
350   DWORD ret = getNumNonLoopbackInterfaces();
351
352   ret += gLoopbackInterfaceMap ? gLoopbackInterfaceMap->numInterfaces : 0;
353   return ret;
354 }
355
356 const char *getInterfaceNameByIndex(DWORD index)
357 {
358   DWORD realIndex;
359   InterfaceNameMap *map;
360   const char *ret = NULL;
361
362   if (index & INDEX_IS_LOOPBACK) {
363     realIndex = index ^ INDEX_IS_LOOPBACK;
364     map = gLoopbackInterfaceMap;
365   }
366   else {
367     realIndex = index;
368     map = gNonLoopbackInterfaceMap;
369   }
370   if (map && realIndex < map->nextAvailable)
371     ret = map->table[realIndex].name;
372   return ret;
373 }
374
375 DWORD getInterfaceIndexByName(const char *name, PDWORD index)
376 {
377   DWORD ndx;
378   BOOL found = FALSE;
379
380   if (!name)
381     return ERROR_INVALID_PARAMETER;
382   if (!index)
383     return ERROR_INVALID_PARAMETER;
384
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   if (found)
398     return NO_ERROR;
399   else
400     return ERROR_INVALID_DATA;
401 }
402
403 static void addMapEntriesToIndexTable(InterfaceIndexTable *table,
404  const InterfaceNameMap *map)
405 {
406   if (table && map) {
407     DWORD ndx;
408
409     for (ndx = 0; ndx < map->nextAvailable &&
410      table->numIndexes < table->numAllocated; ndx++)
411       if (map->table[ndx].inUse) {
412         DWORD externalNdx = ndx;
413
414         if (map == gLoopbackInterfaceMap)
415           externalNdx |= INDEX_IS_LOOPBACK;
416         table->indexes[table->numIndexes++] = externalNdx;
417       }
418   }
419 }
420
421 InterfaceIndexTable *getInterfaceIndexTable(void)
422 {
423   DWORD numInterfaces = getNumInterfaces();
424   InterfaceIndexTable *ret = (InterfaceIndexTable *)calloc(1,
425    sizeof(InterfaceIndexTable) + (numInterfaces - 1) * sizeof(DWORD));
426
427   if (ret) {
428     ret->numAllocated = numInterfaces;
429     addMapEntriesToIndexTable(ret, gNonLoopbackInterfaceMap);
430     addMapEntriesToIndexTable(ret, gLoopbackInterfaceMap);
431   }
432   return ret;
433 }
434
435 InterfaceIndexTable *getNonLoopbackInterfaceIndexTable(void)
436 {
437   DWORD numInterfaces = getNumNonLoopbackInterfaces();
438   InterfaceIndexTable *ret = (InterfaceIndexTable *)calloc(1,
439    sizeof(InterfaceIndexTable) + (numInterfaces - 1) * sizeof(DWORD));
440
441   if (ret) {
442     ret->numAllocated = numInterfaces;
443     addMapEntriesToIndexTable(ret, gNonLoopbackInterfaceMap);
444   }
445   return ret;
446 }
447
448 DWORD getInterfaceIPAddrByName(const char *name)
449 {
450   DWORD ret = INADDR_ANY;
451
452   if (name) {
453     int fd = socket(PF_INET, SOCK_DGRAM, 0);
454
455     if (fd != -1) {
456       struct ifreq ifr;
457
458       strncpy(ifr.ifr_name, name, IFNAMSIZ);
459       ifr.ifr_name[IFNAMSIZ-1] = '\0';
460       if (ioctl(fd, SIOCGIFADDR, &ifr) == 0)
461         memcpy(&ret, ifr.ifr_addr.sa_data + 2, sizeof(DWORD));
462       close(fd);
463     }
464   }
465   return ret;
466 }
467
468 DWORD getInterfaceIPAddrByIndex(DWORD index)
469 {
470   DWORD ret;
471   const char *name = getInterfaceNameByIndex(index);
472
473   if (name)
474     ret = getInterfaceIPAddrByName(name);
475   else
476     ret = INADDR_ANY;
477   return ret;
478 }
479
480 DWORD getInterfaceBCastAddrByName(const char *name)
481 {
482   DWORD ret = INADDR_ANY;
483
484   if (name) {
485     int fd = socket(PF_INET, SOCK_DGRAM, 0);
486
487     if (fd != -1) {
488       struct ifreq ifr;
489
490       strncpy(ifr.ifr_name, name, IFNAMSIZ);
491       ifr.ifr_name[IFNAMSIZ-1] = '\0';
492       if (ioctl(fd, SIOCGIFBRDADDR, &ifr) == 0)
493         memcpy(&ret, ifr.ifr_addr.sa_data + 2, sizeof(DWORD));
494       close(fd);
495     }
496   }
497   return ret;
498 }
499
500 DWORD getInterfaceBCastAddrByIndex(DWORD index)
501 {
502   DWORD ret;
503   const char *name = getInterfaceNameByIndex(index);
504
505   if (name)
506     ret = getInterfaceBCastAddrByName(name);
507   else
508     ret = INADDR_ANY;
509   return ret;
510 }
511
512 DWORD getInterfaceMaskByName(const char *name)
513 {
514   DWORD ret = INADDR_NONE;
515
516   if (name) {
517     int fd = socket(PF_INET, SOCK_DGRAM, 0);
518
519     if (fd != -1) {
520       struct ifreq ifr;
521
522       strncpy(ifr.ifr_name, name, IFNAMSIZ);
523       ifr.ifr_name[IFNAMSIZ-1] = '\0';
524       if (ioctl(fd, SIOCGIFNETMASK, &ifr) == 0)
525         memcpy(&ret, ifr.ifr_addr.sa_data + 2, sizeof(DWORD));
526       close(fd);
527     }
528   }
529   return ret;
530 }
531
532 DWORD getInterfaceMaskByIndex(DWORD index)
533 {
534   DWORD ret;
535   const char *name = getInterfaceNameByIndex(index);
536
537   if (name)
538     ret = getInterfaceMaskByName(name);
539   else
540     ret = INADDR_NONE;
541   return ret;
542 }
543
544 #if defined (SIOCGIFHWADDR)
545 DWORD getInterfacePhysicalByName(const char *name, PDWORD len, PBYTE addr,
546  PDWORD type)
547 {
548   DWORD ret;
549   int fd;
550
551   if (!name || !len || !addr || !type)
552     return ERROR_INVALID_PARAMETER;
553
554   fd = socket(PF_INET, SOCK_DGRAM, 0);
555   if (fd != -1) {
556     struct ifreq ifr;
557
558     memset(&ifr, 0, sizeof(struct ifreq));
559     strncpy(ifr.ifr_name, name, IFNAMSIZ);
560     ifr.ifr_name[IFNAMSIZ-1] = '\0';
561     if ((ioctl(fd, SIOCGIFHWADDR, &ifr)))
562       ret = ERROR_INVALID_DATA;
563     else {
564       int addrLen;
565
566       switch (ifr.ifr_hwaddr.sa_family)
567       {
568         case ARPHRD_LOOPBACK:
569           addrLen = 0;
570           *type = MIB_IF_TYPE_LOOPBACK;
571           break;
572         case ARPHRD_ETHER:
573           addrLen = ETH_ALEN;
574           *type = MIB_IF_TYPE_ETHERNET;
575           break;
576         case ARPHRD_FDDI:
577           addrLen = ETH_ALEN;
578           *type = MIB_IF_TYPE_FDDI;
579           break;
580         case ARPHRD_IEEE802: /* 802.2 Ethernet && Token Ring, guess TR? */
581           addrLen = ETH_ALEN;
582           *type = MIB_IF_TYPE_TOKENRING;
583           break;
584         case ARPHRD_IEEE802_TR: /* also Token Ring? */
585           addrLen = ETH_ALEN;
586           *type = MIB_IF_TYPE_TOKENRING;
587           break;
588         case ARPHRD_SLIP:
589           addrLen = 0;
590           *type = MIB_IF_TYPE_SLIP;
591           break;
592         case ARPHRD_PPP:
593           addrLen = 0;
594           *type = MIB_IF_TYPE_PPP;
595           break;
596         default:
597           addrLen = min(MAX_INTERFACE_PHYSADDR, sizeof(ifr.ifr_hwaddr.sa_data));
598           *type = MIB_IF_TYPE_OTHER;
599       }
600       if (addrLen > *len) {
601         ret = ERROR_INSUFFICIENT_BUFFER;
602         *len = addrLen;
603       }
604       else {
605         if (addrLen > 0)
606           memcpy(addr, ifr.ifr_hwaddr.sa_data, addrLen);
607         /* zero out remaining bytes for broken implementations */
608         memset(addr + addrLen, 0, *len - addrLen);
609         *len = addrLen;
610         ret = NO_ERROR;
611       }
612     }
613     close(fd);
614   }
615   else
616     ret = ERROR_NO_MORE_FILES;
617   return ret;
618 }
619 #elif defined (SIOCGARP)
620 DWORD getInterfacePhysicalByName(const char *name, PDWORD len, PBYTE addr,
621  PDWORD type)
622 {
623   DWORD ret;
624   int fd;
625
626   if (!name || !len || !addr || !type)
627     return ERROR_INVALID_PARAMETER;
628
629   fd = socket(PF_INET, SOCK_DGRAM, 0);
630   if (fd != -1) {
631     if (isLoopbackInterface(fd, name)) {
632       *type = MIB_IF_TYPE_LOOPBACK;
633       memset(addr, 0, *len);
634       *len = 0;
635       ret=NOERROR;
636     }
637     else {
638       struct arpreq arp;
639       struct sockaddr_in *saddr;
640
641       memset(&arp, 0, sizeof(struct arpreq));
642       arp.arp_pa.sa_family = AF_INET;
643       saddr = (struct sockaddr_in *)&arp; /* proto addr is first member */
644       saddr->sin_family = AF_INET;
645       saddr->sin_addr.s_addr = getInterfaceIPAddrByName(name);
646       if ((ioctl(fd, SIOCGARP, &arp)))
647         ret = ERROR_INVALID_DATA;
648       else {
649         /* FIXME:  heh:  who said it was ethernet? */
650         int addrLen = ETH_ALEN;
651
652         if (addrLen > *len) {
653           ret = ERROR_INSUFFICIENT_BUFFER;
654           *len = addrLen;
655         }
656         else {
657           if (addrLen > 0)
658             memcpy(addr, &arp.arp_ha.sa_data[0], addrLen);
659           /* zero out remaining bytes for broken implementations */
660           memset(addr + addrLen, 0, *len - addrLen);
661           *len = addrLen;
662           *type = MIB_IF_TYPE_ETHERNET;
663           ret = NO_ERROR;
664         }
665       }
666     }
667     close(fd);
668   }
669     else
670       ret = ERROR_NO_MORE_FILES;
671
672   return ret;
673 }
674 #elif defined (HAVE_SYS_SYSCTL_H) && defined (HAVE_NET_IF_DL_H)
675 DWORD getInterfacePhysicalByName(const char *name, PDWORD len, PBYTE addr,
676  PDWORD type)
677 {
678   DWORD ret;
679   struct if_msghdr *ifm;
680   struct sockaddr_dl *sdl;
681   u_char *p, *buf;
682   size_t mibLen;
683   int mib[] = { CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0 };
684   int addrLen;
685   BOOL found = FALSE;
686
687   if (!name || !len || !addr || !type)
688     return ERROR_INVALID_PARAMETER;
689
690   if (sysctl(mib, 6, NULL, &mibLen, NULL, 0) < 0)
691     return ERROR_NO_MORE_FILES;
692
693   buf = (u_char *)malloc(mibLen);
694   if (!buf)
695     return ERROR_NOT_ENOUGH_MEMORY;
696
697   if (sysctl(mib, 6, buf, &mibLen, NULL, 0) < 0) {
698     free(buf);
699     return ERROR_NO_MORE_FILES;
700   }
701
702   ret = ERROR_INVALID_DATA;
703   for (p = buf; !found && p < buf + mibLen; p += ifm->ifm_msglen) {
704     ifm = (struct if_msghdr *)p;
705     sdl = (struct sockaddr_dl *)(ifm + 1);
706
707     if (ifm->ifm_type != RTM_IFINFO || (ifm->ifm_addrs & RTA_IFP) == 0)
708       continue;
709
710     if (sdl->sdl_family != AF_LINK || sdl->sdl_nlen == 0 ||
711      memcmp(sdl->sdl_data, name, max(sdl->sdl_nlen, strlen(name))) != 0)
712       continue;
713
714     found = TRUE;
715     addrLen = min(MAX_INTERFACE_PHYSADDR, sdl->sdl_alen);
716     if (addrLen > *len) {
717       ret = ERROR_INSUFFICIENT_BUFFER;
718       *len = addrLen;
719     }
720     else {
721       if (addrLen > 0)
722         memcpy(addr, LLADDR(sdl), addrLen);
723       /* zero out remaining bytes for broken implementations */
724       memset(addr + addrLen, 0, *len - addrLen);
725       *len = addrLen;
726 #if defined(HAVE_NET_IF_TYPES_H)
727       switch (sdl->sdl_type)
728       {
729         case IFT_ETHER:
730           *type = MIB_IF_TYPE_ETHERNET;
731           break;
732         case IFT_FDDI:
733           *type = MIB_IF_TYPE_FDDI;
734           break;
735         case IFT_ISO88024: /* Token Bus */
736           *type = MIB_IF_TYPE_TOKENRING;
737           break;
738         case IFT_ISO88025: /* Token Ring */
739           *type = MIB_IF_TYPE_TOKENRING;
740           break;
741         case IFT_PPP:
742           *type = MIB_IF_TYPE_PPP;
743           break;
744         case IFT_SLIP:
745           *type = MIB_IF_TYPE_SLIP;
746           break;
747         case IFT_LOOP:
748           *type = MIB_IF_TYPE_LOOPBACK;
749           break;
750         default:
751           *type = MIB_IF_TYPE_OTHER;
752       }
753 #else
754       /* default if we don't know */
755       *type = MIB_IF_TYPE_ETHERNET;
756 #endif
757       ret = NO_ERROR;
758     }
759   }
760   free(buf);
761   return ret;
762 }
763 #endif
764
765 DWORD getInterfacePhysicalByIndex(DWORD index, PDWORD len, PBYTE addr,
766  PDWORD type)
767 {
768   const char *name = getInterfaceNameByIndex(index);
769
770   if (name)
771     return getInterfacePhysicalByName(name, len, addr, type);
772   else
773     return ERROR_INVALID_DATA;
774 }
775
776 DWORD getInterfaceMtuByName(const char *name, PDWORD mtu)
777 {
778   DWORD ret;
779   int fd;
780
781   if (!name)
782     return ERROR_INVALID_PARAMETER;
783   if (!mtu)
784     return ERROR_INVALID_PARAMETER;
785
786   fd = socket(PF_INET, SOCK_DGRAM, 0);
787   if (fd != -1) {
788     struct ifreq ifr;
789
790     strncpy(ifr.ifr_name, name, IFNAMSIZ);
791     ifr.ifr_name[IFNAMSIZ-1] = '\0';
792     if ((ioctl(fd, SIOCGIFMTU, &ifr)))
793       ret = ERROR_INVALID_DATA;
794     else {
795       *mtu = ifr.ifr_mtu;
796       ret = NO_ERROR;
797     }
798   }
799   else
800     ret = ERROR_NO_MORE_FILES;
801   return ret;
802 }
803
804 DWORD getInterfaceMtuByIndex(DWORD index, PDWORD mtu)
805 {
806   const char *name = getInterfaceNameByIndex(index);
807
808   if (name)
809     return getInterfaceMtuByName(name, mtu);
810   else
811     return ERROR_INVALID_DATA;
812 }
813
814 DWORD getInterfaceStatusByName(const char *name, PDWORD status)
815 {
816   DWORD ret;
817   int fd;
818
819   if (!name)
820     return ERROR_INVALID_PARAMETER;
821   if (!status)
822     return ERROR_INVALID_PARAMETER;
823
824   fd = socket(PF_INET, SOCK_DGRAM, 0);
825   if (fd != -1) {
826     struct ifreq ifr;
827
828     strncpy(ifr.ifr_name, name, IFNAMSIZ);
829     ifr.ifr_name[IFNAMSIZ-1] = '\0';
830     if ((ioctl(fd, SIOCGIFFLAGS, &ifr)))
831       ret = ERROR_INVALID_DATA;
832     else {
833       if (ifr.ifr_flags & IFF_UP)
834         *status = MIB_IF_OPER_STATUS_OPERATIONAL;
835       else
836         *status = MIB_IF_OPER_STATUS_NON_OPERATIONAL;
837       ret = NO_ERROR;
838     }
839   }
840   else
841     ret = ERROR_NO_MORE_FILES;
842   return ret;
843 }
844
845 DWORD getInterfaceStatusByIndex(DWORD index, PDWORD status)
846 {
847   const char *name = getInterfaceNameByIndex(index);
848
849   if (name)
850     return getInterfaceStatusByName(name, status);
851   else
852     return ERROR_INVALID_DATA;
853 }
854
855 DWORD getInterfaceEntryByName(const char *name, PMIB_IFROW entry)
856 {
857   BYTE addr[MAX_INTERFACE_PHYSADDR];
858   DWORD ret, len = sizeof(addr), type;
859
860   if (!name)
861     return ERROR_INVALID_PARAMETER;
862   if (!entry)
863     return ERROR_INVALID_PARAMETER;
864
865   if (getInterfacePhysicalByName(name, &len, addr, &type) == NO_ERROR) {
866     WCHAR *assigner;
867     const char *walker;
868
869     memset(entry, 0, sizeof(MIB_IFROW));
870     for (assigner = entry->wszName, walker = name; *walker; 
871      walker++, assigner++)
872       *assigner = *walker;
873     *assigner = 0;
874     getInterfaceIndexByName(name, &entry->dwIndex);
875     entry->dwPhysAddrLen = len;
876     memcpy(entry->bPhysAddr, addr, len);
877     memset(entry->bPhysAddr + len, 0, sizeof(entry->bPhysAddr) - len);
878     entry->dwType = type;
879     /* FIXME: how to calculate real speed? */
880     getInterfaceMtuByName(name, &entry->dwMtu);
881     /* lie, there's no "administratively down" here */
882     entry->dwAdminStatus = MIB_IF_ADMIN_STATUS_UP;
883     getInterfaceStatusByName(name, &entry->dwOperStatus);
884     /* punt on dwLastChange? */
885     entry->dwDescrLen = min(strlen(name), MAX_INTERFACE_DESCRIPTION - 1);
886     memcpy(entry->bDescr, name, entry->dwDescrLen);
887     entry->bDescr[entry->dwDescrLen] = '\0';
888     entry->dwDescrLen++;
889     ret = NO_ERROR;
890   }
891   else
892     ret = ERROR_INVALID_DATA;
893   return ret;
894 }
895
896 DWORD getInterfaceEntryByIndex(DWORD index, PMIB_IFROW entry)
897 {
898   const char *name = getInterfaceNameByIndex(index);
899
900   if (name)
901     return getInterfaceEntryByName(name, entry);
902   else
903     return ERROR_INVALID_DATA;
904 }
905
906 char *toIPAddressString(unsigned int addr, char string[16])
907 {
908   if (string) {
909     struct in_addr iAddr;
910
911     iAddr.s_addr = addr;
912     /* extra-anal, just to make auditors happy */
913     strncpy(string, inet_ntoa(iAddr), 16);
914     string[16] = '\0';
915   }
916   return string;
917 }