iphlpapi: Implement GetAdaptersAddresses() on top of nsi.

Signed-off-by: Huw Davies <huw@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Huw Davies 2021-08-09 10:06:28 +01:00 committed by Alexandre Julliard
parent fade7a5862
commit 091c92521e
3 changed files with 248 additions and 1143 deletions

View File

@ -122,7 +122,7 @@ static BOOL isLoopbackInterface(int fd, const char *name)
/* The comments say MAX_ADAPTER_NAME is required, but really only IF_NAMESIZE
* bytes are necessary.
*/
char *getInterfaceNameByIndex(IF_INDEX index, char *name)
static char *getInterfaceNameByIndex(IF_INDEX index, char *name)
{
return if_indextoname(index, name);
}
@ -146,7 +146,7 @@ DWORD getInterfaceIndexByName(const char *name, IF_INDEX *index)
return ret;
}
BOOL isIfIndexLoopback(ULONG idx)
static BOOL isIfIndexLoopback(ULONG idx)
{
BOOL ret = FALSE;
char name[IFNAMSIZ];
@ -393,671 +393,3 @@ DWORD get_interface_indices( BOOL skip_loopback, InterfaceIndexTable **table )
return 0;
}
#endif
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 = 0;
*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;
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(IF_INDEX 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, INTERNAL_IF_OPER_STATUS *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;
}
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;
/* wType is a bit field composed of MIB_IPADDR_* flags. Windows <= XP seems
* to like returning undocumented values 0x20 + 0x02 but for our current
* needs returning MIB_IPADDR_PRIMARY is enough.
*/
ipAddrRow->wType = MIB_IPADDR_PRIMARY;
return ret;
}
#if defined(HAVE_IFADDRS_H) && defined(HAVE_GETIFADDRS)
/* 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++;
}
if (ret)
HeapFree(GetProcessHeap(), 0, *ppIpAddrTable);
}
else
ret = ERROR_OUTOFMEMORY;
freeifaddrs(ifa);
}
else
ret = ERROR_INVALID_PARAMETER;
}
return ret;
}
ULONG v6addressesFromIndex(IF_INDEX index, SOCKET_ADDRESS **addrs, ULONG *num_addrs, SOCKET_ADDRESS **masks)
{
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)));
*masks = HeapAlloc(GetProcessHeap(), 0, n * (sizeof(SOCKET_ADDRESS) +
sizeof(struct WS_sockaddr_in6)));
if (*addrs && *masks)
{
struct WS_sockaddr_in6 *next_addr = (struct WS_sockaddr_in6 *)(
(BYTE *)*addrs + n * sizeof(SOCKET_ADDRESS));
struct WS_sockaddr_in6 *mask_addr = (struct WS_sockaddr_in6 *)(
(BYTE *)*masks + 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;
struct sockaddr_in6 *mask = (struct sockaddr_in6 *)p->ifa_netmask;
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++;
mask_addr->sin6_family = WS_AF_INET6;
mask_addr->sin6_port = mask->sin6_port;
mask_addr->sin6_flowinfo = mask->sin6_flowinfo;
memcpy(&mask_addr->sin6_addr, &mask->sin6_addr,
sizeof(mask_addr->sin6_addr));
mask_addr->sin6_scope_id = mask->sin6_scope_id;
(*masks)[n].lpSockaddr = (LPSOCKADDR)mask_addr;
(*masks)[n].iSockaddrLength = sizeof(struct WS_sockaddr_in6);
mask_addr++;
n++;
}
}
*num_addrs = n;
ret = ERROR_SUCCESS;
}
else
{
HeapFree(GetProcessHeap(), 0, *addrs);
HeapFree(GetProcessHeap(), 0, *masks);
ret = ERROR_OUTOFMEMORY;
}
}
else
{
*addrs = NULL;
*num_addrs = 0;
*masks = NULL;
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;
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
until returned is constant across 2 calls */
do {
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);
} while ((ioctlRet == 0) && (ifc->ifc_len != lastlen));
if (ioctlRet == 0) {
ifPtr = ifc->ifc_buf;
while (ifPtr && (char *)ifPtr < ifc->ifc_buf + ifc->ifc_len) {
struct ifreq *ifr = (struct ifreq *)ifPtr;
if (ifr->ifr_addr.sa_family == AF_INET)
numAddresses++;
ifPtr = (char *)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 && (char *)ifPtr < ifc.ifc_buf + ifc.ifc_len) {
struct ifreq *ifr = (struct ifreq *)ifPtr;
ifPtr = (char *)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++;
}
if (ret)
HeapFree(GetProcessHeap(), 0, *ppIpAddrTable);
}
else
ret = ERROR_OUTOFMEMORY;
HeapFree(GetProcessHeap(), 0, ifc.ifc_buf);
}
}
return ret;
}
ULONG v6addressesFromIndex(IF_INDEX index, SOCKET_ADDRESS **addrs, ULONG *num_addrs, SOCKET_ADDRESS **masks)
{
*addrs = NULL;
*num_addrs = 0;
*masks = NULL;
return ERROR_SUCCESS;
}
#endif

View File

@ -44,8 +44,6 @@
#define MAX_INTERFACE_PHYSADDR 8
#define MAX_INTERFACE_DESCRIPTION 256
BOOL isIfIndexLoopback(ULONG idx) DECLSPEC_HIDDEN;
/* A table of interface indexes, see get_interface_indices(). */
typedef struct _InterfaceIndexTable {
DWORD numIndexes;
@ -59,50 +57,10 @@ DWORD get_interface_indices( BOOL skip_loopback, InterfaceIndexTable **table ) D
/* ByName/ByIndex versions of various getter functions. */
/* can be used as quick check to see if you've got a valid index, returns NULL
* if not. Overwrites your buffer, which should be at least of size
* MAX_ADAPTER_NAME.
*/
char *getInterfaceNameByIndex(IF_INDEX index, char *name) DECLSPEC_HIDDEN;
/* Fills index with the index of name, if found. Returns
* ERROR_INVALID_PARAMETER if name or index is NULL, ERROR_INVALID_DATA if name
* is not found, and NO_ERROR on success.
*/
DWORD getInterfaceIndexByName(const char *name, IF_INDEX *index) DECLSPEC_HIDDEN;
/* Gets a few physical characteristics of a device: MAC addr len, MAC addr,
* and type as one of the MIB_IF_TYPEs.
* len's in-out: on in, needs to say how many bytes are available in addr,
* which to be safe should be MAX_INTERFACE_PHYSADDR. On out, it's how many
* bytes were set, or how many were required if addr isn't big enough.
* Returns ERROR_INVALID_PARAMETER if name, len, addr, or type is NULL.
* Returns ERROR_INVALID_DATA if name/index isn't valid.
* Returns ERROR_INSUFFICIENT_BUFFER if addr isn't large enough for the
* physical address; *len will contain the required size.
* May return other errors, e.g. ERROR_OUTOFMEMORY or ERROR_NO_MORE_FILES,
* if internal errors occur.
* Returns NO_ERROR on success.
*/
DWORD getInterfacePhysicalByIndex(IF_INDEX index, PDWORD len, PBYTE addr,
PDWORD type) DECLSPEC_HIDDEN;
DWORD getNumIPAddresses(void) DECLSPEC_HIDDEN;
/* Gets the configured IP addresses for the system, and sets *ppIpAddrTable to
* a table of them allocated from heap, or NULL if out of memory. Returns
* NO_ERROR on success, something else on failure. Note there may be more than
* one IP address may exist per interface.
*/
DWORD getIPAddrTable(PMIB_IPADDRTABLE *ppIpAddrTable, HANDLE heap, DWORD flags) DECLSPEC_HIDDEN;
/* Returns the IPv6 addresses for a particular interface index.
* Returns NO_ERROR on success, something else on failure.
*/
ULONG v6addressesFromIndex(IF_INDEX index, SOCKET_ADDRESS **addrs, ULONG *num_addrs,
SOCKET_ADDRESS **masks) DECLSPEC_HIDDEN;
DWORD getInterfaceMtuByName(const char *name, PDWORD mtu) DECLSPEC_HIDDEN;
DWORD getInterfaceStatusByName(const char *name, INTERNAL_IF_OPER_STATUS *status) DECLSPEC_HIDDEN;
#endif /* ndef WINE_IFENUM_H_ */

View File

@ -109,6 +109,15 @@ DWORD WINAPI ConvertStringToGuidW( const WCHAR *str, GUID *guid )
return RtlNtStatusToDosError( RtlGUIDFromString( &ustr, guid ) );
}
static void if_counted_string_copy( WCHAR *dst, unsigned int len, IF_COUNTED_STRING *src )
{
unsigned int copy = src->Length;
if (copy >= len * sizeof(WCHAR)) copy = 0;
memcpy( dst, src->String, copy );
memset( (char *)dst + copy, 0, len * sizeof(WCHAR) - copy );
}
/******************************************************************
* AddIPAddress (IPHLPAPI.@)
*
@ -913,129 +922,6 @@ void adapters_addresses_copy( IP_ADAPTER_ADDRESSES *dst, IP_ADAPTER_ADDRESSES *s
}
}
static DWORD typeFromMibType(DWORD mib_type)
{
switch (mib_type)
{
case MIB_IF_TYPE_ETHERNET: return IF_TYPE_ETHERNET_CSMACD;
case MIB_IF_TYPE_TOKENRING: return IF_TYPE_ISO88025_TOKENRING;
case MIB_IF_TYPE_PPP: return IF_TYPE_PPP;
case MIB_IF_TYPE_LOOPBACK: return IF_TYPE_SOFTWARE_LOOPBACK;
default: return IF_TYPE_OTHER;
}
}
static NET_IF_CONNECTION_TYPE connectionTypeFromMibType(DWORD mib_type)
{
switch (mib_type)
{
case MIB_IF_TYPE_PPP: return NET_IF_CONNECTION_DEMAND;
case MIB_IF_TYPE_SLIP: return NET_IF_CONNECTION_DEMAND;
default: return NET_IF_CONNECTION_DEDICATED;
}
}
static ULONG v4addressesFromIndex(IF_INDEX index, DWORD **addrs, ULONG *num_addrs, DWORD **masks)
{
ULONG ret, i, j;
MIB_IPADDRTABLE *at;
*num_addrs = 0;
if ((ret = getIPAddrTable(&at, GetProcessHeap(), 0))) return ret;
for (i = 0; i < at->dwNumEntries; i++)
{
if (at->table[i].dwIndex == index) (*num_addrs)++;
}
if (!(*addrs = HeapAlloc(GetProcessHeap(), 0, *num_addrs * sizeof(DWORD))))
{
HeapFree(GetProcessHeap(), 0, at);
return ERROR_OUTOFMEMORY;
}
if (!(*masks = HeapAlloc(GetProcessHeap(), 0, *num_addrs * sizeof(DWORD))))
{
HeapFree(GetProcessHeap(), 0, *addrs);
HeapFree(GetProcessHeap(), 0, at);
return ERROR_OUTOFMEMORY;
}
for (i = 0, j = 0; i < at->dwNumEntries; i++)
{
if (at->table[i].dwIndex == index)
{
(*addrs)[j] = at->table[i].dwAddr;
(*masks)[j] = at->table[i].dwMask;
j++;
}
}
HeapFree(GetProcessHeap(), 0, at);
return ERROR_SUCCESS;
}
static char *debugstr_ipv4(const in_addr_t *in_addr, char *buf)
{
const BYTE *addrp;
char *p = buf;
for (addrp = (const BYTE *)in_addr;
addrp - (const BYTE *)in_addr < sizeof(*in_addr);
addrp++)
{
if (addrp == (const BYTE *)in_addr + sizeof(*in_addr) - 1)
sprintf(p, "%d", *addrp);
else
p += sprintf(p, "%d.", *addrp);
}
return buf;
}
static ULONG count_v4_gateways(DWORD index, PMIB_IPFORWARDTABLE routeTable)
{
DWORD i, num_gateways = 0;
for (i = 0; i < routeTable->dwNumEntries; i++)
{
if (routeTable->table[i].dwForwardIfIndex == index &&
routeTable->table[i].u1.ForwardType == MIB_IPROUTE_TYPE_INDIRECT)
num_gateways++;
}
return num_gateways;
}
static DWORD mask_v4_to_prefix(DWORD m)
{
#ifdef HAVE___BUILTIN_POPCOUNT
return __builtin_popcount(m);
#else
m -= m >> 1 & 0x55555555;
m = (m & 0x33333333) + (m >> 2 & 0x33333333);
return ((m + (m >> 4)) & 0x0f0f0f0f) * 0x01010101 >> 24;
#endif
}
static DWORD mask_v6_to_prefix(SOCKET_ADDRESS *m)
{
const IN6_ADDR *mask = &((struct WS_sockaddr_in6 *)m->lpSockaddr)->sin6_addr;
DWORD ret = 0, i;
for (i = 0; i < 8; i++)
ret += mask_v4_to_prefix(mask->u.Word[i]);
return ret;
}
static PMIB_IPFORWARDROW findIPv4Gateway(DWORD index,
PMIB_IPFORWARDTABLE routeTable)
{
DWORD i;
PMIB_IPFORWARDROW row = NULL;
for (i = 0; !row && i < routeTable->dwNumEntries; i++)
{
if (routeTable->table[i].dwForwardIfIndex == index &&
routeTable->table[i].u1.ForwardType == MIB_IPROUTE_TYPE_INDIRECT)
row = &routeTable->table[i];
}
return row;
}
static BOOL sockaddr_is_loopback( SOCKADDR *sock )
{
if (sock->sa_family == WS_AF_INET)
@ -1067,308 +953,211 @@ static BOOL unicast_is_dns_eligible( IP_ADAPTER_UNICAST_ADDRESS *uni )
!sockaddr_is_linklocal( uni->Address.lpSockaddr );
}
static void fill_unicast_addr_data(IP_ADAPTER_ADDRESSES *aa, IP_ADAPTER_UNICAST_ADDRESS *ua)
static DWORD unicast_addresses_alloc( IP_ADAPTER_ADDRESSES *aa, ULONG family, ULONG flags )
{
/* Actually this information should be read somewhere from the system
* but it doesn't matter much for the bugs found so far.
* This information is required for DirectPlay8 games. */
if (aa->IfType != IF_TYPE_SOFTWARE_LOOPBACK)
struct nsi_ipv4_unicast_key *key4;
struct nsi_ipv6_unicast_key *key6;
struct nsi_ip_unicast_rw *rw;
struct nsi_ip_unicast_dynamic *dyn;
struct nsi_ip_unicast_static *stat;
IP_ADAPTER_UNICAST_ADDRESS *addr, **next;
DWORD err, count, i, key_size = (family == WS_AF_INET) ? sizeof(*key4) : sizeof(*key6);
DWORD sockaddr_size = (family == WS_AF_INET) ? sizeof(SOCKADDR_IN) : sizeof(SOCKADDR_IN6);
NET_LUID *luid;
void *key;
err = NsiAllocateAndGetTable( 1, ip_module_id( family ), NSI_IP_UNICAST_TABLE, &key, key_size,
(void **)&rw, sizeof(*rw), (void **)&dyn, sizeof(*dyn),
(void **)&stat, sizeof(*stat), &count, 0 );
if (err) return err;
while (aa)
{
ua->PrefixOrigin = IpPrefixOriginDhcp;
ua->SuffixOrigin = IpSuffixOriginDhcp;
}
else
{
ua->PrefixOrigin = IpPrefixOriginManual;
ua->SuffixOrigin = IpSuffixOriginManual;
for (next = &aa->FirstUnicastAddress; *next; next = &(*next)->Next)
;
for (i = 0; i < count; i++)
{
key4 = (struct nsi_ipv4_unicast_key *)key + i;
key6 = (struct nsi_ipv6_unicast_key *)key + i;
luid = (family == WS_AF_INET) ? &key4->luid : &key6->luid;
if (luid->Value != aa->Luid.Value) continue;
addr = heap_alloc_zero( sizeof(*addr) + sockaddr_size );
if (!addr)
{
err = ERROR_NOT_ENOUGH_MEMORY;
goto err;
}
addr->u.s.Length = sizeof(*addr);
addr->Address.lpSockaddr = (SOCKADDR *)(addr + 1);
addr->Address.iSockaddrLength = sockaddr_size;
addr->Address.lpSockaddr->sa_family = family;
if (family == WS_AF_INET)
{
SOCKADDR_IN *in = (SOCKADDR_IN *)addr->Address.lpSockaddr;
in->sin_addr = key4->addr;
}
else
{
SOCKADDR_IN6 *in6 = (SOCKADDR_IN6 *)addr->Address.lpSockaddr;
in6->sin6_addr = key6->addr;
in6->sin6_scope_id = dyn[i].scope_id;
}
addr->PrefixOrigin = rw[i].prefix_origin;
addr->SuffixOrigin = rw[i].suffix_origin;
addr->DadState = dyn[i].dad_state;
addr->ValidLifetime = rw[i].valid_lifetime;
addr->PreferredLifetime = rw[i].preferred_lifetime;
addr->LeaseLifetime = rw[i].valid_lifetime; /* FIXME */
addr->OnLinkPrefixLength = rw[i].on_link_prefix;
if (unicast_is_dns_eligible( addr )) addr->u.s.Flags |= IP_ADAPTER_ADDRESS_DNS_ELIGIBLE;
*next = addr;
next = &addr->Next;
}
aa = aa->Next;
}
/* The address is not duplicated in the network */
ua->DadState = IpDadStatePreferred;
/* Some address life time values, required even for non-dhcp addresses */
ua->ValidLifetime = 60000;
ua->PreferredLifetime = 60000;
ua->LeaseLifetime = 60000;
if (unicast_is_dns_eligible( ua )) ua->u.s.Flags |= IP_ADAPTER_ADDRESS_DNS_ELIGIBLE;
err:
NsiFreeTable( key, rw, dyn, stat );
return err;
}
static ULONG adapterAddressesFromIndex(ULONG family, ULONG flags, IF_INDEX index,
IP_ADAPTER_ADDRESSES *aa, char **ptr)
static DWORD gateway_and_prefix_addresses_alloc( IP_ADAPTER_ADDRESSES *aa, ULONG family, ULONG flags )
{
ULONG ret = ERROR_SUCCESS, i, j, num_v4addrs = 0, num_v4_gateways = 0, num_v6addrs = 0;
DWORD *v4addrs = NULL, *v4masks = NULL;
SOCKET_ADDRESS *v6addrs = NULL, *v6masks = NULL;
PMIB_IPFORWARDTABLE routeTable = NULL;
BOOL output_gateways;
char name[IF_NAMESIZE], *src;
WCHAR *dst;
DWORD buflen, type;
INTERNAL_IF_OPER_STATUS status;
NET_LUID luid;
GUID guid;
struct nsi_ipv4_forward_key *key4;
struct nsi_ipv6_forward_key *key6;
IP_ADAPTER_GATEWAY_ADDRESS *gw, **gw_next;
IP_ADAPTER_PREFIX *prefix, **prefix_next;
DWORD err, count, i, prefix_len, key_size = (family == WS_AF_INET) ? sizeof(*key4) : sizeof(*key6);
DWORD sockaddr_size = (family == WS_AF_INET) ? sizeof(SOCKADDR_IN) : sizeof(SOCKADDR_IN6);
SOCKADDR_INET sockaddr;
NET_LUID *luid;
void *key;
if ((flags & GAA_FLAG_INCLUDE_ALL_GATEWAYS) || !(flags & GAA_FLAG_SKIP_UNICAST))
{
ret = AllocateAndGetIpForwardTableFromStack(&routeTable, FALSE, GetProcessHeap(), 0);
if (ret) return ret;
num_v4_gateways = count_v4_gateways(index, routeTable);
}
output_gateways = (flags & GAA_FLAG_INCLUDE_ALL_GATEWAYS) && (family == WS_AF_INET || family == WS_AF_UNSPEC);
err = NsiAllocateAndGetTable( 1, ip_module_id( family ), NSI_IP_FORWARD_TABLE, &key, key_size,
NULL, 0, NULL, 0, NULL, 0, &count, 0 );
if (err) return err;
if (family == WS_AF_INET)
while (aa)
{
ret = v4addressesFromIndex(index, &v4addrs, &num_v4addrs, &v4masks);
}
else if (family == WS_AF_INET6)
{
ret = v6addressesFromIndex(index, &v6addrs, &num_v6addrs, &v6masks);
}
else if (family == WS_AF_UNSPEC)
{
ret = v4addressesFromIndex(index, &v4addrs, &num_v4addrs, &v4masks);
if (!ret) ret = v6addressesFromIndex(index, &v6addrs, &num_v6addrs, &v6masks);
}
else
{
FIXME("address family %u unsupported\n", family);
ret = ERROR_NO_DATA;
}
if (ret)
{
HeapFree(GetProcessHeap(), 0, v4addrs);
HeapFree(GetProcessHeap(), 0, v4masks);
HeapFree(GetProcessHeap(), 0, v6addrs);
HeapFree(GetProcessHeap(), 0, v6masks);
HeapFree(GetProcessHeap(), 0, routeTable);
return ret;
}
for (gw_next = &aa->FirstGatewayAddress; *gw_next; gw_next = &(*gw_next)->Next)
;
for (prefix_next = &aa->FirstPrefix; *prefix_next; prefix_next = &(*prefix_next)->Next)
;
if (1)
{
memset(aa, 0, sizeof(IP_ADAPTER_ADDRESSES));
aa->u.s.Length = sizeof(IP_ADAPTER_ADDRESSES);
aa->u.s.IfIndex = index;
ConvertInterfaceIndexToLuid(index, &luid);
ConvertInterfaceLuidToGuid(&luid, &guid);
ConvertGuidToStringA( &guid, *ptr, CHARS_IN_GUID );
aa->AdapterName = *ptr;
*ptr += (CHARS_IN_GUID + 1) & ~1;
getInterfaceNameByIndex(index, name);
if (!(flags & GAA_FLAG_SKIP_FRIENDLY_NAME))
for (i = 0; i < count; i++)
{
aa->FriendlyName = (WCHAR *)*ptr;
for (src = name, dst = (WCHAR *)*ptr; *src; src++, dst++)
*dst = *src;
*dst++ = 0;
*ptr = (char *)dst;
}
aa->Description = (WCHAR *)ptr;
for (src = name, dst = (WCHAR *)*ptr; *src; src++, dst++)
*dst = *src;
*dst++ = 0;
*ptr = (char *)dst;
key4 = (struct nsi_ipv4_forward_key *)key + i;
key6 = (struct nsi_ipv6_forward_key *)key + i;
luid = (family == WS_AF_INET) ? &key4->luid : &key6->luid;
if (luid->Value != aa->Luid.Value) continue;
TRACE("%s: %d IPv4 addresses, %d IPv6 addresses:\n", name, num_v4addrs,
num_v6addrs);
buflen = MAX_INTERFACE_PHYSADDR;
getInterfacePhysicalByIndex(index, &buflen, aa->PhysicalAddress, &type);
aa->PhysicalAddressLength = buflen;
aa->IfType = typeFromMibType(type);
aa->ConnectionType = connectionTypeFromMibType(type);
ConvertInterfaceIndexToLuid( index, &aa->Luid );
if (output_gateways && num_v4_gateways)
{
PMIB_IPFORWARDROW adapterRow;
if ((adapterRow = findIPv4Gateway(index, routeTable)))
if (flags & GAA_FLAG_INCLUDE_ALL_GATEWAYS)
{
PIP_ADAPTER_GATEWAY_ADDRESS gw;
PSOCKADDR_IN sin;
gw = heap_alloc( sizeof(IP_ADAPTER_GATEWAY_ADDRESS) + sizeof(SOCKADDR_IN) );
aa->FirstGatewayAddress = gw;
gw->u.s.Length = sizeof(IP_ADAPTER_GATEWAY_ADDRESS);
sin = (PSOCKADDR_IN)(gw + 1);
sin->sin_family = WS_AF_INET;
sin->sin_port = 0;
memcpy(&sin->sin_addr, &adapterRow->dwForwardNextHop,
sizeof(DWORD));
gw->Address.lpSockaddr = (LPSOCKADDR)sin;
gw->Address.iSockaddrLength = sizeof(SOCKADDR_IN);
gw->Next = NULL;
}
}
if (num_v4addrs && !(flags & GAA_FLAG_SKIP_UNICAST))
{
IP_ADAPTER_UNICAST_ADDRESS *ua;
struct WS_sockaddr_in *sa;
aa->u1.s1.Ipv4Enabled = TRUE;
ua = aa->FirstUnicastAddress = heap_alloc_zero( sizeof(IP_ADAPTER_UNICAST_ADDRESS) + sizeof(SOCKADDR_IN) );
for (i = 0; i < num_v4addrs; i++)
{
char addr_buf[16];
memset(ua, 0, sizeof(IP_ADAPTER_UNICAST_ADDRESS));
ua->u.s.Length = sizeof(IP_ADAPTER_UNICAST_ADDRESS);
ua->Address.iSockaddrLength = sizeof(struct sockaddr_in);
ua->Address.lpSockaddr = (SOCKADDR *)(ua + 1);
sa = (struct WS_sockaddr_in *)ua->Address.lpSockaddr;
sa->sin_family = WS_AF_INET;
sa->sin_addr.S_un.S_addr = v4addrs[i];
sa->sin_port = 0;
TRACE("IPv4 %d/%d: %s\n", i + 1, num_v4addrs,
debugstr_ipv4(&sa->sin_addr.S_un.S_addr, addr_buf));
fill_unicast_addr_data(aa, ua);
ua->OnLinkPrefixLength = mask_v4_to_prefix(v4masks[i]);
if (i < num_v4addrs - 1)
memset( &sockaddr, 0, sizeof(sockaddr) );
if (family == WS_AF_INET)
{
ua->Next = heap_alloc_zero( sizeof(IP_ADAPTER_UNICAST_ADDRESS) + sizeof(SOCKADDR_IN) );
ua = ua->Next;
if (key4->next_hop.WS_s_addr != 0)
{
sockaddr.si_family = family;
sockaddr.Ipv4.sin_addr = key4->next_hop;
}
}
else
{
static const IN6_ADDR zero;
if (memcmp( &key6->next_hop, &zero, sizeof(zero) ))
{
sockaddr.si_family = family;
sockaddr.Ipv6.sin6_addr = key6->next_hop;
}
}
if (sockaddr.si_family)
{
gw = heap_alloc_zero( sizeof(*gw) + sockaddr_size );
if (!gw)
{
err = ERROR_NOT_ENOUGH_MEMORY;
goto err;
}
gw->u.s.Length = sizeof(*gw);
gw->Address.lpSockaddr = (SOCKADDR *)(gw + 1);
gw->Address.iSockaddrLength = sockaddr_size;
memcpy( gw->Address.lpSockaddr, &sockaddr, sockaddr_size );
*gw_next = gw;
gw_next = &gw->Next;
}
}
if (flags & GAA_FLAG_INCLUDE_PREFIX)
{
memset( &sockaddr, 0, sizeof(sockaddr) );
if (family == WS_AF_INET)
{
if (!key4->next_hop.WS_s_addr)
{
sockaddr.si_family = family;
sockaddr.Ipv4.sin_addr = key4->prefix;
prefix_len = key4->prefix_len;
}
}
else
{
static const IN6_ADDR zero;
if (!memcmp( &key6->next_hop, &zero, sizeof(zero) ))
{
sockaddr.si_family = family;
sockaddr.Ipv6.sin6_addr = key6->prefix;
prefix_len = key6->prefix_len;
}
}
if (sockaddr.si_family)
{
prefix = heap_alloc_zero( sizeof(*prefix) + sockaddr_size );
if (!prefix)
{
err = ERROR_NOT_ENOUGH_MEMORY;
goto err;
}
prefix->u.s.Length = sizeof(*prefix);
prefix->Address.lpSockaddr = (SOCKADDR *)(prefix + 1);
prefix->Address.iSockaddrLength = sockaddr_size;
memcpy( prefix->Address.lpSockaddr, &sockaddr, sockaddr_size );
prefix->PrefixLength = prefix_len;
*prefix_next = prefix;
prefix_next = &prefix->Next;
}
}
}
if (num_v6addrs && !(flags & GAA_FLAG_SKIP_UNICAST))
{
IP_ADAPTER_UNICAST_ADDRESS *ua;
struct WS_sockaddr_in6 *sa;
aa->u1.s1.Ipv6Enabled = TRUE;
if (aa->FirstUnicastAddress)
{
for (ua = aa->FirstUnicastAddress; ua->Next; ua = ua->Next)
;
ua->Next = heap_alloc_zero( sizeof(IP_ADAPTER_UNICAST_ADDRESS) + sizeof(SOCKADDR_IN6) );
ua = ua->Next;
}
else
ua = aa->FirstUnicastAddress = heap_alloc_zero( sizeof(IP_ADAPTER_UNICAST_ADDRESS) + sizeof(SOCKADDR_IN6) );
for (i = 0; i < num_v6addrs; i++)
{
char addr_buf[46];
memset(ua, 0, sizeof(IP_ADAPTER_UNICAST_ADDRESS));
ua->u.s.Length = sizeof(IP_ADAPTER_UNICAST_ADDRESS);
ua->Address.iSockaddrLength = v6addrs[i].iSockaddrLength;
ua->Address.lpSockaddr = (SOCKADDR *)(ua + 1);
sa = (struct WS_sockaddr_in6 *)ua->Address.lpSockaddr;
memcpy(sa, v6addrs[i].lpSockaddr, sizeof(*sa));
TRACE("IPv6 %d/%d: %s\n", i + 1, num_v6addrs,
debugstr_ipv6(sa, addr_buf));
fill_unicast_addr_data(aa, ua);
ua->OnLinkPrefixLength = mask_v6_to_prefix(&v6masks[i]);
if (i < num_v6addrs - 1)
{
ua->Next = heap_alloc_zero( sizeof(IP_ADAPTER_UNICAST_ADDRESS) + sizeof(SOCKADDR_IN6) );
ua = ua->Next;
}
}
}
if (num_v4addrs && (flags & GAA_FLAG_INCLUDE_PREFIX))
{
IP_ADAPTER_PREFIX *prefix;
prefix = aa->FirstPrefix = heap_alloc_zero( sizeof(*prefix) + sizeof(SOCKADDR_IN) );
for (i = 0; i < num_v4addrs; i++)
{
char addr_buf[16];
struct WS_sockaddr_in *sa;
prefix->u.s.Length = sizeof(*prefix);
prefix->u.s.Flags = 0;
prefix->Next = NULL;
prefix->Address.iSockaddrLength = sizeof(struct sockaddr_in);
prefix->Address.lpSockaddr = (SOCKADDR *)(prefix + 1);
sa = (struct WS_sockaddr_in *)prefix->Address.lpSockaddr;
sa->sin_family = WS_AF_INET;
sa->sin_addr.S_un.S_addr = v4addrs[i] & v4masks[i];
sa->sin_port = 0;
prefix->PrefixLength = mask_v4_to_prefix(v4masks[i]);
TRACE("IPv4 network: %s/%u\n",
debugstr_ipv4((const in_addr_t *)&sa->sin_addr.S_un.S_addr, addr_buf),
prefix->PrefixLength);
if (i < num_v4addrs - 1)
{
prefix->Next = heap_alloc_zero( sizeof(*prefix) + sizeof(SOCKADDR_IN) );
prefix = prefix->Next;
}
}
}
if (num_v6addrs && (flags & GAA_FLAG_INCLUDE_PREFIX))
{
IP_ADAPTER_PREFIX *prefix;
if (aa->FirstPrefix)
{
for (prefix = aa->FirstPrefix; prefix->Next; prefix = prefix->Next)
;
prefix->Next = heap_alloc_zero( sizeof(*prefix) + sizeof(SOCKADDR_IN6) );
prefix = prefix->Next;
}
else
prefix = aa->FirstPrefix = heap_alloc_zero( sizeof(*prefix) + sizeof(SOCKADDR_IN6) );
for (i = 0; i < num_v6addrs; i++)
{
char addr_buf[46];
struct WS_sockaddr_in6 *sa;
const IN6_ADDR *addr, *mask;
prefix->u.s.Length = sizeof(*prefix);
prefix->u.s.Flags = 0;
prefix->Next = NULL;
prefix->Address.iSockaddrLength = sizeof(struct sockaddr_in6);
prefix->Address.lpSockaddr = (SOCKADDR *)(prefix + 1);
sa = (struct WS_sockaddr_in6 *)prefix->Address.lpSockaddr;
sa->sin6_family = WS_AF_INET6;
sa->sin6_port = 0;
sa->sin6_flowinfo = 0;
addr = &((struct WS_sockaddr_in6 *)v6addrs[i].lpSockaddr)->sin6_addr;
mask = &((struct WS_sockaddr_in6 *)v6masks[i].lpSockaddr)->sin6_addr;
for (j = 0; j < 8; j++) sa->sin6_addr.u.Word[j] = addr->u.Word[j] & mask->u.Word[j];
sa->sin6_scope_id = 0;
prefix->PrefixLength = mask_v6_to_prefix(&v6masks[i]);
TRACE("IPv6 network: %s/%u\n", debugstr_ipv6(sa, addr_buf), prefix->PrefixLength);
if (i < num_v6addrs - 1)
{
prefix->Next = heap_alloc_zero( sizeof(*prefix) + sizeof(SOCKADDR_IN6) );
prefix = prefix->Next;
}
}
}
getInterfaceMtuByName(name, &aa->Mtu);
getInterfaceStatusByName(name, &status);
if (status == MIB_IF_OPER_STATUS_OPERATIONAL) aa->OperStatus = IfOperStatusUp;
else if (status == MIB_IF_OPER_STATUS_NON_OPERATIONAL) aa->OperStatus = IfOperStatusDown;
else aa->OperStatus = IfOperStatusUnknown;
aa = aa->Next;
}
HeapFree(GetProcessHeap(), 0, routeTable);
HeapFree(GetProcessHeap(), 0, v6addrs);
HeapFree(GetProcessHeap(), 0, v6masks);
HeapFree(GetProcessHeap(), 0, v4addrs);
HeapFree(GetProcessHeap(), 0, v4masks);
return ERROR_SUCCESS;
err:
NsiFreeTable( key, NULL, NULL, NULL );
return err;
}
static DWORD call_families( DWORD (*fn)( IP_ADAPTER_ADDRESSES *aa, ULONG family, ULONG flags ),
IP_ADAPTER_ADDRESSES *aa, ULONG family, ULONG flags )
{
DWORD err;
if (family != WS_AF_INET)
{
err = fn( aa, WS_AF_INET6, flags );
if (err) return err;
}
if (family != WS_AF_INET6)
{
err = fn( aa, WS_AF_INET, flags );
if (err) return err;
}
return err;
}
static DWORD dns_servers_query_code( ULONG family )
@ -1461,20 +1250,21 @@ err:
static DWORD adapters_addresses_alloc( ULONG family, ULONG flags, IP_ADAPTER_ADDRESSES **info )
{
IP_ADAPTER_ADDRESSES *aa;
NET_LUID *luids;
struct nsi_ndis_ifinfo_rw *rw;
struct nsi_ndis_ifinfo_dynamic *dyn;
struct nsi_ndis_ifinfo_static *stat;
DWORD err, i, count, needed;
GUID guid;
char *str_ptr;
InterfaceIndexTable *table;
get_interface_indices( FALSE, &table );
if (!table || !table->numIndexes)
{
HeapFree(GetProcessHeap(), 0, table);
return ERROR_NO_DATA;
}
count = table->numIndexes;
err = NsiAllocateAndGetTable( 1, &NPI_MS_NDIS_MODULEID, NSI_NDIS_IFINFO_TABLE, (void **)&luids, sizeof(*luids),
(void **)&rw, sizeof(*rw), (void **)&dyn, sizeof(*dyn),
(void **)&stat, sizeof(*stat), &count, 0 );
if (err) return err;
needed = count * (sizeof(*aa) + ((CHARS_IN_GUID + 1) & ~1) + sizeof(IF_COUNTED_STRING));
if (!(flags & GAA_FLAG_SKIP_FRIENDLY_NAME)) needed += count * sizeof(IF_COUNTED_STRING);
needed = count * (sizeof(*aa) + ((CHARS_IN_GUID + 1) & ~1) + sizeof(stat->descr.String));
if (!(flags & GAA_FLAG_SKIP_FRIENDLY_NAME)) needed += count * sizeof(rw->alias.String);
aa = heap_alloc_zero( needed );
if (!aa)
@ -1486,18 +1276,52 @@ static DWORD adapters_addresses_alloc( ULONG family, ULONG flags, IP_ADAPTER_ADD
str_ptr = (char *)(aa + count);
for (i = 0; i < count; i++)
{
if ((err = adapterAddressesFromIndex(family, flags, table->indexes[i], aa + i, &str_ptr)))
{
HeapFree(GetProcessHeap(), 0, table);
return err;
}
aa[i].u.s.Length = sizeof(*aa);
aa[i].u.s.IfIndex = stat[i].if_index;
if (i < count - 1) aa[i].Next = aa + i + 1;
ConvertInterfaceLuidToGuid( luids + i, &guid );
ConvertGuidToStringA( &guid, str_ptr, CHARS_IN_GUID );
aa[i].AdapterName = str_ptr;
str_ptr += (CHARS_IN_GUID + 1) & ~1;
if_counted_string_copy( (WCHAR *)str_ptr, ARRAY_SIZE(stat[i].descr.String), &stat[i].descr );
aa[i].Description = (WCHAR *)str_ptr;
str_ptr += sizeof(stat[i].descr.String);
if (!(flags & GAA_FLAG_SKIP_FRIENDLY_NAME))
{
if_counted_string_copy( (WCHAR *)str_ptr, ARRAY_SIZE(rw[i].alias.String), &rw[i].alias );
aa[i].FriendlyName = (WCHAR *)str_ptr;
str_ptr += sizeof(rw[i].alias.String);
}
aa[i].PhysicalAddressLength = rw->phys_addr.Length;
if (aa[i].PhysicalAddressLength > sizeof(aa[i].PhysicalAddress)) aa[i].PhysicalAddressLength = 0;
memcpy( aa[i].PhysicalAddress, rw->phys_addr.Address, aa[i].PhysicalAddressLength );
aa[i].Mtu = dyn[i].mtu;
aa[i].IfType = stat[i].type;
aa[i].OperStatus = dyn[i].oper_status;
aa[i].TransmitLinkSpeed = dyn[i].xmit_speed;
aa[i].ReceiveLinkSpeed = dyn[i].rcv_speed;
aa[i].Luid = luids[i];
aa[i].NetworkGuid = rw[i].network_guid;
aa[i].ConnectionType = stat[i].conn_type;
}
if (!(flags & GAA_FLAG_SKIP_UNICAST))
{
err = call_families( unicast_addresses_alloc, aa, family, flags );
if (err) goto err;
}
if (flags & (GAA_FLAG_INCLUDE_ALL_GATEWAYS | GAA_FLAG_INCLUDE_PREFIX))
{
err = call_families( gateway_and_prefix_addresses_alloc, aa, family, flags );
if (err) goto err;
}
err = dns_info_alloc( aa, family, flags );
if (err) goto err;
err:
HeapFree(GetProcessHeap(), 0, table);
NsiFreeTable( luids, rw, dyn, stat );
if (!err) *info = aa;
else adapters_addresses_free( aa );
return err;
@ -1861,15 +1685,6 @@ err:
return err;
}
static void if_counted_string_copy( WCHAR *dst, unsigned int len, IF_COUNTED_STRING *src )
{
unsigned int copy = src->Length;
if (copy >= len * sizeof(WCHAR)) copy = 0;
memcpy( dst, src->String, copy );
memset( (char *)dst + copy, 0, len * sizeof(WCHAR) - copy );
}
static void if_row2_fill( MIB_IF_ROW2 *row, struct nsi_ndis_ifinfo_rw *rw, struct nsi_ndis_ifinfo_dynamic *dyn,
struct nsi_ndis_ifinfo_static *stat )
{