INT21_GetFreeDiskSpace(): The drive parameter is found in the DL
[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 #ifdef ARPHRD_LOOPBACK
569         case ARPHRD_LOOPBACK:
570           addrLen = 0;
571           *type = MIB_IF_TYPE_LOOPBACK;
572           break;
573 #endif
574 #ifdef ARPHRD_ETHER
575         case ARPHRD_ETHER:
576           addrLen = ETH_ALEN;
577           *type = MIB_IF_TYPE_ETHERNET;
578           break;
579 #endif
580 #ifdef ARPHRD_FDDI
581         case ARPHRD_FDDI:
582           addrLen = ETH_ALEN;
583           *type = MIB_IF_TYPE_FDDI;
584           break;
585 #endif
586 #ifdef ARPHRD_IEEE802
587         case ARPHRD_IEEE802: /* 802.2 Ethernet && Token Ring, guess TR? */
588           addrLen = ETH_ALEN;
589           *type = MIB_IF_TYPE_TOKENRING;
590           break;
591 #endif
592 #ifdef ARPHRD_IEEE802_TR
593         case ARPHRD_IEEE802_TR: /* also Token Ring? */
594           addrLen = ETH_ALEN;
595           *type = MIB_IF_TYPE_TOKENRING;
596           break;
597 #endif
598 #ifdef ARPHRD_SLIP
599         case ARPHRD_SLIP:
600           addrLen = 0;
601           *type = MIB_IF_TYPE_SLIP;
602           break;
603 #endif
604 #ifdef ARPHRD_PPP
605         case ARPHRD_PPP:
606           addrLen = 0;
607           *type = MIB_IF_TYPE_PPP;
608           break;
609 #endif
610         default:
611           addrLen = min(MAX_INTERFACE_PHYSADDR, sizeof(ifr.ifr_hwaddr.sa_data));
612           *type = MIB_IF_TYPE_OTHER;
613       }
614       if (addrLen > *len) {
615         ret = ERROR_INSUFFICIENT_BUFFER;
616         *len = addrLen;
617       }
618       else {
619         if (addrLen > 0)
620           memcpy(addr, ifr.ifr_hwaddr.sa_data, addrLen);
621         /* zero out remaining bytes for broken implementations */
622         memset(addr + addrLen, 0, *len - addrLen);
623         *len = addrLen;
624         ret = NO_ERROR;
625       }
626     }
627     close(fd);
628   }
629   else
630     ret = ERROR_NO_MORE_FILES;
631   return ret;
632 }
633 #elif defined (SIOCGARP)
634 DWORD getInterfacePhysicalByName(const char *name, PDWORD len, PBYTE addr,
635  PDWORD type)
636 {
637   DWORD ret;
638   int fd;
639
640   if (!name || !len || !addr || !type)
641     return ERROR_INVALID_PARAMETER;
642
643   fd = socket(PF_INET, SOCK_DGRAM, 0);
644   if (fd != -1) {
645     if (isLoopbackInterface(fd, name)) {
646       *type = MIB_IF_TYPE_LOOPBACK;
647       memset(addr, 0, *len);
648       *len = 0;
649       ret=NOERROR;
650     }
651     else {
652       struct arpreq arp;
653       struct sockaddr_in *saddr;
654
655       memset(&arp, 0, sizeof(struct arpreq));
656       arp.arp_pa.sa_family = AF_INET;
657       saddr = (struct sockaddr_in *)&arp; /* proto addr is first member */
658       saddr->sin_family = AF_INET;
659       saddr->sin_addr.s_addr = getInterfaceIPAddrByName(name);
660       if ((ioctl(fd, SIOCGARP, &arp)))
661         ret = ERROR_INVALID_DATA;
662       else {
663         /* FIXME:  heh:  who said it was ethernet? */
664         int addrLen = ETH_ALEN;
665
666         if (addrLen > *len) {
667           ret = ERROR_INSUFFICIENT_BUFFER;
668           *len = addrLen;
669         }
670         else {
671           if (addrLen > 0)
672             memcpy(addr, &arp.arp_ha.sa_data[0], addrLen);
673           /* zero out remaining bytes for broken implementations */
674           memset(addr + addrLen, 0, *len - addrLen);
675           *len = addrLen;
676           *type = MIB_IF_TYPE_ETHERNET;
677           ret = NO_ERROR;
678         }
679       }
680     }
681     close(fd);
682   }
683     else
684       ret = ERROR_NO_MORE_FILES;
685
686   return ret;
687 }
688 #elif defined (HAVE_SYS_SYSCTL_H) && defined (HAVE_NET_IF_DL_H)
689 DWORD getInterfacePhysicalByName(const char *name, PDWORD len, PBYTE addr,
690  PDWORD type)
691 {
692   DWORD ret;
693   struct if_msghdr *ifm;
694   struct sockaddr_dl *sdl;
695   u_char *p, *buf;
696   size_t mibLen;
697   int mib[] = { CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0 };
698   int addrLen;
699   BOOL found = FALSE;
700
701   if (!name || !len || !addr || !type)
702     return ERROR_INVALID_PARAMETER;
703
704   if (sysctl(mib, 6, NULL, &mibLen, NULL, 0) < 0)
705     return ERROR_NO_MORE_FILES;
706
707   buf = (u_char *)malloc(mibLen);
708   if (!buf)
709     return ERROR_NOT_ENOUGH_MEMORY;
710
711   if (sysctl(mib, 6, buf, &mibLen, NULL, 0) < 0) {
712     free(buf);
713     return ERROR_NO_MORE_FILES;
714   }
715
716   ret = ERROR_INVALID_DATA;
717   for (p = buf; !found && p < buf + mibLen; p += ifm->ifm_msglen) {
718     ifm = (struct if_msghdr *)p;
719     sdl = (struct sockaddr_dl *)(ifm + 1);
720
721     if (ifm->ifm_type != RTM_IFINFO || (ifm->ifm_addrs & RTA_IFP) == 0)
722       continue;
723
724     if (sdl->sdl_family != AF_LINK || sdl->sdl_nlen == 0 ||
725      memcmp(sdl->sdl_data, name, max(sdl->sdl_nlen, strlen(name))) != 0)
726       continue;
727
728     found = TRUE;
729     addrLen = min(MAX_INTERFACE_PHYSADDR, sdl->sdl_alen);
730     if (addrLen > *len) {
731       ret = ERROR_INSUFFICIENT_BUFFER;
732       *len = addrLen;
733     }
734     else {
735       if (addrLen > 0)
736         memcpy(addr, LLADDR(sdl), addrLen);
737       /* zero out remaining bytes for broken implementations */
738       memset(addr + addrLen, 0, *len - addrLen);
739       *len = addrLen;
740 #if defined(HAVE_NET_IF_TYPES_H)
741       switch (sdl->sdl_type)
742       {
743         case IFT_ETHER:
744           *type = MIB_IF_TYPE_ETHERNET;
745           break;
746         case IFT_FDDI:
747           *type = MIB_IF_TYPE_FDDI;
748           break;
749         case IFT_ISO88024: /* Token Bus */
750           *type = MIB_IF_TYPE_TOKENRING;
751           break;
752         case IFT_ISO88025: /* Token Ring */
753           *type = MIB_IF_TYPE_TOKENRING;
754           break;
755         case IFT_PPP:
756           *type = MIB_IF_TYPE_PPP;
757           break;
758         case IFT_SLIP:
759           *type = MIB_IF_TYPE_SLIP;
760           break;
761         case IFT_LOOP:
762           *type = MIB_IF_TYPE_LOOPBACK;
763           break;
764         default:
765           *type = MIB_IF_TYPE_OTHER;
766       }
767 #else
768       /* default if we don't know */
769       *type = MIB_IF_TYPE_ETHERNET;
770 #endif
771       ret = NO_ERROR;
772     }
773   }
774   free(buf);
775   return ret;
776 }
777 #endif
778
779 DWORD getInterfacePhysicalByIndex(DWORD index, PDWORD len, PBYTE addr,
780  PDWORD type)
781 {
782   const char *name = getInterfaceNameByIndex(index);
783
784   if (name)
785     return getInterfacePhysicalByName(name, len, addr, type);
786   else
787     return ERROR_INVALID_DATA;
788 }
789
790 DWORD getInterfaceMtuByName(const char *name, PDWORD mtu)
791 {
792   DWORD ret;
793   int fd;
794
795   if (!name)
796     return ERROR_INVALID_PARAMETER;
797   if (!mtu)
798     return ERROR_INVALID_PARAMETER;
799
800   fd = socket(PF_INET, SOCK_DGRAM, 0);
801   if (fd != -1) {
802     struct ifreq ifr;
803
804     strncpy(ifr.ifr_name, name, IFNAMSIZ);
805     ifr.ifr_name[IFNAMSIZ-1] = '\0';
806     if ((ioctl(fd, SIOCGIFMTU, &ifr)))
807       ret = ERROR_INVALID_DATA;
808     else {
809       *mtu = ifr.ifr_mtu;
810       ret = NO_ERROR;
811     }
812   }
813   else
814     ret = ERROR_NO_MORE_FILES;
815   return ret;
816 }
817
818 DWORD getInterfaceMtuByIndex(DWORD index, PDWORD mtu)
819 {
820   const char *name = getInterfaceNameByIndex(index);
821
822   if (name)
823     return getInterfaceMtuByName(name, mtu);
824   else
825     return ERROR_INVALID_DATA;
826 }
827
828 DWORD getInterfaceStatusByName(const char *name, PDWORD status)
829 {
830   DWORD ret;
831   int fd;
832
833   if (!name)
834     return ERROR_INVALID_PARAMETER;
835   if (!status)
836     return ERROR_INVALID_PARAMETER;
837
838   fd = socket(PF_INET, SOCK_DGRAM, 0);
839   if (fd != -1) {
840     struct ifreq ifr;
841
842     strncpy(ifr.ifr_name, name, IFNAMSIZ);
843     ifr.ifr_name[IFNAMSIZ-1] = '\0';
844     if ((ioctl(fd, SIOCGIFFLAGS, &ifr)))
845       ret = ERROR_INVALID_DATA;
846     else {
847       if (ifr.ifr_flags & IFF_UP)
848         *status = MIB_IF_OPER_STATUS_OPERATIONAL;
849       else
850         *status = MIB_IF_OPER_STATUS_NON_OPERATIONAL;
851       ret = NO_ERROR;
852     }
853   }
854   else
855     ret = ERROR_NO_MORE_FILES;
856   return ret;
857 }
858
859 DWORD getInterfaceStatusByIndex(DWORD index, PDWORD status)
860 {
861   const char *name = getInterfaceNameByIndex(index);
862
863   if (name)
864     return getInterfaceStatusByName(name, status);
865   else
866     return ERROR_INVALID_DATA;
867 }
868
869 DWORD getInterfaceEntryByName(const char *name, PMIB_IFROW entry)
870 {
871   BYTE addr[MAX_INTERFACE_PHYSADDR];
872   DWORD ret, len = sizeof(addr), type;
873
874   if (!name)
875     return ERROR_INVALID_PARAMETER;
876   if (!entry)
877     return ERROR_INVALID_PARAMETER;
878
879   if (getInterfacePhysicalByName(name, &len, addr, &type) == NO_ERROR) {
880     WCHAR *assigner;
881     const char *walker;
882
883     memset(entry, 0, sizeof(MIB_IFROW));
884     for (assigner = entry->wszName, walker = name; *walker; 
885      walker++, assigner++)
886       *assigner = *walker;
887     *assigner = 0;
888     getInterfaceIndexByName(name, &entry->dwIndex);
889     entry->dwPhysAddrLen = len;
890     memcpy(entry->bPhysAddr, addr, len);
891     memset(entry->bPhysAddr + len, 0, sizeof(entry->bPhysAddr) - len);
892     entry->dwType = type;
893     /* FIXME: how to calculate real speed? */
894     getInterfaceMtuByName(name, &entry->dwMtu);
895     /* lie, there's no "administratively down" here */
896     entry->dwAdminStatus = MIB_IF_ADMIN_STATUS_UP;
897     getInterfaceStatusByName(name, &entry->dwOperStatus);
898     /* punt on dwLastChange? */
899     entry->dwDescrLen = min(strlen(name), MAX_INTERFACE_DESCRIPTION - 1);
900     memcpy(entry->bDescr, name, entry->dwDescrLen);
901     entry->bDescr[entry->dwDescrLen] = '\0';
902     entry->dwDescrLen++;
903     ret = NO_ERROR;
904   }
905   else
906     ret = ERROR_INVALID_DATA;
907   return ret;
908 }
909
910 DWORD getInterfaceEntryByIndex(DWORD index, PMIB_IFROW entry)
911 {
912   const char *name = getInterfaceNameByIndex(index);
913
914   if (name)
915     return getInterfaceEntryByName(name, entry);
916   else
917     return ERROR_INVALID_DATA;
918 }
919
920 char *toIPAddressString(unsigned int addr, char string[16])
921 {
922   if (string) {
923     struct in_addr iAddr;
924
925     iAddr.s_addr = addr;
926     /* extra-anal, just to make auditors happy */
927     strncpy(string, inet_ntoa(iAddr), 16);
928     string[16] = '\0';
929   }
930   return string;
931 }