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