Move declaration of LSA* structs to ntsecapi.h.
[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  * - the memory interface uses malloc/free; it should be using HeapAlloc instead
37  *
38  * There are three implemened methods for determining the MAC address of an
39  * interface:
40  * - a specific IOCTL (Linux)
41  * - looking in the ARP cache (at least Solaris)
42  * - using the sysctl interface (FreeBSD and MacOSX)
43  * Solaris and some others have SIOCGENADDR, but I haven't gotten that to work
44  * on the Solaris boxes at SourceForge's compile farm, whereas SIOCGARP does.
45  */
46
47 #include "config.h"
48
49 #include <stdarg.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53
54 #ifdef HAVE_UNISTD_H
55 #include <unistd.h>
56 #endif
57
58 #include <sys/types.h>
59
60 #ifdef HAVE_SYS_SOCKET_H
61 #include <sys/socket.h>
62 #endif
63
64 #ifdef HAVE_NETINET_IN_H
65 #include <netinet/in.h>
66 #endif
67
68 #ifdef HAVE_ARPA_INET_H
69 #include <arpa/inet.h>
70 #endif
71
72 #ifdef HAVE_NET_IF_H
73 #include <net/if.h>
74 #endif
75
76 #ifdef HAVE_NET_IF_ARP_H
77 #include <net/if_arp.h>
78 #endif
79
80 #ifdef HAVE_NET_ROUTE_H
81 #include <net/route.h>
82 #endif
83
84 #ifdef HAVE_SYS_IOCTL_H
85 #include <sys/ioctl.h>
86 #endif
87
88 #ifdef HAVE_SYS_SYSCTL_H
89 #include <sys/sysctl.h>
90 #endif
91
92 #ifdef HAVE_SYS_SOCKIO_H
93 #include <sys/sockio.h>
94 #endif
95
96 #ifdef HAVE_NET_IF_DL_H
97 #include <net/if_dl.h>
98 #endif
99
100 #ifdef HAVE_NET_IF_TYPES_H
101 #include <net/if_types.h>
102 #endif
103
104 #include "ifenum.h"
105
106 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
107 #define ifreq_len(ifr) \
108  max(sizeof(struct ifreq), sizeof((ifr)->ifr_name)+(ifr)->ifr_addr.sa_len)
109 #else
110 #define ifreq_len(ifr) sizeof(struct ifreq)
111 #endif
112
113 #ifndef ETH_ALEN
114 #define ETH_ALEN 6
115 #endif
116
117 #ifndef INADDR_NONE
118 #define INADDR_NONE (~0U)
119 #endif
120
121 #define INITIAL_INTERFACES_ASSUMED 4
122
123 #define INDEX_IS_LOOPBACK 0x00800000
124
125 /* Type declarations */
126
127 typedef struct _InterfaceNameMapEntry {
128   char  name[IFNAMSIZ];
129   BOOL  inUse;
130   BOOL  usedLastPass;
131 } InterfaceNameMapEntry;
132
133 typedef struct _InterfaceNameMap {
134   DWORD numInterfaces;
135   DWORD nextAvailable;
136   DWORD numAllocated;
137   InterfaceNameMapEntry table[1];
138 } InterfaceNameMap;
139
140 /* Global variables */
141
142 static CRITICAL_SECTION mapCS;
143 static InterfaceNameMap *gNonLoopbackInterfaceMap = NULL;
144 static InterfaceNameMap *gLoopbackInterfaceMap = NULL;
145
146 /* Functions */
147
148 void interfaceMapInit(void)
149 {
150     InitializeCriticalSection(&mapCS);
151 }
152
153 void interfaceMapFree(void)
154 {
155     DeleteCriticalSection(&mapCS);
156     if (gNonLoopbackInterfaceMap)
157         free(gNonLoopbackInterfaceMap);
158     if (gLoopbackInterfaceMap)
159         free(gLoopbackInterfaceMap);
160 }
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 static 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 (ifr->ifr_addr.sa_family == AF_INET) {
288       if (isLoopbackInterface(fd, ifr->ifr_name))
289         storeInterfaceInMap(gLoopbackInterfaceMap, ifr->ifr_name);
290       else
291         storeInterfaceInMap(gNonLoopbackInterfaceMap, ifr->ifr_name);
292     }
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       EnterCriticalSection(&mapCS);
333       countInterfaces(fd, ifc.ifc_buf, ifc.ifc_len);
334       classifyInterfaces(fd, ifc.ifc_buf, ifc.ifc_len);
335       LeaveCriticalSection(&mapCS);
336     }
337
338     if (ifc.ifc_buf)
339       free(ifc.ifc_buf);
340     close(fd);
341   }
342 }
343
344 DWORD getNumNonLoopbackInterfaces(void)
345 {
346   enumerateInterfaces();
347   return gNonLoopbackInterfaceMap ? gNonLoopbackInterfaceMap->numInterfaces : 0;
348 }
349
350 DWORD getNumInterfaces(void)
351 {
352   DWORD ret = getNumNonLoopbackInterfaces();
353
354   ret += gLoopbackInterfaceMap ? gLoopbackInterfaceMap->numInterfaces : 0;
355   return ret;
356 }
357
358 const char *getInterfaceNameByIndex(DWORD index)
359 {
360   DWORD realIndex;
361   InterfaceNameMap *map;
362   const char *ret = NULL;
363
364   EnterCriticalSection(&mapCS);
365   if (index & INDEX_IS_LOOPBACK) {
366     realIndex = index ^ INDEX_IS_LOOPBACK;
367     map = gLoopbackInterfaceMap;
368   }
369   else {
370     realIndex = index;
371     map = gNonLoopbackInterfaceMap;
372   }
373   if (map && realIndex < map->nextAvailable)
374     ret = map->table[realIndex].name;
375   LeaveCriticalSection(&mapCS);
376   return ret;
377 }
378
379 DWORD getInterfaceIndexByName(const char *name, PDWORD index)
380 {
381   DWORD ndx, ret;
382   BOOL found = FALSE;
383
384   if (!name)
385     return ERROR_INVALID_PARAMETER;
386   if (!index)
387     return ERROR_INVALID_PARAMETER;
388
389   EnterCriticalSection(&mapCS);
390   for (ndx = 0; !found && gNonLoopbackInterfaceMap &&
391    ndx < gNonLoopbackInterfaceMap->nextAvailable; ndx++)
392     if (!strncmp(gNonLoopbackInterfaceMap->table[ndx].name, name, IFNAMSIZ)) {
393       found = TRUE;
394       *index = ndx;
395     }
396   for (ndx = 0; !found && gLoopbackInterfaceMap &&
397    ndx < gLoopbackInterfaceMap->nextAvailable; ndx++)
398     if (!strncmp(gLoopbackInterfaceMap->table[ndx].name, name, IFNAMSIZ)) {
399       found = TRUE;
400       *index = ndx | INDEX_IS_LOOPBACK;
401     }
402   LeaveCriticalSection(&mapCS);
403   if (found)
404     ret = NO_ERROR;
405   else
406     ret = ERROR_INVALID_DATA;
407   return ret;
408 }
409
410 static void addMapEntriesToIndexTable(InterfaceIndexTable *table,
411  const InterfaceNameMap *map)
412 {
413   if (table && map) {
414     DWORD ndx;
415
416     for (ndx = 0; ndx < map->nextAvailable &&
417      table->numIndexes < table->numAllocated; ndx++)
418       if (map->table[ndx].inUse) {
419         DWORD externalNdx = ndx;
420
421         if (map == gLoopbackInterfaceMap)
422           externalNdx |= INDEX_IS_LOOPBACK;
423         table->indexes[table->numIndexes++] = externalNdx;
424       }
425   }
426 }
427
428 InterfaceIndexTable *getInterfaceIndexTable(void)
429 {
430   DWORD numInterfaces;
431   InterfaceIndexTable *ret;
432  
433   EnterCriticalSection(&mapCS);
434   numInterfaces = getNumInterfaces();
435   ret = (InterfaceIndexTable *)calloc(1,
436    sizeof(InterfaceIndexTable) + (numInterfaces - 1) * sizeof(DWORD));
437   if (ret) {
438     ret->numAllocated = numInterfaces;
439     addMapEntriesToIndexTable(ret, gNonLoopbackInterfaceMap);
440     addMapEntriesToIndexTable(ret, gLoopbackInterfaceMap);
441   }
442   LeaveCriticalSection(&mapCS);
443   return ret;
444 }
445
446 InterfaceIndexTable *getNonLoopbackInterfaceIndexTable(void)
447 {
448   DWORD numInterfaces;
449   InterfaceIndexTable *ret;
450
451   EnterCriticalSection(&mapCS);
452   numInterfaces = getNumNonLoopbackInterfaces();
453   ret = (InterfaceIndexTable *)calloc(1,
454    sizeof(InterfaceIndexTable) + (numInterfaces - 1) * sizeof(DWORD));
455   if (ret) {
456     ret->numAllocated = numInterfaces;
457     addMapEntriesToIndexTable(ret, gNonLoopbackInterfaceMap);
458   }
459   LeaveCriticalSection(&mapCS);
460   return ret;
461 }
462
463 DWORD getInterfaceIPAddrByName(const char *name)
464 {
465   DWORD ret = INADDR_ANY;
466
467   if (name) {
468     int fd = socket(PF_INET, SOCK_DGRAM, 0);
469
470     if (fd != -1) {
471       struct ifreq ifr;
472
473       strncpy(ifr.ifr_name, name, IFNAMSIZ);
474       ifr.ifr_name[IFNAMSIZ-1] = '\0';
475       if (ioctl(fd, SIOCGIFADDR, &ifr) == 0)
476         memcpy(&ret, ifr.ifr_addr.sa_data + 2, sizeof(DWORD));
477       close(fd);
478     }
479   }
480   return ret;
481 }
482
483 DWORD getInterfaceIPAddrByIndex(DWORD index)
484 {
485   DWORD ret;
486   const char *name = getInterfaceNameByIndex(index);
487
488   if (name)
489     ret = getInterfaceIPAddrByName(name);
490   else
491     ret = INADDR_ANY;
492   return ret;
493 }
494
495 DWORD getInterfaceBCastAddrByName(const char *name)
496 {
497   DWORD ret = INADDR_ANY;
498
499   if (name) {
500     int fd = socket(PF_INET, SOCK_DGRAM, 0);
501
502     if (fd != -1) {
503       struct ifreq ifr;
504
505       strncpy(ifr.ifr_name, name, IFNAMSIZ);
506       ifr.ifr_name[IFNAMSIZ-1] = '\0';
507       if (ioctl(fd, SIOCGIFBRDADDR, &ifr) == 0)
508         memcpy(&ret, ifr.ifr_addr.sa_data + 2, sizeof(DWORD));
509       close(fd);
510     }
511   }
512   return ret;
513 }
514
515 DWORD getInterfaceBCastAddrByIndex(DWORD index)
516 {
517   DWORD ret;
518   const char *name = getInterfaceNameByIndex(index);
519
520   if (name)
521     ret = getInterfaceBCastAddrByName(name);
522   else
523     ret = INADDR_ANY;
524   return ret;
525 }
526
527 DWORD getInterfaceMaskByName(const char *name)
528 {
529   DWORD ret = INADDR_NONE;
530
531   if (name) {
532     int fd = socket(PF_INET, SOCK_DGRAM, 0);
533
534     if (fd != -1) {
535       struct ifreq ifr;
536
537       strncpy(ifr.ifr_name, name, IFNAMSIZ);
538       ifr.ifr_name[IFNAMSIZ-1] = '\0';
539       if (ioctl(fd, SIOCGIFNETMASK, &ifr) == 0)
540         memcpy(&ret, ifr.ifr_addr.sa_data + 2, sizeof(DWORD));
541       close(fd);
542     }
543   }
544   return ret;
545 }
546
547 DWORD getInterfaceMaskByIndex(DWORD index)
548 {
549   DWORD ret;
550   const char *name = getInterfaceNameByIndex(index);
551
552   if (name)
553     ret = getInterfaceMaskByName(name);
554   else
555     ret = INADDR_NONE;
556   return ret;
557 }
558
559 #if defined (SIOCGIFHWADDR)
560 DWORD getInterfacePhysicalByName(const char *name, PDWORD len, PBYTE addr,
561  PDWORD type)
562 {
563   DWORD ret;
564   int fd;
565
566   if (!name || !len || !addr || !type)
567     return ERROR_INVALID_PARAMETER;
568
569   fd = socket(PF_INET, SOCK_DGRAM, 0);
570   if (fd != -1) {
571     struct ifreq ifr;
572
573     memset(&ifr, 0, sizeof(struct ifreq));
574     strncpy(ifr.ifr_name, name, IFNAMSIZ);
575     ifr.ifr_name[IFNAMSIZ-1] = '\0';
576     if ((ioctl(fd, SIOCGIFHWADDR, &ifr)))
577       ret = ERROR_INVALID_DATA;
578     else {
579       int addrLen;
580
581       switch (ifr.ifr_hwaddr.sa_family)
582       {
583 #ifdef ARPHRD_LOOPBACK
584         case ARPHRD_LOOPBACK:
585           addrLen = 0;
586           *type = MIB_IF_TYPE_LOOPBACK;
587           break;
588 #endif
589 #ifdef ARPHRD_ETHER
590         case ARPHRD_ETHER:
591           addrLen = ETH_ALEN;
592           *type = MIB_IF_TYPE_ETHERNET;
593           break;
594 #endif
595 #ifdef ARPHRD_FDDI
596         case ARPHRD_FDDI:
597           addrLen = ETH_ALEN;
598           *type = MIB_IF_TYPE_FDDI;
599           break;
600 #endif
601 #ifdef ARPHRD_IEEE802
602         case ARPHRD_IEEE802: /* 802.2 Ethernet && Token Ring, guess TR? */
603           addrLen = ETH_ALEN;
604           *type = MIB_IF_TYPE_TOKENRING;
605           break;
606 #endif
607 #ifdef ARPHRD_IEEE802_TR
608         case ARPHRD_IEEE802_TR: /* also Token Ring? */
609           addrLen = ETH_ALEN;
610           *type = MIB_IF_TYPE_TOKENRING;
611           break;
612 #endif
613 #ifdef ARPHRD_SLIP
614         case ARPHRD_SLIP:
615           addrLen = 0;
616           *type = MIB_IF_TYPE_SLIP;
617           break;
618 #endif
619 #ifdef ARPHRD_PPP
620         case ARPHRD_PPP:
621           addrLen = 0;
622           *type = MIB_IF_TYPE_PPP;
623           break;
624 #endif
625         default:
626           addrLen = min(MAX_INTERFACE_PHYSADDR, sizeof(ifr.ifr_hwaddr.sa_data));
627           *type = MIB_IF_TYPE_OTHER;
628       }
629       if (addrLen > *len) {
630         ret = ERROR_INSUFFICIENT_BUFFER;
631         *len = addrLen;
632       }
633       else {
634         if (addrLen > 0)
635           memcpy(addr, ifr.ifr_hwaddr.sa_data, addrLen);
636         /* zero out remaining bytes for broken implementations */
637         memset(addr + addrLen, 0, *len - addrLen);
638         *len = addrLen;
639         ret = NO_ERROR;
640       }
641     }
642     close(fd);
643   }
644   else
645     ret = ERROR_NO_MORE_FILES;
646   return ret;
647 }
648 #elif defined (SIOCGARP)
649 DWORD getInterfacePhysicalByName(const char *name, PDWORD len, PBYTE addr,
650  PDWORD type)
651 {
652   DWORD ret;
653   int fd;
654
655   if (!name || !len || !addr || !type)
656     return ERROR_INVALID_PARAMETER;
657
658   fd = socket(PF_INET, SOCK_DGRAM, 0);
659   if (fd != -1) {
660     if (isLoopbackInterface(fd, name)) {
661       *type = MIB_IF_TYPE_LOOPBACK;
662       memset(addr, 0, *len);
663       *len = 0;
664       ret=NOERROR;
665     }
666     else {
667       struct arpreq arp;
668       struct sockaddr_in *saddr;
669
670       memset(&arp, 0, sizeof(struct arpreq));
671       arp.arp_pa.sa_family = AF_INET;
672       saddr = (struct sockaddr_in *)&arp; /* proto addr is first member */
673       saddr->sin_family = AF_INET;
674       saddr->sin_addr.s_addr = getInterfaceIPAddrByName(name);
675       if ((ioctl(fd, SIOCGARP, &arp)))
676         ret = ERROR_INVALID_DATA;
677       else {
678         /* FIXME:  heh:  who said it was ethernet? */
679         int addrLen = ETH_ALEN;
680
681         if (addrLen > *len) {
682           ret = ERROR_INSUFFICIENT_BUFFER;
683           *len = addrLen;
684         }
685         else {
686           if (addrLen > 0)
687             memcpy(addr, &arp.arp_ha.sa_data[0], addrLen);
688           /* zero out remaining bytes for broken implementations */
689           memset(addr + addrLen, 0, *len - addrLen);
690           *len = addrLen;
691           *type = MIB_IF_TYPE_ETHERNET;
692           ret = NO_ERROR;
693         }
694       }
695     }
696     close(fd);
697   }
698     else
699       ret = ERROR_NO_MORE_FILES;
700
701   return ret;
702 }
703 #elif defined (HAVE_SYS_SYSCTL_H) && defined (HAVE_NET_IF_DL_H)
704 DWORD getInterfacePhysicalByName(const char *name, PDWORD len, PBYTE addr,
705  PDWORD type)
706 {
707   DWORD ret;
708   struct if_msghdr *ifm;
709   struct sockaddr_dl *sdl;
710   u_char *p, *buf;
711   size_t mibLen;
712   int mib[] = { CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0 };
713   int addrLen;
714   BOOL found = FALSE;
715
716   if (!name || !len || !addr || !type)
717     return ERROR_INVALID_PARAMETER;
718
719   if (sysctl(mib, 6, NULL, &mibLen, NULL, 0) < 0)
720     return ERROR_NO_MORE_FILES;
721
722   buf = (u_char *)malloc(mibLen);
723   if (!buf)
724     return ERROR_NOT_ENOUGH_MEMORY;
725
726   if (sysctl(mib, 6, buf, &mibLen, NULL, 0) < 0) {
727     free(buf);
728     return ERROR_NO_MORE_FILES;
729   }
730
731   ret = ERROR_INVALID_DATA;
732   for (p = buf; !found && p < buf + mibLen; p += ifm->ifm_msglen) {
733     ifm = (struct if_msghdr *)p;
734     sdl = (struct sockaddr_dl *)(ifm + 1);
735
736     if (ifm->ifm_type != RTM_IFINFO || (ifm->ifm_addrs & RTA_IFP) == 0)
737       continue;
738
739     if (sdl->sdl_family != AF_LINK || sdl->sdl_nlen == 0 ||
740      memcmp(sdl->sdl_data, name, max(sdl->sdl_nlen, strlen(name))) != 0)
741       continue;
742
743     found = TRUE;
744     addrLen = min(MAX_INTERFACE_PHYSADDR, sdl->sdl_alen);
745     if (addrLen > *len) {
746       ret = ERROR_INSUFFICIENT_BUFFER;
747       *len = addrLen;
748     }
749     else {
750       if (addrLen > 0)
751         memcpy(addr, LLADDR(sdl), addrLen);
752       /* zero out remaining bytes for broken implementations */
753       memset(addr + addrLen, 0, *len - addrLen);
754       *len = addrLen;
755 #if defined(HAVE_NET_IF_TYPES_H)
756       switch (sdl->sdl_type)
757       {
758         case IFT_ETHER:
759           *type = MIB_IF_TYPE_ETHERNET;
760           break;
761         case IFT_FDDI:
762           *type = MIB_IF_TYPE_FDDI;
763           break;
764         case IFT_ISO88024: /* Token Bus */
765           *type = MIB_IF_TYPE_TOKENRING;
766           break;
767         case IFT_ISO88025: /* Token Ring */
768           *type = MIB_IF_TYPE_TOKENRING;
769           break;
770         case IFT_PPP:
771           *type = MIB_IF_TYPE_PPP;
772           break;
773         case IFT_SLIP:
774           *type = MIB_IF_TYPE_SLIP;
775           break;
776         case IFT_LOOP:
777           *type = MIB_IF_TYPE_LOOPBACK;
778           break;
779         default:
780           *type = MIB_IF_TYPE_OTHER;
781       }
782 #else
783       /* default if we don't know */
784       *type = MIB_IF_TYPE_ETHERNET;
785 #endif
786       ret = NO_ERROR;
787     }
788   }
789   free(buf);
790   return ret;
791 }
792 #endif
793
794 DWORD getInterfacePhysicalByIndex(DWORD index, PDWORD len, PBYTE addr,
795  PDWORD type)
796 {
797   const char *name = getInterfaceNameByIndex(index);
798
799   if (name)
800     return getInterfacePhysicalByName(name, len, addr, type);
801   else
802     return ERROR_INVALID_DATA;
803 }
804
805 DWORD getInterfaceMtuByName(const char *name, PDWORD mtu)
806 {
807   DWORD ret;
808   int fd;
809
810   if (!name)
811     return ERROR_INVALID_PARAMETER;
812   if (!mtu)
813     return ERROR_INVALID_PARAMETER;
814
815   fd = socket(PF_INET, SOCK_DGRAM, 0);
816   if (fd != -1) {
817     struct ifreq ifr;
818
819     strncpy(ifr.ifr_name, name, IFNAMSIZ);
820     ifr.ifr_name[IFNAMSIZ-1] = '\0';
821     if ((ioctl(fd, SIOCGIFMTU, &ifr)))
822       ret = ERROR_INVALID_DATA;
823     else {
824 #ifndef __sun
825       *mtu = ifr.ifr_mtu;
826 #else
827       *mtu = ifr.ifr_metric;
828 #endif
829       ret = NO_ERROR;
830     }
831   }
832   else
833     ret = ERROR_NO_MORE_FILES;
834   return ret;
835 }
836
837 DWORD getInterfaceMtuByIndex(DWORD index, PDWORD mtu)
838 {
839   const char *name = getInterfaceNameByIndex(index);
840
841   if (name)
842     return getInterfaceMtuByName(name, mtu);
843   else
844     return ERROR_INVALID_DATA;
845 }
846
847 DWORD getInterfaceStatusByName(const char *name, PDWORD status)
848 {
849   DWORD ret;
850   int fd;
851
852   if (!name)
853     return ERROR_INVALID_PARAMETER;
854   if (!status)
855     return ERROR_INVALID_PARAMETER;
856
857   fd = socket(PF_INET, SOCK_DGRAM, 0);
858   if (fd != -1) {
859     struct ifreq ifr;
860
861     strncpy(ifr.ifr_name, name, IFNAMSIZ);
862     ifr.ifr_name[IFNAMSIZ-1] = '\0';
863     if ((ioctl(fd, SIOCGIFFLAGS, &ifr)))
864       ret = ERROR_INVALID_DATA;
865     else {
866       if (ifr.ifr_flags & IFF_UP)
867         *status = MIB_IF_OPER_STATUS_OPERATIONAL;
868       else
869         *status = MIB_IF_OPER_STATUS_NON_OPERATIONAL;
870       ret = NO_ERROR;
871     }
872   }
873   else
874     ret = ERROR_NO_MORE_FILES;
875   return ret;
876 }
877
878 DWORD getInterfaceStatusByIndex(DWORD index, PDWORD status)
879 {
880   const char *name = getInterfaceNameByIndex(index);
881
882   if (name)
883     return getInterfaceStatusByName(name, status);
884   else
885     return ERROR_INVALID_DATA;
886 }
887
888 DWORD getInterfaceEntryByName(const char *name, PMIB_IFROW entry)
889 {
890   BYTE addr[MAX_INTERFACE_PHYSADDR];
891   DWORD ret, len = sizeof(addr), type;
892
893   if (!name)
894     return ERROR_INVALID_PARAMETER;
895   if (!entry)
896     return ERROR_INVALID_PARAMETER;
897
898   if (getInterfacePhysicalByName(name, &len, addr, &type) == NO_ERROR) {
899     WCHAR *assigner;
900     const char *walker;
901
902     memset(entry, 0, sizeof(MIB_IFROW));
903     for (assigner = entry->wszName, walker = name; *walker; 
904      walker++, assigner++)
905       *assigner = *walker;
906     *assigner = 0;
907     getInterfaceIndexByName(name, &entry->dwIndex);
908     entry->dwPhysAddrLen = len;
909     memcpy(entry->bPhysAddr, addr, len);
910     memset(entry->bPhysAddr + len, 0, sizeof(entry->bPhysAddr) - len);
911     entry->dwType = type;
912     /* FIXME: how to calculate real speed? */
913     getInterfaceMtuByName(name, &entry->dwMtu);
914     /* lie, there's no "administratively down" here */
915     entry->dwAdminStatus = MIB_IF_ADMIN_STATUS_UP;
916     getInterfaceStatusByName(name, &entry->dwOperStatus);
917     /* punt on dwLastChange? */
918     entry->dwDescrLen = min(strlen(name), MAX_INTERFACE_DESCRIPTION - 1);
919     memcpy(entry->bDescr, name, entry->dwDescrLen);
920     entry->bDescr[entry->dwDescrLen] = '\0';
921     entry->dwDescrLen++;
922     ret = NO_ERROR;
923   }
924   else
925     ret = ERROR_INVALID_DATA;
926   return ret;
927 }
928
929 DWORD getInterfaceEntryByIndex(DWORD index, PMIB_IFROW entry)
930 {
931   const char *name = getInterfaceNameByIndex(index);
932
933   if (name)
934     return getInterfaceEntryByName(name, entry);
935   else
936     return ERROR_INVALID_DATA;
937 }
938
939 char *toIPAddressString(unsigned int addr, char string[16])
940 {
941   if (string) {
942     struct in_addr iAddr;
943
944     iAddr.s_addr = addr;
945     /* extra-anal, just to make auditors happy */
946     strncpy(string, inet_ntoa(iAddr), 16);
947     string[16] = '\0';
948   }
949   return string;
950 }