/*++ Copyright (c) 1994-1997 Microsoft Corporation Module Name: //KERNEL/RAZZLE3/src/sockets/tcpcmd/ipconfig/adaptlst.c Abstract: This module contains functions for retrieving adapter information from TCP/IP device driver Contents: GetAdapterList GetAdapterList2 AddIpAddress AddIpAddressString ConvertIpAddressToString CopyString (CleanDescription) Author: Richard L Firth (rfirth) 20-May-1994 Revision History: 20-May-1994 rfirth Created 30-Apr-97 MohsinA Cleaned Up. --*/ #include "precomp.h" #pragma hdrstop #define OVERFLOW_COUNT 10 // // prototypes // void CleanDescription(LPSTR); extern PIP_ADAPTER_ORDER_MAP APIENTRY GetAdapterOrderMap(); // // functions // /******************************************************************************* * * GetAdapterList * * Returns a linked list of IP_ADAPTER_INFO structures. The adapter info is * queried from the TCP/IP stack. Only those instances corresponding to * physical adapters are returned * * This function only fills in the information in the IP_ADAPTER_INFO * structure pertaining to the physical adapter (like MAC address, adapter * type, etc.) and IP address info * * ENTRY nothing * * EXIT nothing * * RETURNS Success - pointer to linked list of IP_ADAPTER_INFO structures, * 0 terminated * Failure - NULL * * ASSUMES * ******************************************************************************/ PIP_ADAPTER_INFO GetAdapterList() { TCP_REQUEST_QUERY_INFORMATION_EX req; TDIObjectID id; PIP_ADAPTER_INFO list = NULL, prev = NULL; PIP_ADAPTER_INFO this, UniList = NULL, tmp; UINT numberOfEntities; TDIEntityID* pEntity = NULL; TDIEntityID* entityList; UINT i; UINT j; DWORD status; DWORD inputLen; DWORD outputLen; PIP_ADAPTER_ORDER_MAP adapterOrderMap; PIP_UNIDIRECTIONAL_ADAPTER_ADDRESS pUniInfo=NULL; ULONG OutBufLen; // // get the list of entities supported by TCP/IP then make 2 passes on the // list. Pass 1 scans for IF_ENTITY's (interface entities perhaps?) which // describe adapter instances (physical and virtual). Once we have our list // of adapters, on pass 2 we look for CL_NL_ENTITY's (connection-less // network layer entities peut-etre?) which will give us the list of IP // addresses for the adapters we found in pass 1 // entityList = GetEntityList(&numberOfEntities); if (!entityList) { DEBUG_PRINT(("GetAdapterList: failed to get entity list\n")); return NULL; } adapterOrderMap = GetAdapterOrderMap(); if (!adapterOrderMap) { DEBUG_PRINT(("GetAdapterList: failed to get adapter order map\n")); ReleaseMemory(entityList); return NULL; } // ==================================================================== // pass 1 // ==================================================================== for (i = 0, pEntity = entityList; i < numberOfEntities; ++i, ++pEntity) { DEBUG_PRINT(("Pass 1: Entity %lx [%s] Instance %ld\n", pEntity->tei_entity, entity$(pEntity->tei_entity), pEntity->tei_instance )); if (pEntity->tei_entity == IF_ENTITY) { // // IF_ENTITY: this entity/instance describes an adapter // DWORD isMib; BYTE info[sizeof(IFEntry) + MAX_ADAPTER_DESCRIPTION_LENGTH + 1]; IFEntry* pIfEntry = (IFEntry*)info; int len; // // find out if this entity supports MIB requests // memset(&req, 0, sizeof(req)); id.toi_entity = *pEntity; id.toi_class = INFO_CLASS_GENERIC; id.toi_type = INFO_TYPE_PROVIDER; id.toi_id = ENTITY_TYPE_ID; req.ID = id; inputLen = sizeof(req); outputLen = sizeof(isMib); status = WsControl(IPPROTO_TCP, WSCNTL_TCPIP_QUERY_INFO, (LPVOID)&req, &inputLen, (LPVOID)&isMib, &outputLen ); if (status != TDI_SUCCESS) { // // unexpected results - bail out // DEBUG_PRINT(("GetAdapterList: WsControl(ENTITY_TYPE_ID): status = %ld, outputLen = %ld\n", status, outputLen )); // goto error_exit; continue; } if (isMib != IF_MIB) { // // entity doesn't support MIB requests - try another // DEBUG_PRINT(("GetAdapterList: Entity %lx, Instance %ld doesn't support MIB (%lx)\n", id.toi_entity.tei_entity, id.toi_entity.tei_instance, isMib )); continue; } // // MIB requests supported - query the adapter info // id.toi_class = INFO_CLASS_PROTOCOL; id.toi_id = IF_MIB_STATS_ID; memset(&req, 0, sizeof(req)); req.ID = id; inputLen = sizeof(req); outputLen = sizeof(info); status = WsControl(IPPROTO_TCP, WSCNTL_TCPIP_QUERY_INFO, (LPVOID)&req, &inputLen, (LPVOID)&info, &outputLen ); if (status != TDI_SUCCESS && status != ERROR_MORE_DATA) { // // unexpected results - bail out // DEBUG_PRINT(("GetAdapterList: WsControl(IF_MIB_STATS_ID) returns %ld\n", status )); // goto error_exit; continue; } #ifdef DBG if( MyTrace ){ print_IFEntry( "GetAdapterList", pIfEntry ); } #endif // // we only want physical adapters // if (!IS_INTERESTING_ADAPTER(pIfEntry)) { DEBUG_PRINT(("GetAdapterList: ignoring adapter #%ld [%s]\n", pIfEntry->if_index, if_type$(pIfEntry->if_type) )); continue; } // // got this adapter info ok. Create a new IP_ADAPTER_INFO and // fill in what we can // this = NEW(IP_ADAPTER_INFO); if (!this) { DEBUG_PRINT(("GetAdapterList: no mem for this IP_ADAPTER_INFO\n")); goto error_exit; } memset( this, 0, sizeof( IP_ADAPTER_INFO ) ); len = (int) min(MAX_ADAPTER_DESCRIPTION_LENGTH, (size_t)pIfEntry->if_descrlen); strncpy(this->Description, pIfEntry->if_descr, len); this->Description[len] = 0; // // if the last word of the description is " Adapter", remove it (its // redundant) and if the description is terminated with a period, // remove that too // // CleanDescription(this->Description); len = (int) min(MAX_ADAPTER_ADDRESS_LENGTH, (size_t)pIfEntry->if_physaddrlen); this->AddressLength = (BYTE)len; memcpy(this->Address, pIfEntry->if_physaddr, len); this->Index = (UINT)pIfEntry->if_index; this->Type = (UINT)pIfEntry->if_type; // // add this IP_ADAPTER_INFO to our list. // We build the list sorted according to the adapter order // specified for TCP/IP under its Linkage key. // In order to put this new entry in the right place in the list, // we determine its position in the adapter-order, store that // position in the (unused) 'ComboIndex' field, and then use that // index for comparison on subsequent insertions. // If this IP_ADAPTER_INFO doesn't appear in our list at all, // we put it at the end of the current list. // for (j = 0; j < adapterOrderMap->NumAdapters; j++) { if (adapterOrderMap->AdapterOrder[j] == this->Index) { break; } } // // 'j' now contains the 'order' for the new entry. // Put the entry in the right place in the list. // this->ComboIndex = j; for (prev = NULL, this->Next = list; this->Next; prev = this->Next, this->Next = this->Next->Next) { if (this->ComboIndex >= this->Next->ComboIndex) { continue; } else { break; } } if (prev) { prev->Next = this; } if (list == this->Next) { list = this; } } } OutBufLen = sizeof(IP_UNIDIRECTIONAL_ADAPTER_ADDRESS) + MAX_UNI_ADAPTERS*sizeof(IPAddr); pUniInfo = MALLOC(OutBufLen); if(!pUniInfo) { printf("GetAdapterList: IP_UNIDIRECTIONAL_ADAPTER_ADDRESS resource failure= %ld\n",status); DEBUG_PRINT(("GetAdapterList: IP_UNIDIRECTIONAL_ADAPTER_ADDRESS resource failure= %ld\n",status)); goto error_exit; } pUniInfo->NumAdapters = 0; status = GetUniDirectionalAdapterInfo(pUniInfo, &OutBufLen); if (status == ERROR_MORE_DATA) { OutBufLen = sizeof(IP_UNIDIRECTIONAL_ADAPTER_ADDRESS)+pUniInfo->NumAdapters*sizeof(IPAddr); pUniInfo = MALLOC(OutBufLen); if(!pUniInfo) { DEBUG_PRINT(("GetAdapterList: IP_UNIDIRECTIONAL_ADAPTER_ADDRESS resource failure= %ld\n",status)); goto error_exit; } status = GetUniDirectionalAdapterInfo(pUniInfo, &OutBufLen); } if(status != NO_ERROR) { DEBUG_PRINT(("GetAdapterList: GetUniDirectionalAdapterInfo returned status= %ld\n",status)); goto error_exit; } // ==================================================================== // pass 2 // ==================================================================== for (i = 0, pEntity = entityList; i < numberOfEntities; ++i, ++pEntity) { DEBUG_PRINT(("Pass 2: Entity %lx [%s] Instance %ld\n", pEntity->tei_entity, entity$(pEntity->tei_entity), pEntity->tei_instance )); if (pEntity->tei_entity == CL_NL_ENTITY) { IPSNMPInfo info; DWORD type; // // first off, see if this network layer entity supports IP // memset(&req, 0, sizeof(req)); id.toi_entity = *pEntity; id.toi_class = INFO_CLASS_GENERIC; id.toi_type = INFO_TYPE_PROVIDER; id.toi_id = ENTITY_TYPE_ID; req.ID = id; inputLen = sizeof(req); outputLen = sizeof(type); status = WsControl(IPPROTO_TCP, WSCNTL_TCPIP_QUERY_INFO, (LPVOID)&req, &inputLen, (LPVOID)&type, &outputLen ); if (status != TDI_SUCCESS) { // // unexpected results - bail out // DEBUG_PRINT(("GetAdapterList: WsControl(ENTITY_TYPE_ID): status = %ld, outputLen = %ld\n", status, outputLen )); // goto error_exit; continue; } if (type != CL_NL_IP) { // // nope, not IP - try next one // DEBUG_PRINT(("GetAdapterList: CL_NL_ENTITY #%ld not CL_NL_IP\n", pEntity->tei_instance )); continue; } // // okay, this NL provider supports IP. Let's get them addresses: // First we find out how many by getting the SNMP stats and looking // at the number of addresses supported by this interface // memset(&req, 0, sizeof(req)); id.toi_class = INFO_CLASS_PROTOCOL; id.toi_id = IP_MIB_STATS_ID; req.ID = id; inputLen = sizeof(req); outputLen = sizeof(info); status = WsControl(IPPROTO_TCP, WSCNTL_TCPIP_QUERY_INFO, (LPVOID)&req, &inputLen, (LPVOID)&info, &outputLen ); if ((status != TDI_SUCCESS) || (outputLen != sizeof(info))) { // // unexpected results - bail out // DEBUG_PRINT(("GetAdapterList: WsControl(IP_MIB_STATS_ID): status = %ld, outputLen = %ld\n", status, outputLen )); // goto error_exit; continue; } // // get the IP addresses & subnet masks // if (info.ipsi_numaddr) { // // this interface has some addresses. What are they? // LPVOID buffer; UINT numberOfAddresses; IPAddrEntry* pAddr; UINT i; outputLen = (info.ipsi_numaddr + OVERFLOW_COUNT) * sizeof(IPAddrEntry); buffer = (LPVOID)NEW_MEMORY((size_t)outputLen); if (!buffer) { DEBUG_PRINT(("GetAdapterList:NEW_MEMORY failed.\n" )); goto error_exit; } memset(&req, 0, sizeof(req)); id.toi_id = IP_MIB_ADDRTABLE_ENTRY_ID; req.ID = id; inputLen = sizeof(req); status = WsControl(IPPROTO_TCP, WSCNTL_TCPIP_QUERY_INFO, (LPVOID)&req, &inputLen, (LPVOID)buffer, &outputLen ); if (status != TDI_SUCCESS) { // // unexpected results - bail out // DEBUG_PRINT(("GetAdapterList: WsControl(IP_MIB_ADDRTABLE_ENTRY_ID): status = %ld, outputLen = %ld\n", status, outputLen )); // goto error_exit; ReleaseMemory((void*)buffer); continue; } // // now loop through this list of IP addresses, applying them // to the correct adapter // numberOfAddresses = min((UINT)(outputLen / sizeof(IPAddrEntry)), (UINT)info.ipsi_numaddr ); DEBUG_PRINT(("GetAdapterList: %d IP addresses\n", numberOfAddresses)); pAddr = (IPAddrEntry*)buffer; for (i = 0; i < numberOfAddresses; ++i, ++pAddr) { PIP_ADAPTER_INFO pAdapterInfo; DEBUG_PRINT(("GetAdapterList: IP address %d.%d.%d.%d, index %ld, context %ld\n", ((LPBYTE)&pAddr->iae_addr)[0] & 0xff, ((LPBYTE)&pAddr->iae_addr)[1] & 0xff, ((LPBYTE)&pAddr->iae_addr)[2] & 0xff, ((LPBYTE)&pAddr->iae_addr)[3] & 0xff, pAddr->iae_index, pAddr->iae_context )); for (pAdapterInfo = list; pAdapterInfo; pAdapterInfo = pAdapterInfo->Next) { if (pAdapterInfo->Index == (UINT)pAddr->iae_index) { DEBUG_PRINT(("GetAdapterList: adding IP address %d.%d.%d.%d, index %d, context %d\n", ((LPBYTE)&pAddr->iae_addr)[0] & 0xff, ((LPBYTE)&pAddr->iae_addr)[1] & 0xff, ((LPBYTE)&pAddr->iae_addr)[2] & 0xff, ((LPBYTE)&pAddr->iae_addr)[3] & 0xff, pAddr->iae_index, pAddr->iae_context )); // // Append the IP address to the list. // Note that this operation preserves the order // of the IP address list returned by TCP/IP. // This is important because that list contains // entries listed in the *reverse* of the order // specified for each adapter. A number of clients // depend upon this fact when calling this and // other API routines. // if (!AddIpAddress(&pAdapterInfo->IpAddressList, pAddr->iae_addr, pAddr->iae_mask, pAddr->iae_context )) { ReleaseMemory((void*)buffer); goto error_exit; } for (j = 0; j < pUniInfo->NumAdapters ; j++) { if (pAddr->iae_index == pUniInfo->Address[j] ) { // // Use DhcpEnabled field as a temporary // storage to remember the type // pAdapterInfo->DhcpEnabled = IF_TYPE_RECEIVE_ONLY; break; } } break; } } } ReleaseMemory((void*)buffer); } // // get the gateway server IP address(es) // if (info.ipsi_numroutes) { IPRouteEntry* routeTable; IPRouteEntry* pRoute; UINT numberOfRoutes; UINT i; int moreRoutes = TRUE; memset(&req, 0, sizeof(req)); id.toi_id = IP_MIB_RTTABLE_ENTRY_ID; req.ID = id; inputLen = sizeof(req); outputLen = sizeof(IPRouteEntry) * info.ipsi_numroutes; routeTable = NULL; // // the route table may have grown since we got the SNMP stats // while (moreRoutes) { DWORD previousOutputLen; previousOutputLen = outputLen; if (routeTable) { ReleaseMemory((void*)routeTable); routeTable = NULL; } routeTable = (IPRouteEntry*)NEW_MEMORY((size_t)outputLen); if (!routeTable) { goto error_exit; } status = WsControl(IPPROTO_TCP, WSCNTL_TCPIP_QUERY_INFO, (LPVOID)&req, &inputLen, (LPVOID)routeTable, &outputLen ); if (status != TDI_SUCCESS) { // // unexpected results - bail out // DEBUG_PRINT(("GetAdapterList: WsControl(IP_MIB_RTTABLE_ENTRY_ID): status = %ld, outputLen = %ld\n", status, outputLen )); if (status == ERROR_MORE_DATA) { TCP_REQUEST_QUERY_INFORMATION_EX statsReq; IPSNMPInfo statsInfo; DWORD inLen; DWORD outLen; memset(&statsReq, 0, sizeof(statsReq)); id.toi_id = IP_MIB_STATS_ID; statsReq.ID = id; inLen = sizeof(statsReq); outLen = sizeof(statsInfo); status = WsControl( IPPROTO_TCP, WSCNTL_TCPIP_QUERY_INFO, (LPVOID)&statsReq, &inLen, (LPVOID)&statsInfo, &outLen); if (status != TDI_SUCCESS || outLen != sizeof(statsInfo)) { ReleaseMemory((void*)routeTable); goto error_exit; } else { outputLen = sizeof(IPRouteEntry) * statsInfo.ipsi_numroutes; } } else { ReleaseMemory((void*)routeTable); goto error_exit; } } if (outputLen <= previousOutputLen) { moreRoutes = FALSE; } } numberOfRoutes = (UINT)(outputLen / sizeof(IPRouteEntry)); for (i = 0, pRoute = routeTable; i < numberOfRoutes; ++i, ++pRoute) { // // the gateway address has a destination of 0.0.0.0 // if (pRoute->ire_dest == INADDR_ANY) { PIP_ADAPTER_INFO pAdapterInfo = list; for (; pAdapterInfo; pAdapterInfo = pAdapterInfo->Next) { if (pAdapterInfo->Index == (UINT)pRoute->ire_index) { TRACE_PRINT(("GetAdapterList: gw=0x%08x.\n", pRoute->ire_nexthop )); if (!AddIpAddress(&pAdapterInfo->GatewayList, pRoute->ire_nexthop, // // gateway IP address doesn't // have corresponding IP mask // INADDR_ANY, 0 )) { ReleaseMemory((void*)routeTable); goto error_exit; } // MohsinA, 22-Jul-97. // break; } } } } ReleaseMemory((void*)routeTable); } } } // ==================================================================== ReleaseMemory((void*)entityList); ReleaseMemory(adapterOrderMap); // // If there are any unidirectional adapters // move them to the end of the list // tmp = list; if (pUniInfo->NumAdapters) { this = list; prev = NULL; while (this) { if (this->DhcpEnabled == IF_TYPE_RECEIVE_ONLY) { // // Remove "this" from the list // if (prev) { prev->Next = this->Next; } else { prev = this->Next; list = this->Next; } tmp = this->Next; // // Restore DhcbEnabled // this->DhcpEnabled = FALSE; // // Chain this to list of TV adapters // this->Next = UniList; UniList = this; this = tmp; } else { prev = this; this = this->Next; } } // // Insert UniList at the end. // if (prev) { prev->Next = UniList; } else { ASSERT(list == NULL); list = UniList; } } FREE(pUniInfo); return list; error_exit: DEBUG_PRINT(("GetAdapterList: <= failed\n")); if (entityList) { ReleaseMemory((void*)entityList); } if (adapterOrderMap) { ReleaseMemory(adapterOrderMap); } if (pUniInfo) { FREE(pUniInfo); } KillAdapterInfo(list); return NULL; } /******************************************************************************* * * AddIpAddress * * Adds an IP_ADDR_STRING to a list. If the input IP_ADDR_STRING is empty this * is filled in, else a new IP_ADDR_STRING is allocated and chained to the * input IP_ADDR_STRING * * ENTRY AddressList - pointer to IP_ADDR which may or may not already hold * an IP address * Address - IP address to add * Mask - corresponding IP subnet mask * Context - address context * * EXIT AddressList - updated with new info * * RETURNS Success - 1 * Failure - 0 * * ASSUMES 1. INADDR_ANY (ulong 0) indicates inactive IP address * ******************************************************************************/ int AddIpAddress(PIP_ADDR_STRING AddressList, DWORD Address, DWORD Mask, DWORD Context) { PIP_ADDR_STRING ipAddr; if (AddressList->IpAddress.String[0]) { for (ipAddr = AddressList; ipAddr->Next; ipAddr = ipAddr->Next) { ; } ipAddr->Next = NEW(IP_ADDR_STRING); if (!ipAddr->Next) { DEBUG_PRINT(("AddIpAddress: failed to allocate memory for IP_ADDR_STRING\n")); return FALSE; } ipAddr = ipAddr->Next; } else { ipAddr = AddressList; } ConvertIpAddressToString(Address, ipAddr->IpAddress.String); ConvertIpAddressToString(Mask, ipAddr->IpMask.String); ipAddr->Context = Context; ipAddr->Next = NULL; return TRUE; } /******************************************************************************* * * AddIpAddressString * * Same as AddIpAddress, except the arguments are already converted to strings * * ENTRY AddressList - pointer to IP_ADDR which may or may not already hold * an IP address * Address - IP address to add, as a string * Mask - corresponding IP subnet mask, as a string * * EXIT AddressList - updated with new info * * RETURNS Success - 1 * Failure - 0 * * ASSUMES nothing * ******************************************************************************/ int AddIpAddressString(PIP_ADDR_STRING AddressList, LPSTR Address, LPSTR Mask) { PIP_ADDR_STRING ipAddr; if (AddressList->IpAddress.String[0]) { for (ipAddr = AddressList; ipAddr->Next; ipAddr = ipAddr->Next) { if (!strncmp(ipAddr->IpAddress.String, Address, sizeof(ipAddr->IpAddress.String))) { return FALSE; } } if (!strncmp(ipAddr->IpAddress.String, Address, sizeof(ipAddr->IpAddress.String))) { return FALSE; } ipAddr->Next = NEW(IP_ADDR_STRING); if (!ipAddr->Next) { DEBUG_PRINT(("AddIpAddressString: failed to allocate memory for IP_ADDR_STRING\n")); return FALSE; } ipAddr = ipAddr->Next; } else { ipAddr = AddressList; } CopyString(ipAddr->IpAddress.String, sizeof(ipAddr->IpAddress.String), Address); CopyString(ipAddr->IpMask.String, sizeof(ipAddr->IpMask.String), Mask); return TRUE; } /******************************************************************************* * * ConvertIpAddressToString * * Converts a DWORD IP address or subnet mask to dotted decimal string * * ENTRY IpAddress - IP Address to convert * String - pointer to place to store dotted decimal string * * EXIT String contains ASCII representation of IpAddress * * RETURNS nothing * * ASSUMES 1. IP address fits in a DWORD * ******************************************************************************/ VOID ConvertIpAddressToString(DWORD IpAddress, LPSTR String) { IP_ADDRESS ipAddr; ipAddr.d = IpAddress; sprintf(String, "%d.%d.%d.%d", ipAddr.b[0], ipAddr.b[1], ipAddr.b[2], ipAddr.b[3] ); } /******************************************************************************* * * CopyString * * Copies a string to a buffer. If the buffer would overflow, the string is * truncated * * ENTRY Destination - destination buffer to copy to * DestinationLength - size of Destination * Source - source string to copy * * EXIT Destination updated * * RETURNS nothing * * ASSUMES * ******************************************************************************/ VOID CopyString(LPSTR Destination, DWORD DestinationLength, LPSTR Source) { DWORD maximumCharacters = min(DestinationLength - 1, STRLEN(Source)); strncpy(Destination, Source, maximumCharacters); Destination[maximumCharacters] = '\0'; } /******************************************************************************* * * CleanDescription * * Given an adapter description string retrieved from TCP/IP, remove the * trailing substring " Adapter". If there is a trailing period, remove that * too * * ENTRY String - pointer to description string to clean up * * EXIT String - possibly bits removed * * RETURNS voidsville * * ASSUMES * ******************************************************************************/ void CleanDescription(LPSTR String) { int len = STRLEN(String); if (String[len - 1] == '.') { String[--len] = 0; } if (!STRICMP(String + len - (sizeof(" Adapter") - 1), " Adapter")) { len -= sizeof(" Adapter") - 1; String[len] = 0; } }