iphlpapi: Reimplement interface enumeration.

Implement interface enumeration based on if_nameindex, based on a
suggestion by Michael Ost
This commit is contained in:
Juan Lang 2006-01-27 19:19:32 +01:00 committed by Alexandre Julliard
parent d4c62162e9
commit 540dca3259
3 changed files with 103 additions and 316 deletions

View File

@ -15,22 +15,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* *
* Implementation notes * 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: * FIXME:
* - I don't support IPv6 addresses here, since SIOCGIFCONF can't return them * - I don't support IPv6 addresses here, since SIOCGIFCONF can't return them
* *
@ -121,69 +105,8 @@
#define INDEX_IS_LOOPBACK 0x00800000 #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 */ /* 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) static int isLoopbackInterface(int fd, const char *name)
{ {
int ret = 0; int ret = 0;
@ -198,243 +121,96 @@ static int isLoopbackInterface(int fd, const char *name)
return ret; return ret;
} }
static void countInterfaces(int fd, caddr_t buf, size_t len) /* The comments say MAX_ADAPTER_NAME is required, but really only IF_NAMESIZE
{ * bytes are necessary.
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) char *getInterfaceNameByIndex(DWORD index, char *name)
{ {
if (map && name) { return if_indextoname(index, 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 getInterfaceIndexByName(const char *name, PDWORD index)
{ {
DWORD ndx, ret; DWORD ret;
BOOL found = FALSE; unsigned int idx;
if (!name) if (!name)
return ERROR_INVALID_PARAMETER; return ERROR_INVALID_PARAMETER;
if (!index) if (!index)
return ERROR_INVALID_PARAMETER; return ERROR_INVALID_PARAMETER;
idx = if_nametoindex(name);
EnterCriticalSection(&mapCS); if (idx) {
for (ndx = 0; !found && gNonLoopbackInterfaceMap && *index = idx;
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; ret = NO_ERROR;
}
else else
ret = ERROR_INVALID_DATA; ret = ERROR_INVALID_DATA;
return ret; return ret;
} }
static void addMapEntriesToIndexTable(InterfaceIndexTable *table, DWORD getNumNonLoopbackInterfaces(void)
const InterfaceNameMap *map)
{ {
if (table && map) { DWORD numInterfaces;
DWORD ndx; int fd = socket(PF_INET, SOCK_DGRAM, 0);
for (ndx = 0; ndx < map->nextAvailable && if (fd != -1) {
table->numIndexes < table->numAllocated; ndx++) struct if_nameindex *indexes = if_nameindex();
if (map->table[ndx].inUse) {
DWORD externalNdx = ndx;
if (map == gLoopbackInterfaceMap) if (indexes) {
externalNdx |= INDEX_IS_LOOPBACK; struct if_nameindex *p;
table->indexes[table->numIndexes++] = externalNdx;
} 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) InterfaceIndexTable *getInterfaceIndexTable(void)
{ {
DWORD numInterfaces; DWORD numInterfaces;
InterfaceIndexTable *ret; InterfaceIndexTable *ret;
struct if_nameindex *indexes = if_nameindex();
EnterCriticalSection(&mapCS);
numInterfaces = getNumInterfaces(); if (indexes) {
ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, struct if_nameindex *p;
sizeof(InterfaceIndexTable) + (numInterfaces - 1) * sizeof(DWORD));
if (ret) { for (p = indexes, numInterfaces = 0; p && p->if_name; p++)
ret->numAllocated = numInterfaces; numInterfaces++;
addMapEntriesToIndexTable(ret, gNonLoopbackInterfaceMap); ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
addMapEntriesToIndexTable(ret, gLoopbackInterfaceMap); sizeof(InterfaceIndexTable) + (numInterfaces - 1) * sizeof(DWORD));
if (ret) {
for (p = indexes; p && p->if_name; p++)
ret->indexes[ret->numIndexes++] = p->if_index;
}
if_freenameindex(indexes);
} }
LeaveCriticalSection(&mapCS); else
ret = NULL;
return ret; return ret;
} }
@ -442,16 +218,32 @@ InterfaceIndexTable *getNonLoopbackInterfaceIndexTable(void)
{ {
DWORD numInterfaces; DWORD numInterfaces;
InterfaceIndexTable *ret; InterfaceIndexTable *ret;
int fd = socket(PF_INET, SOCK_DGRAM, 0);
EnterCriticalSection(&mapCS); if (fd != -1) {
numInterfaces = getNumNonLoopbackInterfaces(); struct if_nameindex *indexes = if_nameindex();
ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
sizeof(InterfaceIndexTable) + (numInterfaces - 1) * sizeof(DWORD)); if (indexes) {
if (ret) { struct if_nameindex *p;
ret->numAllocated = numInterfaces;
addMapEntriesToIndexTable(ret, gNonLoopbackInterfaceMap); for (p = indexes, numInterfaces = 0; p && p->if_name; p++)
if (!isLoopbackInterface(fd, p->if_name))
numInterfaces++;
ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
sizeof(InterfaceIndexTable) + (numInterfaces - 1) * sizeof(DWORD));
if (ret) {
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);
} }
LeaveCriticalSection(&mapCS); else
ret = NULL;
return ret; return ret;
} }
@ -734,7 +526,8 @@ DWORD getInterfacePhysicalByName(const char *name, PDWORD len, PBYTE addr,
DWORD getInterfacePhysicalByIndex(DWORD index, PDWORD len, PBYTE addr, DWORD getInterfacePhysicalByIndex(DWORD index, PDWORD len, PBYTE addr,
PDWORD type) PDWORD type)
{ {
const char *name = getInterfaceNameByIndex(index); char nameBuf[IF_NAMESIZE];
char *name = getInterfaceNameByIndex(index, nameBuf);
if (name) if (name)
return getInterfacePhysicalByName(name, len, addr, type); return getInterfacePhysicalByName(name, len, addr, type);
@ -848,7 +641,8 @@ DWORD getInterfaceEntryByName(const char *name, PMIB_IFROW entry)
DWORD getInterfaceEntryByIndex(DWORD index, PMIB_IFROW entry) DWORD getInterfaceEntryByIndex(DWORD index, PMIB_IFROW entry)
{ {
const char *name = getInterfaceNameByIndex(index); char nameBuf[IF_NAMESIZE];
char *name = getInterfaceNameByIndex(index, nameBuf);
if (name) if (name)
return getInterfaceEntryByName(name, entry); return getInterfaceEntryByName(name, entry);

View File

@ -42,20 +42,12 @@
#define MAX_INTERFACE_PHYSADDR 8 #define MAX_INTERFACE_PHYSADDR 8
#define MAX_INTERFACE_DESCRIPTION 256 #define MAX_INTERFACE_DESCRIPTION 256
/* Call before using the functions in this module */
void interfaceMapInit(void);
/* Call to free resources allocated in interfaceMapInit() */
void interfaceMapFree(void);
DWORD getNumInterfaces(void); DWORD getNumInterfaces(void);
DWORD getNumNonLoopbackInterfaces(void); DWORD getNumNonLoopbackInterfaces(void);
/* A table of interface indexes, see get*InterfaceTable(). Ignore numAllocated, /* A table of interface indexes, see get*InterfaceTable(). */
* it's used during the creation of the table.
*/
typedef struct _InterfaceIndexTable { typedef struct _InterfaceIndexTable {
DWORD numIndexes; DWORD numIndexes;
DWORD numAllocated;
DWORD indexes[1]; DWORD indexes[1];
} InterfaceIndexTable; } InterfaceIndexTable;
@ -70,10 +62,10 @@ InterfaceIndexTable *getNonLoopbackInterfaceIndexTable(void);
/* ByName/ByIndex versions of various getter functions. */ /* ByName/ByIndex versions of various getter functions. */
/* can be used as quick check to see if you've got a valid index, returns NULL /* can be used as quick check to see if you've got a valid index, returns NULL
* if not. The buffer's only valid till the next call, so copy it right away * if not. Overwrites your buffer, which should be at least of size
* if you care. * MAX_ADAPTER_NAME.
*/ */
const char *getInterfaceNameByIndex(DWORD index); char *getInterfaceNameByIndex(DWORD index, char *name);
/* Fills index with the index of name, if found. Returns /* Fills index with the index of name, if found. Returns
* ERROR_INVALID_PARAMETER if name or index is NULL, ERROR_INVALID_DATA if name * ERROR_INVALID_PARAMETER if name or index is NULL, ERROR_INVALID_DATA if name

View File

@ -55,11 +55,9 @@ BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
switch (fdwReason) { switch (fdwReason) {
case DLL_PROCESS_ATTACH: case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls( hinstDLL ); DisableThreadLibraryCalls( hinstDLL );
interfaceMapInit();
break; break;
case DLL_PROCESS_DETACH: case DLL_PROCESS_DETACH:
interfaceMapFree();
break; break;
} }
return TRUE; return TRUE;
@ -714,9 +712,7 @@ DWORD WINAPI GetAdaptersInfo(PIP_ADAPTER_INFO pAdapterInfo, PULONG pOutBufLen)
BOOL firstIPAddr = TRUE; BOOL firstIPAddr = TRUE;
/* on Win98 this is left empty, but whatever */ /* on Win98 this is left empty, but whatever */
lstrcpynA(ptr->AdapterName, getInterfaceNameByIndex(table->indexes[ndx], ptr->AdapterName);
getInterfaceNameByIndex(table->indexes[ndx]),
MAX_ADAPTER_NAME_LENGTH+1);
getInterfacePhysicalByIndex(table->indexes[ndx], &addrLen, getInterfacePhysicalByIndex(table->indexes[ndx], &addrLen,
ptr->Address, &type); ptr->Address, &type);
/* MS defines address length and type as UINT in some places and /* MS defines address length and type as UINT in some places and
@ -929,13 +925,14 @@ DWORD WINAPI GetIcmpStatistics(PMIB_ICMP pStats)
DWORD WINAPI GetIfEntry(PMIB_IFROW pIfRow) DWORD WINAPI GetIfEntry(PMIB_IFROW pIfRow)
{ {
DWORD ret; DWORD ret;
const char *name; char nameBuf[MAX_ADAPTER_NAME];
char *name;
TRACE("pIfRow %p\n", pIfRow); TRACE("pIfRow %p\n", pIfRow);
if (!pIfRow) if (!pIfRow)
return ERROR_INVALID_PARAMETER; return ERROR_INVALID_PARAMETER;
name = getInterfaceNameByIndex(pIfRow->dwIndex); name = getInterfaceNameByIndex(pIfRow->dwIndex, nameBuf);
if (name) { if (name) {
ret = getInterfaceEntryByName(name, pIfRow); ret = getInterfaceEntryByName(name, pIfRow);
if (ret == NO_ERROR) if (ret == NO_ERROR)
@ -1044,6 +1041,9 @@ DWORD WINAPI GetIfTable(PMIB_IFTABLE pIfTable, PULONG pdwSize, BOOL bOrder)
* RETURNS * RETURNS
* Success: NO_ERROR * Success: NO_ERROR
* Failure: error code from winerror.h * Failure: error code from winerror.h
*
* BUGS
* MSDN states this should return non-loopback interfaces only.
*/ */
DWORD WINAPI GetInterfaceInfo(PIP_INTERFACE_INFO pIfTable, PULONG dwOutBufLen) DWORD WINAPI GetInterfaceInfo(PIP_INTERFACE_INFO pIfTable, PULONG dwOutBufLen)
{ {
@ -1073,6 +1073,7 @@ DWORD WINAPI GetInterfaceInfo(PIP_INTERFACE_INFO pIfTable, PULONG dwOutBufLen)
} }
else { else {
DWORD ndx; DWORD ndx;
char nameBuf[MAX_ADAPTER_NAME];
*dwOutBufLen = size; *dwOutBufLen = size;
pIfTable->NumAdapters = 0; pIfTable->NumAdapters = 0;
@ -1081,7 +1082,7 @@ DWORD WINAPI GetInterfaceInfo(PIP_INTERFACE_INFO pIfTable, PULONG dwOutBufLen)
WCHAR *assigner; WCHAR *assigner;
pIfTable->Adapter[ndx].Index = table->indexes[ndx]; pIfTable->Adapter[ndx].Index = table->indexes[ndx];
name = getInterfaceNameByIndex(table->indexes[ndx]); name = getInterfaceNameByIndex(table->indexes[ndx], nameBuf);
for (walker = name, assigner = pIfTable->Adapter[ndx].Name; for (walker = name, assigner = pIfTable->Adapter[ndx].Name;
walker && *walker && walker && *walker &&
assigner - pIfTable->Adapter[ndx].Name < MAX_ADAPTER_NAME - 1; assigner - pIfTable->Adapter[ndx].Name < MAX_ADAPTER_NAME - 1;