| /* Copyright (C) 2003 Juan Lang | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| * | |
| * Implementation notes | |
| * Interface index fun: | |
| * - Windows may rely on an index being cleared in the topmost 8 bits in some | |
| * APIs; see GetFriendlyIfIndex and the mention of "backward compatible" | |
| * indexes. It isn't clear which APIs might fail with non-backward-compatible | |
| * indexes, but I'll keep them bits clear just in case. | |
| * - Even though if_nametoindex and if_indextoname seem to be pretty portable, | |
| * Linux, at any rate, uses the same interface index for all virtual | |
| * interfaces of a real interface as well as for the real interface itself. | |
| * If I used the Linux index as my index, this would break my statement that | |
| * an index is a key, and that an interface has 0 or 1 IP addresses. | |
| * If that behavior were consistent across UNIXen (I don't know), it could | |
| * help me implement multiple IP addresses more in the Windows way. | |
| * I used to assert I could not use UNIX interface indexes as my iphlpapi | |
| * indexes due to restrictions in netapi32 and wsock32, but I have removed | |
| * those restrictions, so using if_nametoindex and if_indextoname rather | |
| * than my current mess would probably be better. | |
| * FIXME: | |
| * - I don't support IPv6 addresses here, since SIOCGIFCONF can't return them | |
| * | |
| * There are three implemened methods for determining the MAC address of an | |
| * interface: | |
| * - a specific IOCTL (Linux) | |
| * - looking in the ARP cache (at least Solaris) | |
| * - using the sysctl interface (FreeBSD and MacOSX) | |
| * Solaris and some others have SIOCGENADDR, but I haven't gotten that to work | |
| * on the Solaris boxes at SourceForge's compile farm, whereas SIOCGARP does. | |
| */ | |
| #include "config.h" | |
| #include <stdarg.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #ifdef HAVE_UNISTD_H | |
| #include <unistd.h> | |
| #endif | |
| #include <sys/types.h> | |
| #ifdef HAVE_SYS_SOCKET_H | |
| #include <sys/socket.h> | |
| #endif | |
| #ifdef HAVE_NETINET_IN_H | |
| #include <netinet/in.h> | |
| #endif | |
| #ifdef HAVE_ARPA_INET_H | |
| #include <arpa/inet.h> | |
| #endif | |
| #ifdef HAVE_NET_IF_H | |
| #include <net/if.h> | |
| #endif | |
| #ifdef HAVE_NET_IF_ARP_H | |
| #include <net/if_arp.h> | |
| #endif | |
| #ifdef HAVE_NET_ROUTE_H | |
| #include <net/route.h> | |
| #endif | |
| #ifdef HAVE_SYS_IOCTL_H | |
| #include <sys/ioctl.h> | |
| #endif | |
| #ifdef HAVE_SYS_SYSCTL_H | |
| #include <sys/sysctl.h> | |
| #endif | |
| #ifdef HAVE_SYS_SOCKIO_H | |
| #include <sys/sockio.h> | |
| #endif | |
| #ifdef HAVE_NET_IF_DL_H | |
| #include <net/if_dl.h> | |
| #endif | |
| #ifdef HAVE_NET_IF_TYPES_H | |
| #include <net/if_types.h> | |
| #endif | |
| #include "ifenum.h" | |
| #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN | |
| #define ifreq_len(ifr) \ | |
| max(sizeof(struct ifreq), sizeof((ifr)->ifr_name)+(ifr)->ifr_addr.sa_len) | |
| #else | |
| #define ifreq_len(ifr) sizeof(struct ifreq) | |
| #endif | |
| #ifndef ETH_ALEN | |
| #define ETH_ALEN 6 | |
| #endif | |
| #ifndef INADDR_NONE | |
| #define INADDR_NONE (~0U) | |
| #endif | |
| #define INITIAL_INTERFACES_ASSUMED 4 | |
| #define INDEX_IS_LOOPBACK 0x00800000 | |
| /* Type declarations */ | |
| typedef struct _InterfaceNameMapEntry { | |
| char name[IFNAMSIZ]; | |
| BOOL inUse; | |
| BOOL usedLastPass; | |
| } InterfaceNameMapEntry; | |
| typedef struct _InterfaceNameMap { | |
| DWORD numInterfaces; | |
| DWORD nextAvailable; | |
| DWORD numAllocated; | |
| InterfaceNameMapEntry table[1]; | |
| } InterfaceNameMap; | |
| /* Global variables */ | |
| static CRITICAL_SECTION mapCS; | |
| static InterfaceNameMap *gNonLoopbackInterfaceMap = NULL; | |
| static InterfaceNameMap *gLoopbackInterfaceMap = NULL; | |
| /* Functions */ | |
| void interfaceMapInit(void) | |
| { | |
| InitializeCriticalSection(&mapCS); | |
| } | |
| void interfaceMapFree(void) | |
| { | |
| DeleteCriticalSection(&mapCS); | |
| HeapFree(GetProcessHeap(), 0, gNonLoopbackInterfaceMap); | |
| HeapFree(GetProcessHeap(), 0, gLoopbackInterfaceMap); | |
| } | |
| /* Sizes the passed-in map to have enough space for numInterfaces interfaces. | |
| * If map is NULL, allocates a new map. If it is not, may reallocate the | |
| * existing map and return a map of increased size. Returns the allocated map, | |
| * or NULL if it could not allocate a map of the requested size. | |
| */ | |
| static InterfaceNameMap *sizeMap(InterfaceNameMap *map, DWORD numInterfaces) | |
| { | |
| if (!map) { | |
| numInterfaces = max(numInterfaces, INITIAL_INTERFACES_ASSUMED); | |
| map = (InterfaceNameMap *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, | |
| sizeof(InterfaceNameMap) + | |
| (numInterfaces - 1) * sizeof(InterfaceNameMapEntry)); | |
| if (map) | |
| map->numAllocated = numInterfaces; | |
| } | |
| else { | |
| if (map->numAllocated < numInterfaces) { | |
| map = (InterfaceNameMap *)HeapReAlloc(GetProcessHeap(), 0, map, | |
| sizeof(InterfaceNameMap) + | |
| (numInterfaces - 1) * sizeof(InterfaceNameMapEntry)); | |
| if (map) | |
| memset(&map->table[map->numAllocated], 0, | |
| (numInterfaces - map->numAllocated) * sizeof(InterfaceNameMapEntry)); | |
| } | |
| } | |
| return map; | |
| } | |
| static int isLoopbackInterface(int fd, const char *name) | |
| { | |
| int ret = 0; | |
| if (name) { | |
| struct ifreq ifr; | |
| strncpy(ifr.ifr_name, name, IFNAMSIZ); | |
| ifr.ifr_name[IFNAMSIZ-1] = '\0'; | |
| if (ioctl(fd, SIOCGIFFLAGS, &ifr) == 0) | |
| ret = ifr.ifr_flags & IFF_LOOPBACK; | |
| } | |
| return ret; | |
| } | |
| static void countInterfaces(int fd, caddr_t buf, size_t len) | |
| { | |
| caddr_t ifPtr = buf; | |
| DWORD numNonLoopbackInterfaces = 0, numLoopbackInterfaces = 0; | |
| while (ifPtr && ifPtr < buf + len) { | |
| struct ifreq *ifr = (struct ifreq *)ifPtr; | |
| if (isLoopbackInterface(fd, ifr->ifr_name)) | |
| numLoopbackInterfaces++; | |
| else | |
| numNonLoopbackInterfaces++; | |
| ifPtr += ifreq_len(ifr); | |
| } | |
| gNonLoopbackInterfaceMap = sizeMap(gNonLoopbackInterfaceMap, | |
| numNonLoopbackInterfaces); | |
| gLoopbackInterfaceMap = sizeMap(gLoopbackInterfaceMap, | |
| numLoopbackInterfaces); | |
| } | |
| /* Stores the name in the given map, and increments the map's numInterfaces | |
| * member if stored successfully. Will store in the same slot as previously if | |
| * usedLastPass is set, otherwise will store in a new slot. | |
| * Assumes map and name are not NULL, and the usedLastPass flag is set | |
| * correctly for each entry in the map, and that map->numInterfaces < | |
| * map->numAllocated. | |
| * FIXME: this is kind of expensive, doing a linear scan of the map with a | |
| * string comparison of each entry to find the old slot. | |
| */ | |
| static void storeInterfaceInMap(InterfaceNameMap *map, const char *name) | |
| { | |
| if (map && name) { | |
| DWORD ndx; | |
| BOOL stored = FALSE; | |
| /* look for previous slot, mark in use if so */ | |
| for (ndx = 0; !stored && ndx < map->nextAvailable; ndx++) { | |
| if (map->table[ndx].usedLastPass && !strncmp(map->table[ndx].name, name, | |
| sizeof(map->table[ndx].name))) { | |
| map->table[ndx].inUse = TRUE; | |
| stored = TRUE; | |
| } | |
| } | |
| /* look for new slot */ | |
| for (ndx = 0; !stored && ndx < map->numAllocated; ndx++) { | |
| if (!map->table[ndx].inUse) { | |
| strncpy(map->table[ndx].name, name, IFNAMSIZ); | |
| map->table[ndx].name[IFNAMSIZ-1] = '\0'; | |
| map->table[ndx].inUse = TRUE; | |
| stored = TRUE; | |
| if (ndx >= map->nextAvailable) | |
| map->nextAvailable = ndx + 1; | |
| } | |
| } | |
| if (stored) | |
| map->numInterfaces++; | |
| } | |
| } | |
| /* Sets all used entries' usedLastPass flag to their inUse flag, clears | |
| * their inUse flag, and clears their numInterfaces member. | |
| */ | |
| static void markOldInterfaces(InterfaceNameMap *map) | |
| { | |
| if (map) { | |
| DWORD ndx; | |
| map->numInterfaces = 0; | |
| for (ndx = 0; ndx < map->nextAvailable; ndx++) { | |
| map->table[ndx].usedLastPass = map->table[ndx].inUse; | |
| map->table[ndx].inUse = FALSE; | |
| } | |
| } | |
| } | |
| static void classifyInterfaces(int fd, caddr_t buf, size_t len) | |
| { | |
| caddr_t ifPtr = buf; | |
| markOldInterfaces(gNonLoopbackInterfaceMap); | |
| markOldInterfaces(gLoopbackInterfaceMap); | |
| while (ifPtr && ifPtr < buf + len) { | |
| struct ifreq *ifr = (struct ifreq *)ifPtr; | |
| if (ifr->ifr_addr.sa_family == AF_INET) { | |
| if (isLoopbackInterface(fd, ifr->ifr_name)) | |
| storeInterfaceInMap(gLoopbackInterfaceMap, ifr->ifr_name); | |
| else | |
| storeInterfaceInMap(gNonLoopbackInterfaceMap, ifr->ifr_name); | |
| } | |
| ifPtr += ifreq_len(ifr); | |
| } | |
| } | |
| static void enumerateInterfaces(void) | |
| { | |
| int fd; | |
| fd = socket(PF_INET, SOCK_DGRAM, 0); | |
| if (fd != -1) { | |
| int ret, guessedNumInterfaces; | |
| struct ifconf ifc; | |
| /* try to avoid silly heap action by starting with the right size buffer */ | |
| guessedNumInterfaces = 0; | |
| if (gNonLoopbackInterfaceMap) | |
| guessedNumInterfaces += gNonLoopbackInterfaceMap->numInterfaces; | |
| if (gLoopbackInterfaceMap) | |
| guessedNumInterfaces += gLoopbackInterfaceMap->numInterfaces; | |
| ret = 0; | |
| memset(&ifc, 0, sizeof(ifc)); | |
| /* there is no way to know the interface count beforehand, | |
| so we need to loop again and again upping our max each time | |
| until returned < max */ | |
| do { | |
| if (guessedNumInterfaces == 0) | |
| guessedNumInterfaces = INITIAL_INTERFACES_ASSUMED; | |
| else | |
| guessedNumInterfaces *= 2; | |
| HeapFree(GetProcessHeap(), 0, ifc.ifc_buf); | |
| ifc.ifc_len = sizeof(struct ifreq) * guessedNumInterfaces; | |
| ifc.ifc_buf = (char *)HeapAlloc(GetProcessHeap(), 0, ifc.ifc_len); | |
| ret = ioctl(fd, SIOCGIFCONF, &ifc); | |
| } while (ret == 0 && | |
| ifc.ifc_len == (sizeof(struct ifreq) * guessedNumInterfaces)); | |
| if (ret == 0) { | |
| EnterCriticalSection(&mapCS); | |
| countInterfaces(fd, ifc.ifc_buf, ifc.ifc_len); | |
| classifyInterfaces(fd, ifc.ifc_buf, ifc.ifc_len); | |
| LeaveCriticalSection(&mapCS); | |
| } | |
| HeapFree(GetProcessHeap(), 0, ifc.ifc_buf); | |
| close(fd); | |
| } | |
| } | |
| DWORD getNumNonLoopbackInterfaces(void) | |
| { | |
| enumerateInterfaces(); | |
| return gNonLoopbackInterfaceMap ? gNonLoopbackInterfaceMap->numInterfaces : 0; | |
| } | |
| DWORD getNumInterfaces(void) | |
| { | |
| DWORD ret = getNumNonLoopbackInterfaces(); | |
| ret += gLoopbackInterfaceMap ? gLoopbackInterfaceMap->numInterfaces : 0; | |
| return ret; | |
| } | |
| const char *getInterfaceNameByIndex(DWORD index) | |
| { | |
| DWORD realIndex; | |
| InterfaceNameMap *map; | |
| const char *ret = NULL; | |
| EnterCriticalSection(&mapCS); | |
| if (index & INDEX_IS_LOOPBACK) { | |
| realIndex = index ^ INDEX_IS_LOOPBACK; | |
| map = gLoopbackInterfaceMap; | |
| } | |
| else { | |
| realIndex = index; | |
| map = gNonLoopbackInterfaceMap; | |
| } | |
| if (map && realIndex < map->nextAvailable) | |
| ret = map->table[realIndex].name; | |
| LeaveCriticalSection(&mapCS); | |
| return ret; | |
| } | |
| DWORD getInterfaceIndexByName(const char *name, PDWORD index) | |
| { | |
| DWORD ndx, ret; | |
| BOOL found = FALSE; | |
| if (!name) | |
| return ERROR_INVALID_PARAMETER; | |
| if (!index) | |
| return ERROR_INVALID_PARAMETER; | |
| EnterCriticalSection(&mapCS); | |
| for (ndx = 0; !found && gNonLoopbackInterfaceMap && | |
| ndx < gNonLoopbackInterfaceMap->nextAvailable; ndx++) | |
| if (!strncmp(gNonLoopbackInterfaceMap->table[ndx].name, name, IFNAMSIZ)) { | |
| found = TRUE; | |
| *index = ndx; | |
| } | |
| for (ndx = 0; !found && gLoopbackInterfaceMap && | |
| ndx < gLoopbackInterfaceMap->nextAvailable; ndx++) | |
| if (!strncmp(gLoopbackInterfaceMap->table[ndx].name, name, IFNAMSIZ)) { | |
| found = TRUE; | |
| *index = ndx | INDEX_IS_LOOPBACK; | |
| } | |
| LeaveCriticalSection(&mapCS); | |
| if (found) | |
| ret = NO_ERROR; | |
| else | |
| ret = ERROR_INVALID_DATA; | |
| return ret; | |
| } | |
| static void addMapEntriesToIndexTable(InterfaceIndexTable *table, | |
| const InterfaceNameMap *map) | |
| { | |
| if (table && map) { | |
| DWORD ndx; | |
| for (ndx = 0; ndx < map->nextAvailable && | |
| table->numIndexes < table->numAllocated; ndx++) | |
| if (map->table[ndx].inUse) { | |
| DWORD externalNdx = ndx; | |
| if (map == gLoopbackInterfaceMap) | |
| externalNdx |= INDEX_IS_LOOPBACK; | |
| table->indexes[table->numIndexes++] = externalNdx; | |
| } | |
| } | |
| } | |
| InterfaceIndexTable *getInterfaceIndexTable(void) | |
| { | |
| DWORD numInterfaces; | |
| InterfaceIndexTable *ret; | |
| EnterCriticalSection(&mapCS); | |
| numInterfaces = getNumInterfaces(); | |
| ret = (InterfaceIndexTable *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, | |
| sizeof(InterfaceIndexTable) + (numInterfaces - 1) * sizeof(DWORD)); | |
| if (ret) { | |
| ret->numAllocated = numInterfaces; | |
| addMapEntriesToIndexTable(ret, gNonLoopbackInterfaceMap); | |
| addMapEntriesToIndexTable(ret, gLoopbackInterfaceMap); | |
| } | |
| LeaveCriticalSection(&mapCS); | |
| return ret; | |
| } | |
| InterfaceIndexTable *getNonLoopbackInterfaceIndexTable(void) | |
| { | |
| DWORD numInterfaces; | |
| InterfaceIndexTable *ret; | |
| EnterCriticalSection(&mapCS); | |
| numInterfaces = getNumNonLoopbackInterfaces(); | |
| ret = (InterfaceIndexTable *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, | |
| sizeof(InterfaceIndexTable) + (numInterfaces - 1) * sizeof(DWORD)); | |
| if (ret) { | |
| ret->numAllocated = numInterfaces; | |
| addMapEntriesToIndexTable(ret, gNonLoopbackInterfaceMap); | |
| } | |
| LeaveCriticalSection(&mapCS); | |
| return ret; | |
| } | |
| DWORD getInterfaceIPAddrByName(const char *name) | |
| { | |
| DWORD ret = INADDR_ANY; | |
| if (name) { | |
| int fd = socket(PF_INET, SOCK_DGRAM, 0); | |
| if (fd != -1) { | |
| struct ifreq ifr; | |
| strncpy(ifr.ifr_name, name, IFNAMSIZ); | |
| ifr.ifr_name[IFNAMSIZ-1] = '\0'; | |
| if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) | |
| memcpy(&ret, ifr.ifr_addr.sa_data + 2, sizeof(DWORD)); | |
| close(fd); | |
| } | |
| } | |
| return ret; | |
| } | |
| DWORD getInterfaceIPAddrByIndex(DWORD index) | |
| { | |
| DWORD ret; | |
| const char *name = getInterfaceNameByIndex(index); | |
| if (name) | |
| ret = getInterfaceIPAddrByName(name); | |
| else | |
| ret = INADDR_ANY; | |
| return ret; | |
| } | |
| DWORD getInterfaceBCastAddrByName(const char *name) | |
| { | |
| DWORD ret = INADDR_ANY; | |
| if (name) { | |
| int fd = socket(PF_INET, SOCK_DGRAM, 0); | |
| if (fd != -1) { | |
| struct ifreq ifr; | |
| strncpy(ifr.ifr_name, name, IFNAMSIZ); | |
| ifr.ifr_name[IFNAMSIZ-1] = '\0'; | |
| if (ioctl(fd, SIOCGIFBRDADDR, &ifr) == 0) | |
| memcpy(&ret, ifr.ifr_addr.sa_data + 2, sizeof(DWORD)); | |
| close(fd); | |
| } | |
| } | |
| return ret; | |
| } | |
| DWORD getInterfaceBCastAddrByIndex(DWORD index) | |
| { | |
| DWORD ret; | |
| const char *name = getInterfaceNameByIndex(index); | |
| if (name) | |
| ret = getInterfaceBCastAddrByName(name); | |
| else | |
| ret = INADDR_ANY; | |
| return ret; | |
| } | |
| DWORD getInterfaceMaskByName(const char *name) | |
| { | |
| DWORD ret = INADDR_NONE; | |
| if (name) { | |
| int fd = socket(PF_INET, SOCK_DGRAM, 0); | |
| if (fd != -1) { | |
| struct ifreq ifr; | |
| strncpy(ifr.ifr_name, name, IFNAMSIZ); | |
| ifr.ifr_name[IFNAMSIZ-1] = '\0'; | |
| if (ioctl(fd, SIOCGIFNETMASK, &ifr) == 0) | |
| memcpy(&ret, ifr.ifr_addr.sa_data + 2, sizeof(DWORD)); | |
| close(fd); | |
| } | |
| } | |
| return ret; | |
| } | |
| DWORD getInterfaceMaskByIndex(DWORD index) | |
| { | |
| DWORD ret; | |
| const char *name = getInterfaceNameByIndex(index); | |
| if (name) | |
| ret = getInterfaceMaskByName(name); | |
| else | |
| ret = INADDR_NONE; | |
| return ret; | |
| } | |
| #if defined (SIOCGIFHWADDR) | |
| DWORD getInterfacePhysicalByName(const char *name, PDWORD len, PBYTE addr, | |
| PDWORD type) | |
| { | |
| DWORD ret; | |
| int fd; | |
| if (!name || !len || !addr || !type) | |
| return ERROR_INVALID_PARAMETER; | |
| fd = socket(PF_INET, SOCK_DGRAM, 0); | |
| if (fd != -1) { | |
| struct ifreq ifr; | |
| memset(&ifr, 0, sizeof(struct ifreq)); | |
| strncpy(ifr.ifr_name, name, IFNAMSIZ); | |
| ifr.ifr_name[IFNAMSIZ-1] = '\0'; | |
| if ((ioctl(fd, SIOCGIFHWADDR, &ifr))) | |
| ret = ERROR_INVALID_DATA; | |
| else { | |
| unsigned int addrLen; | |
| switch (ifr.ifr_hwaddr.sa_family) | |
| { | |
| #ifdef ARPHRD_LOOPBACK | |
| case ARPHRD_LOOPBACK: | |
| addrLen = 0; | |
| *type = MIB_IF_TYPE_LOOPBACK; | |
| break; | |
| #endif | |
| #ifdef ARPHRD_ETHER | |
| case ARPHRD_ETHER: | |
| addrLen = ETH_ALEN; | |
| *type = MIB_IF_TYPE_ETHERNET; | |
| break; | |
| #endif | |
| #ifdef ARPHRD_FDDI | |
| case ARPHRD_FDDI: | |
| addrLen = ETH_ALEN; | |
| *type = MIB_IF_TYPE_FDDI; | |
| break; | |
| #endif | |
| #ifdef ARPHRD_IEEE802 | |
| case ARPHRD_IEEE802: /* 802.2 Ethernet && Token Ring, guess TR? */ | |
| addrLen = ETH_ALEN; | |
| *type = MIB_IF_TYPE_TOKENRING; | |
| break; | |
| #endif | |
| #ifdef ARPHRD_IEEE802_TR | |
| case ARPHRD_IEEE802_TR: /* also Token Ring? */ | |
| addrLen = ETH_ALEN; | |
| *type = MIB_IF_TYPE_TOKENRING; | |
| break; | |
| #endif | |
| #ifdef ARPHRD_SLIP | |
| case ARPHRD_SLIP: | |
| addrLen = 0; | |
| *type = MIB_IF_TYPE_SLIP; | |
| break; | |
| #endif | |
| #ifdef ARPHRD_PPP | |
| case ARPHRD_PPP: | |
| addrLen = 0; | |
| *type = MIB_IF_TYPE_PPP; | |
| break; | |
| #endif | |
| default: | |
| addrLen = min(MAX_INTERFACE_PHYSADDR, sizeof(ifr.ifr_hwaddr.sa_data)); | |
| *type = MIB_IF_TYPE_OTHER; | |
| } | |
| if (addrLen > *len) { | |
| ret = ERROR_INSUFFICIENT_BUFFER; | |
| *len = addrLen; | |
| } | |
| else { | |
| if (addrLen > 0) | |
| memcpy(addr, ifr.ifr_hwaddr.sa_data, addrLen); | |
| /* zero out remaining bytes for broken implementations */ | |
| memset(addr + addrLen, 0, *len - addrLen); | |
| *len = addrLen; | |
| ret = NO_ERROR; | |
| } | |
| } | |
| close(fd); | |
| } | |
| else | |
| ret = ERROR_NO_MORE_FILES; | |
| return ret; | |
| } | |
| #elif defined (SIOCGARP) | |
| DWORD getInterfacePhysicalByName(const char *name, PDWORD len, PBYTE addr, | |
| PDWORD type) | |
| { | |
| DWORD ret; | |
| int fd; | |
| if (!name || !len || !addr || !type) | |
| return ERROR_INVALID_PARAMETER; | |
| fd = socket(PF_INET, SOCK_DGRAM, 0); | |
| if (fd != -1) { | |
| if (isLoopbackInterface(fd, name)) { | |
| *type = MIB_IF_TYPE_LOOPBACK; | |
| memset(addr, 0, *len); | |
| *len = 0; | |
| ret=NOERROR; | |
| } | |
| else { | |
| struct arpreq arp; | |
| struct sockaddr_in *saddr; | |
| memset(&arp, 0, sizeof(struct arpreq)); | |
| arp.arp_pa.sa_family = AF_INET; | |
| saddr = (struct sockaddr_in *)&arp; /* proto addr is first member */ | |
| saddr->sin_family = AF_INET; | |
| saddr->sin_addr.s_addr = getInterfaceIPAddrByName(name); | |
| if ((ioctl(fd, SIOCGARP, &arp))) | |
| ret = ERROR_INVALID_DATA; | |
| else { | |
| /* FIXME: heh: who said it was ethernet? */ | |
| int addrLen = ETH_ALEN; | |
| if (addrLen > *len) { | |
| ret = ERROR_INSUFFICIENT_BUFFER; | |
| *len = addrLen; | |
| } | |
| else { | |
| if (addrLen > 0) | |
| memcpy(addr, &arp.arp_ha.sa_data[0], addrLen); | |
| /* zero out remaining bytes for broken implementations */ | |
| memset(addr + addrLen, 0, *len - addrLen); | |
| *len = addrLen; | |
| *type = MIB_IF_TYPE_ETHERNET; | |
| ret = NO_ERROR; | |
| } | |
| } | |
| } | |
| close(fd); | |
| } | |
| else | |
| ret = ERROR_NO_MORE_FILES; | |
| return ret; | |
| } | |
| #elif defined (HAVE_SYS_SYSCTL_H) && defined (HAVE_NET_IF_DL_H) | |
| DWORD getInterfacePhysicalByName(const char *name, PDWORD len, PBYTE addr, | |
| PDWORD type) | |
| { | |
| DWORD ret; | |
| struct if_msghdr *ifm; | |
| struct sockaddr_dl *sdl; | |
| u_char *p, *buf; | |
| size_t mibLen; | |
| int mib[] = { CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0 }; | |
| int addrLen; | |
| BOOL found = FALSE; | |
| if (!name || !len || !addr || !type) | |
| return ERROR_INVALID_PARAMETER; | |
| if (sysctl(mib, 6, NULL, &mibLen, NULL, 0) < 0) | |
| return ERROR_NO_MORE_FILES; | |
| buf = (u_char *)HeapAlloc(GetProcessHeap(), 0, mibLen); | |
| if (!buf) | |
| return ERROR_NOT_ENOUGH_MEMORY; | |
| if (sysctl(mib, 6, buf, &mibLen, NULL, 0) < 0) { | |
| HeapFree(GetProcessHeap(), 0, buf); | |
| return ERROR_NO_MORE_FILES; | |
| } | |
| ret = ERROR_INVALID_DATA; | |
| for (p = buf; !found && p < buf + mibLen; p += ifm->ifm_msglen) { | |
| ifm = (struct if_msghdr *)p; | |
| sdl = (struct sockaddr_dl *)(ifm + 1); | |
| if (ifm->ifm_type != RTM_IFINFO || (ifm->ifm_addrs & RTA_IFP) == 0) | |
| continue; | |
| if (sdl->sdl_family != AF_LINK || sdl->sdl_nlen == 0 || | |
| memcmp(sdl->sdl_data, name, max(sdl->sdl_nlen, strlen(name))) != 0) | |
| continue; | |
| found = TRUE; | |
| addrLen = min(MAX_INTERFACE_PHYSADDR, sdl->sdl_alen); | |
| if (addrLen > *len) { | |
| ret = ERROR_INSUFFICIENT_BUFFER; | |
| *len = addrLen; | |
| } | |
| else { | |
| if (addrLen > 0) | |
| memcpy(addr, LLADDR(sdl), addrLen); | |
| /* zero out remaining bytes for broken implementations */ | |
| memset(addr + addrLen, 0, *len - addrLen); | |
| *len = addrLen; | |
| #if defined(HAVE_NET_IF_TYPES_H) | |
| switch (sdl->sdl_type) | |
| { | |
| case IFT_ETHER: | |
| *type = MIB_IF_TYPE_ETHERNET; | |
| break; | |
| case IFT_FDDI: | |
| *type = MIB_IF_TYPE_FDDI; | |
| break; | |
| case IFT_ISO88024: /* Token Bus */ | |
| *type = MIB_IF_TYPE_TOKENRING; | |
| break; | |
| case IFT_ISO88025: /* Token Ring */ | |
| *type = MIB_IF_TYPE_TOKENRING; | |
| break; | |
| case IFT_PPP: | |
| *type = MIB_IF_TYPE_PPP; | |
| break; | |
| case IFT_SLIP: | |
| *type = MIB_IF_TYPE_SLIP; | |
| break; | |
| case IFT_LOOP: | |
| *type = MIB_IF_TYPE_LOOPBACK; | |
| break; | |
| default: | |
| *type = MIB_IF_TYPE_OTHER; | |
| } | |
| #else | |
| /* default if we don't know */ | |
| *type = MIB_IF_TYPE_ETHERNET; | |
| #endif | |
| ret = NO_ERROR; | |
| } | |
| } | |
| HeapFree(GetProcessHeap(), 0, buf); | |
| return ret; | |
| } | |
| #endif | |
| DWORD getInterfacePhysicalByIndex(DWORD index, PDWORD len, PBYTE addr, | |
| PDWORD type) | |
| { | |
| const char *name = getInterfaceNameByIndex(index); | |
| if (name) | |
| return getInterfacePhysicalByName(name, len, addr, type); | |
| else | |
| return ERROR_INVALID_DATA; | |
| } | |
| DWORD getInterfaceMtuByName(const char *name, PDWORD mtu) | |
| { | |
| DWORD ret; | |
| int fd; | |
| if (!name) | |
| return ERROR_INVALID_PARAMETER; | |
| if (!mtu) | |
| return ERROR_INVALID_PARAMETER; | |
| fd = socket(PF_INET, SOCK_DGRAM, 0); | |
| if (fd != -1) { | |
| struct ifreq ifr; | |
| strncpy(ifr.ifr_name, name, IFNAMSIZ); | |
| ifr.ifr_name[IFNAMSIZ-1] = '\0'; | |
| if ((ioctl(fd, SIOCGIFMTU, &ifr))) | |
| ret = ERROR_INVALID_DATA; | |
| else { | |
| #ifndef __sun | |
| *mtu = ifr.ifr_mtu; | |
| #else | |
| *mtu = ifr.ifr_metric; | |
| #endif | |
| ret = NO_ERROR; | |
| } | |
| } | |
| else | |
| ret = ERROR_NO_MORE_FILES; | |
| return ret; | |
| } | |
| DWORD getInterfaceMtuByIndex(DWORD index, PDWORD mtu) | |
| { | |
| const char *name = getInterfaceNameByIndex(index); | |
| if (name) | |
| return getInterfaceMtuByName(name, mtu); | |
| else | |
| return ERROR_INVALID_DATA; | |
| } | |
| DWORD getInterfaceStatusByName(const char *name, PDWORD status) | |
| { | |
| DWORD ret; | |
| int fd; | |
| if (!name) | |
| return ERROR_INVALID_PARAMETER; | |
| if (!status) | |
| return ERROR_INVALID_PARAMETER; | |
| fd = socket(PF_INET, SOCK_DGRAM, 0); | |
| if (fd != -1) { | |
| struct ifreq ifr; | |
| strncpy(ifr.ifr_name, name, IFNAMSIZ); | |
| ifr.ifr_name[IFNAMSIZ-1] = '\0'; | |
| if ((ioctl(fd, SIOCGIFFLAGS, &ifr))) | |
| ret = ERROR_INVALID_DATA; | |
| else { | |
| if (ifr.ifr_flags & IFF_UP) | |
| *status = MIB_IF_OPER_STATUS_OPERATIONAL; | |
| else | |
| *status = MIB_IF_OPER_STATUS_NON_OPERATIONAL; | |
| ret = NO_ERROR; | |
| } | |
| } | |
| else | |
| ret = ERROR_NO_MORE_FILES; | |
| return ret; | |
| } | |
| DWORD getInterfaceStatusByIndex(DWORD index, PDWORD status) | |
| { | |
| const char *name = getInterfaceNameByIndex(index); | |
| if (name) | |
| return getInterfaceStatusByName(name, status); | |
| else | |
| return ERROR_INVALID_DATA; | |
| } | |
| DWORD getInterfaceEntryByName(const char *name, PMIB_IFROW entry) | |
| { | |
| BYTE addr[MAX_INTERFACE_PHYSADDR]; | |
| DWORD ret, len = sizeof(addr), type; | |
| if (!name) | |
| return ERROR_INVALID_PARAMETER; | |
| if (!entry) | |
| return ERROR_INVALID_PARAMETER; | |
| if (getInterfacePhysicalByName(name, &len, addr, &type) == NO_ERROR) { | |
| WCHAR *assigner; | |
| const char *walker; | |
| memset(entry, 0, sizeof(MIB_IFROW)); | |
| for (assigner = entry->wszName, walker = name; *walker; | |
| walker++, assigner++) | |
| *assigner = *walker; | |
| *assigner = 0; | |
| getInterfaceIndexByName(name, &entry->dwIndex); | |
| entry->dwPhysAddrLen = len; | |
| memcpy(entry->bPhysAddr, addr, len); | |
| memset(entry->bPhysAddr + len, 0, sizeof(entry->bPhysAddr) - len); | |
| entry->dwType = type; | |
| /* FIXME: how to calculate real speed? */ | |
| getInterfaceMtuByName(name, &entry->dwMtu); | |
| /* lie, there's no "administratively down" here */ | |
| entry->dwAdminStatus = MIB_IF_ADMIN_STATUS_UP; | |
| getInterfaceStatusByName(name, &entry->dwOperStatus); | |
| /* punt on dwLastChange? */ | |
| entry->dwDescrLen = min(strlen(name), MAX_INTERFACE_DESCRIPTION - 1); | |
| memcpy(entry->bDescr, name, entry->dwDescrLen); | |
| entry->bDescr[entry->dwDescrLen] = '\0'; | |
| entry->dwDescrLen++; | |
| ret = NO_ERROR; | |
| } | |
| else | |
| ret = ERROR_INVALID_DATA; | |
| return ret; | |
| } | |
| DWORD getInterfaceEntryByIndex(DWORD index, PMIB_IFROW entry) | |
| { | |
| const char *name = getInterfaceNameByIndex(index); | |
| if (name) | |
| return getInterfaceEntryByName(name, entry); | |
| else | |
| return ERROR_INVALID_DATA; | |
| } | |
| char *toIPAddressString(unsigned int addr, char string[16]) | |
| { | |
| if (string) { | |
| struct in_addr iAddr; | |
| iAddr.s_addr = addr; | |
| /* extra-anal, just to make auditors happy */ | |
| strncpy(string, inet_ntoa(iAddr), 16); | |
| string[16] = '\0'; | |
| } | |
| return string; | |
| } |