Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1011 lines
31 KiB

/*++
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;
}
}