wine/dlls/iphlpapi/ifenum.c
2005-03-28 14:17:51 +00:00

939 lines
24 KiB
C

/* 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 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
sizeof(InterfaceNameMap) +
(numInterfaces - 1) * sizeof(InterfaceNameMapEntry));
if (map)
map->numAllocated = numInterfaces;
}
else {
if (map->numAllocated < numInterfaces) {
map = 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;
lstrcpynA(ifr.ifr_name, name, IFNAMSIZ);
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) {
lstrcpynA(map->table[ndx].name, name, IFNAMSIZ);
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 = 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 = 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 = 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;
lstrcpynA(ifr.ifr_name, name, IFNAMSIZ);
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;
lstrcpynA(ifr.ifr_name, name, IFNAMSIZ);
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;
lstrcpynA(ifr.ifr_name, name, IFNAMSIZ);
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));
lstrcpynA(ifr.ifr_name, name, IFNAMSIZ);
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 = 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;
lstrcpynA(ifr.ifr_name, name, IFNAMSIZ);
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;
lstrcpynA(ifr.ifr_name, name, IFNAMSIZ);
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 */
lstrcpynA(string, inet_ntoa(iAddr), sizeof(string));
}
return string;
}