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