Sweden-Number/dlls/iphlpapi/ifenum.c

960 lines
23 KiB
C
Raw Normal View History

/* Copyright (C) 2003,2006,2011 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#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>
2006-10-22 21:15:46 +02:00
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#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
#ifdef HAVE_IFADDRS_H
#include <ifaddrs.h>
#endif
#include "ifenum.h"
#include "ws2ipdef.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 IF_NAMESIZE
#define IF_NAMESIZE 16
#endif
#ifndef INADDR_NONE
#define INADDR_NONE (~0U)
#endif
#define INITIAL_INTERFACES_ASSUMED 4
/* Functions */
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;
}
/* The comments say MAX_ADAPTER_NAME is required, but really only IF_NAMESIZE
* bytes are necessary.
*/
char *getInterfaceNameByIndex(DWORD index, char *name)
{
return if_indextoname(index, name);
}
DWORD getInterfaceIndexByName(const char *name, PDWORD index)
{
DWORD ret;
unsigned int idx;
if (!name)
return ERROR_INVALID_PARAMETER;
if (!index)
return ERROR_INVALID_PARAMETER;
idx = if_nametoindex(name);
if (idx) {
*index = idx;
ret = NO_ERROR;
}
else
ret = ERROR_INVALID_DATA;
return ret;
}
BOOL isIfIndexLoopback(ULONG idx)
{
BOOL ret = FALSE;
char name[IFNAMSIZ];
int fd;
getInterfaceNameByIndex(idx, name);
fd = socket(PF_INET, SOCK_DGRAM, 0);
if (fd != -1) {
ret = isLoopbackInterface(fd, name);
close(fd);
}
return ret;
}
DWORD getNumNonLoopbackInterfaces(void)
{
DWORD numInterfaces;
int fd = socket(PF_INET, SOCK_DGRAM, 0);
if (fd != -1) {
struct if_nameindex *indexes = if_nameindex();
if (indexes) {
struct if_nameindex *p;
for (p = indexes, numInterfaces = 0; p && p->if_name; p++)
if (!isLoopbackInterface(fd, p->if_name))
numInterfaces++;
if_freenameindex(indexes);
}
else
numInterfaces = 0;
close(fd);
}
else
numInterfaces = 0;
return numInterfaces;
}
DWORD getNumInterfaces(void)
{
DWORD numInterfaces;
struct if_nameindex *indexes = if_nameindex();
if (indexes) {
struct if_nameindex *p;
for (p = indexes, numInterfaces = 0; p && p->if_name; p++)
numInterfaces++;
if_freenameindex(indexes);
}
else
numInterfaces = 0;
return numInterfaces;
}
InterfaceIndexTable *getInterfaceIndexTable(void)
{
DWORD numInterfaces;
InterfaceIndexTable *ret;
struct if_nameindex *indexes = if_nameindex();
if (indexes) {
struct if_nameindex *p;
DWORD size = sizeof(InterfaceIndexTable);
for (p = indexes, numInterfaces = 0; p && p->if_name; p++)
numInterfaces++;
if (numInterfaces > 1)
size += (numInterfaces - 1) * sizeof(DWORD);
ret = HeapAlloc(GetProcessHeap(), 0, size);
if (ret) {
ret->numIndexes = 0;
for (p = indexes; p && p->if_name; p++)
ret->indexes[ret->numIndexes++] = p->if_index;
}
if_freenameindex(indexes);
}
else
ret = NULL;
return ret;
}
InterfaceIndexTable *getNonLoopbackInterfaceIndexTable(void)
{
DWORD numInterfaces;
InterfaceIndexTable *ret;
int fd = socket(PF_INET, SOCK_DGRAM, 0);
if (fd != -1) {
struct if_nameindex *indexes = if_nameindex();
if (indexes) {
struct if_nameindex *p;
DWORD size = sizeof(InterfaceIndexTable);
for (p = indexes, numInterfaces = 0; p && p->if_name; p++)
if (!isLoopbackInterface(fd, p->if_name))
numInterfaces++;
if (numInterfaces > 1)
size += (numInterfaces - 1) * sizeof(DWORD);
ret = HeapAlloc(GetProcessHeap(), 0, size);
if (ret) {
ret->numIndexes = 0;
for (p = indexes; p && p->if_name; p++)
if (!isLoopbackInterface(fd, p->if_name))
ret->indexes[ret->numIndexes++] = p->if_index;
}
if_freenameindex(indexes);
}
else
ret = NULL;
close(fd);
}
else
ret = NULL;
return ret;
}
static 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;
}
static 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;
}
#if defined (SIOCGIFHWADDR) && defined (HAVE_STRUCT_IFREQ_IFR_HWADDR)
static 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)
static 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;
struct ifreq ifr;
/* get IP addr */
lstrcpynA(ifr.ifr_name, name, IFNAMSIZ);
ioctl(fd, SIOCGIFADDR, &ifr);
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;
memcpy(&saddr->sin_addr.s_addr, ifr.ifr_addr.sa_data + 2, sizeof(DWORD));
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)
static 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 };
unsigned 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;
2005-03-24 22:01:35 +01:00
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)
{
char nameBuf[IF_NAMESIZE];
char *name = getInterfaceNameByIndex(index, nameBuf);
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;
}
close(fd);
}
else
ret = ERROR_NO_MORE_FILES;
return ret;
}
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;
}
close(fd);
}
else
ret = ERROR_NO_MORE_FILES;
return ret;
}
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;
}
static DWORD getIPAddrRowByName(PMIB_IPADDRROW ipAddrRow, const char *ifName,
const struct sockaddr *sa)
{
DWORD ret, bcast;
ret = getInterfaceIndexByName(ifName, &ipAddrRow->dwIndex);
memcpy(&ipAddrRow->dwAddr, sa->sa_data + 2, sizeof(DWORD));
ipAddrRow->dwMask = getInterfaceMaskByName(ifName);
/* the dwBCastAddr member isn't the broadcast address, it indicates whether
* the interface uses the 1's broadcast address (1) or the 0's broadcast
* address (0).
*/
bcast = getInterfaceBCastAddrByName(ifName);
ipAddrRow->dwBCastAddr = (bcast & ipAddrRow->dwMask) ? 1 : 0;
/* FIXME: hardcoded reasm size, not sure where to get it */
ipAddrRow->dwReasmSize = 65535;
ipAddrRow->unused1 = 0;
ipAddrRow->wType = 0;
return ret;
}
#ifdef HAVE_IFADDRS_H
/* Counts the IPv4 addresses in the system using the return value from
* getifaddrs, returning the count.
*/
static DWORD countIPv4Addresses(struct ifaddrs *ifa)
{
DWORD numAddresses = 0;
for (; ifa; ifa = ifa->ifa_next)
if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET)
numAddresses++;
return numAddresses;
}
DWORD getNumIPAddresses(void)
{
DWORD numAddresses = 0;
struct ifaddrs *ifa;
if (!getifaddrs(&ifa))
{
numAddresses = countIPv4Addresses(ifa);
freeifaddrs(ifa);
}
return numAddresses;
}
DWORD getIPAddrTable(PMIB_IPADDRTABLE *ppIpAddrTable, HANDLE heap, DWORD flags)
{
DWORD ret;
if (!ppIpAddrTable)
ret = ERROR_INVALID_PARAMETER;
else
{
struct ifaddrs *ifa;
if (!getifaddrs(&ifa))
{
DWORD size = sizeof(MIB_IPADDRTABLE);
DWORD numAddresses = countIPv4Addresses(ifa);
if (numAddresses > 1)
size += (numAddresses - 1) * sizeof(MIB_IPADDRROW);
*ppIpAddrTable = HeapAlloc(heap, flags, size);
if (*ppIpAddrTable)
{
DWORD i = 0;
struct ifaddrs *ifp;
ret = NO_ERROR;
(*ppIpAddrTable)->dwNumEntries = numAddresses;
for (ifp = ifa; !ret && ifp; ifp = ifp->ifa_next)
{
if (!ifp->ifa_addr || ifp->ifa_addr->sa_family != AF_INET)
continue;
ret = getIPAddrRowByName(&(*ppIpAddrTable)->table[i], ifp->ifa_name,
ifp->ifa_addr);
i++;
}
}
else
ret = ERROR_OUTOFMEMORY;
freeifaddrs(ifa);
}
else
ret = ERROR_INVALID_PARAMETER;
}
return ret;
}
ULONG v6addressesFromIndex(DWORD index, SOCKET_ADDRESS **addrs, ULONG *num_addrs)
{
struct ifaddrs *ifa;
ULONG ret;
if (!getifaddrs(&ifa))
{
struct ifaddrs *p;
ULONG n;
char name[IFNAMSIZ];
getInterfaceNameByIndex(index, name);
for (p = ifa, n = 0; p; p = p->ifa_next)
if (p->ifa_addr && p->ifa_addr->sa_family == AF_INET6 &&
!strcmp(name, p->ifa_name))
n++;
if (n)
{
*addrs = HeapAlloc(GetProcessHeap(), 0, n * (sizeof(SOCKET_ADDRESS) +
sizeof(struct WS_sockaddr_in6)));
if (*addrs)
{
struct WS_sockaddr_in6 *next_addr = (struct WS_sockaddr_in6 *)(
(BYTE *)*addrs + n * sizeof(SOCKET_ADDRESS));
for (p = ifa, n = 0; p; p = p->ifa_next)
{
if (p->ifa_addr && p->ifa_addr->sa_family == AF_INET6 &&
!strcmp(name, p->ifa_name))
{
struct sockaddr_in6 *addr = (struct sockaddr_in6 *)p->ifa_addr;
next_addr->sin6_family = WS_AF_INET6;
next_addr->sin6_port = addr->sin6_port;
next_addr->sin6_flowinfo = addr->sin6_flowinfo;
memcpy(&next_addr->sin6_addr, &addr->sin6_addr,
sizeof(next_addr->sin6_addr));
next_addr->sin6_scope_id = addr->sin6_scope_id;
(*addrs)[n].lpSockaddr = (LPSOCKADDR)next_addr;
(*addrs)[n].iSockaddrLength = sizeof(struct WS_sockaddr_in6);
next_addr++;
n++;
}
}
*num_addrs = n;
ret = ERROR_SUCCESS;
}
else
ret = ERROR_OUTOFMEMORY;
}
else
{
*addrs = NULL;
*num_addrs = 0;
ret = ERROR_SUCCESS;
}
freeifaddrs(ifa);
}
else
ret = ERROR_NO_DATA;
return ret;
}
#else
/* Enumerates the IP addresses in the system using SIOCGIFCONF, returning
* the count to you in *pcAddresses. It also returns to you the struct ifconf
* used by the call to ioctl, so that you may process the addresses further.
* Free ifc->ifc_buf using HeapFree.
* Returns NO_ERROR on success, something else on failure.
*/
static DWORD enumIPAddresses(PDWORD pcAddresses, struct ifconf *ifc)
{
DWORD ret;
int fd;
fd = socket(PF_INET, SOCK_DGRAM, 0);
if (fd != -1) {
int ioctlRet = 0;
DWORD guessedNumAddresses = 0, numAddresses = 0;
caddr_t ifPtr;
2007-01-26 02:14:53 +01:00
int lastlen;
ret = NO_ERROR;
ifc->ifc_len = 0;
ifc->ifc_buf = NULL;
/* there is no way to know the interface count beforehand,
so we need to loop again and again upping our max each time
2007-01-26 02:14:53 +01:00
until returned is constant across 2 calls */
do {
2007-01-26 02:14:53 +01:00
lastlen = ifc->ifc_len;
HeapFree(GetProcessHeap(), 0, ifc->ifc_buf);
if (guessedNumAddresses == 0)
guessedNumAddresses = INITIAL_INTERFACES_ASSUMED;
else
guessedNumAddresses *= 2;
ifc->ifc_len = sizeof(struct ifreq) * guessedNumAddresses;
ifc->ifc_buf = HeapAlloc(GetProcessHeap(), 0, ifc->ifc_len);
ioctlRet = ioctl(fd, SIOCGIFCONF, ifc);
2007-01-26 02:14:53 +01:00
} while ((ioctlRet == 0) && (ifc->ifc_len != lastlen));
if (ioctlRet == 0) {
ifPtr = ifc->ifc_buf;
while (ifPtr && ifPtr < ifc->ifc_buf + ifc->ifc_len) {
struct ifreq *ifr = (struct ifreq *)ifPtr;
if (ifr->ifr_addr.sa_family == AF_INET)
numAddresses++;
ifPtr += ifreq_len((struct ifreq *)ifPtr);
}
}
else
ret = ERROR_INVALID_PARAMETER; /* FIXME: map from errno to Win32 */
if (!ret)
*pcAddresses = numAddresses;
else
{
HeapFree(GetProcessHeap(), 0, ifc->ifc_buf);
ifc->ifc_buf = NULL;
}
close(fd);
}
else
ret = ERROR_NO_SYSTEM_RESOURCES;
return ret;
}
DWORD getNumIPAddresses(void)
{
DWORD numAddresses = 0;
struct ifconf ifc;
if (!enumIPAddresses(&numAddresses, &ifc))
HeapFree(GetProcessHeap(), 0, ifc.ifc_buf);
return numAddresses;
}
DWORD getIPAddrTable(PMIB_IPADDRTABLE *ppIpAddrTable, HANDLE heap, DWORD flags)
{
DWORD ret;
if (!ppIpAddrTable)
ret = ERROR_INVALID_PARAMETER;
else
{
DWORD numAddresses = 0;
struct ifconf ifc;
ret = enumIPAddresses(&numAddresses, &ifc);
if (!ret)
{
DWORD size = sizeof(MIB_IPADDRTABLE);
if (numAddresses > 1)
size += (numAddresses - 1) * sizeof(MIB_IPADDRROW);
*ppIpAddrTable = HeapAlloc(heap, flags, size);
if (*ppIpAddrTable) {
DWORD i = 0;
caddr_t ifPtr;
ret = NO_ERROR;
(*ppIpAddrTable)->dwNumEntries = numAddresses;
ifPtr = ifc.ifc_buf;
while (!ret && ifPtr && ifPtr < ifc.ifc_buf + ifc.ifc_len) {
struct ifreq *ifr = (struct ifreq *)ifPtr;
ifPtr += ifreq_len(ifr);
if (ifr->ifr_addr.sa_family != AF_INET)
continue;
ret = getIPAddrRowByName(&(*ppIpAddrTable)->table[i], ifr->ifr_name,
&ifr->ifr_addr);
i++;
}
}
else
ret = ERROR_OUTOFMEMORY;
HeapFree(GetProcessHeap(), 0, ifc.ifc_buf);
}
}
return ret;
}
ULONG v6addressesFromIndex(DWORD index, SOCKET_ADDRESS **addrs, ULONG *num_addrs)
{
*addrs = NULL;
*num_addrs = 0;
return ERROR_SUCCESS;
}
#endif
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), 16);
}
return string;
}