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