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