windows-nt/Source/XPSP1/NT/net/rras/dim/admindll/enumlan.c
2020-09-26 16:20:57 +08:00

749 lines
18 KiB
C

/*
File enumlan.c
Implementation of functions to enumerate lan interfaces
on a given machine. This implementation actually bypasses
netman and gets the information using setup api's.
Paul Mayfield, 5/13/98
*/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <winerror.h>
#include <netcfgx.h>
#include <netcon.h>
#include <setupapi.h>
#include <devguid.h>
#include <cfgmgr32.h>
#include <mprapi.h>
#include "rtcfg.h"
#include "enumlan.h"
#define EL_MAP_GROW_FACTOR 25
//
// Determines whether a given machine is nt40
//
DWORD
IsNt40Machine (
IN HKEY hkeyMachine,
OUT PBOOL pbIsNt40);
//
// Structure represents a growable array of name map nodes.
//
typedef struct _EL_NAMEMAP
{
DWORD dwNumNodes;
EL_ADAPTER_INFO *pNodes;
} EL_NAMEMAP;
//
// Structure contains data manipulated by ElIsNetcfgDevice
//
typedef struct _EL_ISNETCFGDEV_INFO
{
EL_ADAPTER_INFO* pAdapter; // IN OUT
WCHAR pszPnpInstance[MAX_PATH]; // OUT
} EL_ISNETCFGDEV_INFO;
//
// Structure contains data manipulated by ElGetAdapterStatus
//
typedef struct _EL_ADAPTER_STATUS_INFO
{
EL_ADAPTER_INFO* pAdapter; // IN OUT
HANDLE hkCmMachine; // IN
PWCHAR pszPnpInstance; // IN
} EL_ADAPTER_STATUS_INFO;
//
// Defines a filter function (used by lan adapter enumeration)
//
typedef
DWORD
(*DevFilterFuncPtr)(
HKEY,
HKEY,
HANDLE,
PBOOL);
//
// Stolen from netcfg project
//
#define IA_INSTALLED 1
const WCHAR c_szRegKeyInterfacesFromInstance[] = L"Ndi\\Interfaces";
const WCHAR c_szRegValueUpperRange[] = L"UpperRange";
const WCHAR c_szBiNdis4[] = L"ndis4";
const WCHAR c_szBiNdis5[] = L"ndis5";
const WCHAR c_szBiNdisAtm[] = L"ndisatm";
const WCHAR c_szBiNdis1394[] = L"ndis1394";
const WCHAR c_szCharacteristics[] = L"Characteristics";
const WCHAR c_szRegValueNetCfgInstanceId[] = L"NetCfgInstanceID";
const WCHAR c_szRegValueInstallerAction[] = L"InstallerAction";
const WCHAR c_szRegKeyConnection[] = L"Connection";
const WCHAR c_szRegValueConName[] = L"Name";
const WCHAR c_szRegValuePnpInstanceId[] = L"PnpInstanceID";
const WCHAR c_szRegKeyComponentClasses[] =
L"SYSTEM\\CurrentControlSet\\Control\\Network";
//
// Maps a CM_PROB_* value to a EL_STATUS_* value
//
DWORD
ElMapCmStatusToElStatus(
IN DWORD dwCmStatus,
OUT LPDWORD lpdwElStatus)
{
return NO_ERROR;
}
//
// Adapted version of HrIsLanCapableAdapterFromHkey determines
// whether an adapter is lan capable based on its registry key.
//
DWORD
ElIsLanAdapter(
IN HKEY hkMachine,
IN HKEY hkey,
OUT HANDLE hData,
OUT PBOOL pbIsLan)
{
HKEY hkeyInterfaces;
WCHAR pszBuf[256], *pszCur, *pszEnd;
DWORD dwErr, dwType = REG_SZ, dwSize = sizeof(pszBuf);
*pbIsLan = FALSE;
// Open the interfaces key
dwErr = RegOpenKeyEx( hkey,
c_szRegKeyInterfacesFromInstance,
0,
KEY_READ,
&hkeyInterfaces);
if (dwErr != ERROR_SUCCESS)
return dwErr;
// Read in the upper range
dwErr = RegQueryValueExW (hkeyInterfaces,
c_szRegValueUpperRange,
NULL,
&dwType,
(LPBYTE)pszBuf,
&dwSize);
if (dwErr != ERROR_SUCCESS)
return NO_ERROR;
// See if this buffer has the magic strings in it
pszCur = pszBuf;
while (TRUE) {
pszEnd = wcsstr(pszCur, L",");
if (pszEnd != NULL)
*pszEnd = (WCHAR)0;
if ((lstrcmpi (pszCur, c_szBiNdis4) == 0) ||
(lstrcmpi (pszCur, c_szBiNdis5) == 0) ||
(lstrcmpi (pszCur, c_szBiNdis1394) == 0) ||
(lstrcmpi (pszCur, c_szBiNdisAtm) == 0))
{
*pbIsLan = TRUE;
break;
}
if (pszEnd == NULL)
break;
else
pszCur = pszEnd + 1;
}
RegCloseKey(hkeyInterfaces);
return NO_ERROR;
}
//
// Filters netcfg devices. If a device passes this filter
// it will have its guid and freindly name returned through
// the hData parameter (option user defined data).
//
DWORD
ElIsNetcfgDevice(
IN HKEY hkMachine,
IN HKEY hkDev,
OUT HANDLE hData,
OUT PBOOL pbOk)
{
EL_ISNETCFGDEV_INFO* pInfo = (EL_ISNETCFGDEV_INFO*)hData;
EL_ADAPTER_INFO *pNode = pInfo->pAdapter;
GUID Guid = GUID_DEVCLASS_NET;
WCHAR pszBuf[1024], pszPath[256], pszClassGuid[256];
DWORD dwErr = NO_ERROR, dwType = REG_SZ, dwSize = sizeof(pszBuf), dwAction;
HKEY hkeyNetCfg = NULL;
*pbOk = FALSE;
// Read in the netcfg instance
dwErr = RegQueryValueExW (
hkDev,
c_szRegValueNetCfgInstanceId,
NULL,
&dwType,
(LPBYTE)pszBuf,
&dwSize);
if (dwErr != NO_ERROR)
{
return dwErr;
}
// Generate path in registry for lookup
StringFromGUID2(
&Guid,
pszClassGuid,
sizeof(pszClassGuid));
wsprintf(
pszPath,
L"%s\\%s\\%s\\%s",
c_szRegKeyComponentClasses,
pszClassGuid,
pszBuf,
c_szRegKeyConnection);
do
{
// Open the key
dwErr = RegOpenKeyEx(
hkMachine,
pszPath,
0,
KEY_READ,
&hkeyNetCfg);
if (dwErr != ERROR_SUCCESS)
{
break;
}
// Pass the filter
*pbOk = TRUE;
// Store the guid
pszBuf[wcslen(pszBuf) - 1] = (WCHAR)0;
if (UuidFromString(pszBuf + 1, &(pNode->guid)) != RPC_S_OK)
return ERROR_NOT_ENOUGH_MEMORY;
// Read in the adapter name
//
dwType = REG_SZ;
dwSize = sizeof(pszBuf);
dwErr = RegQueryValueEx(
hkeyNetCfg,
c_szRegValueConName,
NULL,
&dwType,
(LPBYTE)pszBuf,
&dwSize);
if (dwErr == ERROR_SUCCESS)
{
pNode->pszName = SysAllocString(pszBuf);
if (pNode->pszName == NULL)
{
dwErr = ERROR_NOT_ENOUGH_MEMORY;
break;
}
}
// Read in the adapter pnp instance id
//
dwType = REG_SZ;
dwSize = sizeof(pInfo->pszPnpInstance);
dwErr = RegQueryValueEx(
hkeyNetCfg,
c_szRegValuePnpInstanceId,
NULL,
&dwType,
(LPBYTE)(pInfo->pszPnpInstance),
&dwSize);
if (dwErr != ERROR_SUCCESS)
{
break;
}
} while (FALSE);
// Cleanup
{
if (hkeyNetCfg)
{
RegCloseKey(hkeyNetCfg);
}
}
return dwErr;
}
//
// Filters hidden devices
//
DWORD
ElIsNotHiddenDevice (
IN HKEY hkMachine,
IN HKEY hkDev,
OUT HANDLE hData,
OUT PBOOL pbOk)
{
DWORD dwErr, dwType = REG_DWORD,
dwSize = sizeof(DWORD), dwChars;
dwErr = RegQueryValueEx ( hkDev,
c_szCharacteristics,
NULL,
&dwType,
(LPBYTE)&dwChars,
&dwSize);
if (dwErr != ERROR_SUCCESS)
return dwErr;
*pbOk = !(dwChars & NCF_HIDDEN);
return NO_ERROR;
}
//
// Filter that simply loads the adapter status
//
DWORD
ElGetAdapterStatus(
IN HKEY hkMachine,
IN HKEY hkDev,
OUT HANDLE hData,
OUT PBOOL pbOk)
{
EL_ADAPTER_STATUS_INFO* pInfo = (EL_ADAPTER_STATUS_INFO*)hData;
DEVINST DevInst;
CONFIGRET cr = CR_SUCCESS;
ULONG ulStatus = 0, ulProblem = 0;
// Validate parameters
//
if (pInfo == NULL)
{
return ERROR_INVALID_PARAMETER;
}
// Find the device
//
cr = CM_Locate_DevNode_ExW(
&DevInst,
pInfo->pszPnpInstance,
CM_LOCATE_DEVNODE_NORMAL,
pInfo->hkCmMachine);
if (cr != CR_SUCCESS)
{
return ERROR_CAN_NOT_COMPLETE;
}
// Get the device status
//
cr = CM_Get_DevNode_Status_Ex(
&ulStatus,
&ulProblem,
DevInst,
0,
pInfo->hkCmMachine);
if (cr != CR_SUCCESS)
{
return ERROR_CAN_NOT_COMPLETE;
}
// Map CM's status to our own
//
switch (ulProblem)
{
// No problem, we're connected
case 0:
pInfo->pAdapter->dwStatus = EL_STATUS_OK;
break;
// Device not present
case CM_PROB_DEVICE_NOT_THERE:
case CM_PROB_MOVED:
pInfo->pAdapter->dwStatus = EL_STATUS_NOT_THERE;
break;
// Device was disabled via Device Manager
case CM_PROB_HARDWARE_DISABLED:
pInfo->pAdapter->dwStatus = EL_STATUS_HWDISABLED;
break;
// Device was disconnected
case CM_PROB_DISABLED:
pInfo->pAdapter->dwStatus = EL_STATUS_DISABLED;
break;
// All other problems
default:
pInfo->pAdapter->dwStatus = EL_STATUS_OTHER;
break;
}
// Make sure this device passes the filter
//
*pbOk = TRUE;
return NO_ERROR;
}
//
// Returns TRUE if the given device passes the filter.
// Returns FALSE otherwise.
//
BOOL
ElDevicePassesFilter (
IN HKEY hkMachine,
IN HKEY hkDev,
IN HANDLE hData,
IN DevFilterFuncPtr pFilter)
{
BOOL bOk = TRUE;
DWORD dwErr;
dwErr = (*pFilter)(hkMachine, hkDev, hData, &bOk);
if ((dwErr == NO_ERROR) && (bOk == TRUE))
return TRUE;
return FALSE;
}
//
// Allocates additional space in a EL_NAMEMAP
//
DWORD
ElEnlargeMap (
IN OUT EL_NAMEMAP * pMap,
DWORD dwAmount)
{
EL_ADAPTER_INFO * pNewNodes;
DWORD dwNewSize, i;
// Figure out the new size
dwNewSize = pMap->dwNumNodes + dwAmount;
// Resize the array
pNewNodes = (EL_ADAPTER_INFO *) Malloc (dwNewSize * sizeof(EL_ADAPTER_INFO));
if (!pNewNodes)
return ERROR_NOT_ENOUGH_MEMORY;
ZeroMemory(pNewNodes, dwNewSize * sizeof(EL_ADAPTER_INFO));
// Initialize the arrays.
CopyMemory(pNewNodes, pMap->pNodes, pMap->dwNumNodes * sizeof(EL_ADAPTER_INFO));
// Free old data if needed
if (pMap->dwNumNodes)
Free (pMap->pNodes);
// Assign the new arrays
pMap->pNodes = pNewNodes;
pMap->dwNumNodes = dwNewSize;
return NO_ERROR;
}
//
// Find out if given server is NT 4
//
DWORD
ElIsNt40Machine (
IN PWCHAR pszMachine,
OUT PBOOL pbNt40,
OUT HKEY* phkMachine)
{
DWORD dwErr;
dwErr = RegConnectRegistry (
pszMachine,
HKEY_LOCAL_MACHINE,
phkMachine);
if (dwErr != ERROR_SUCCESS)
return dwErr;
return IsNt40Machine (*phkMachine, pbNt40);
}
//
// Obtains the map of connection names to guids on the given server
// from its netman service.
//
// Parameters:
// pszServer: Server on which to obtain map (NULL = local)
// ppMap: Returns array of EL_ADAPTER_INFO's
// lpdwCount Returns number of elements read into ppMap
// pbNt40: Returns whether server is nt4 installation
//
DWORD
ElEnumLanAdapters (
IN PWCHAR pszServer,
OUT EL_ADAPTER_INFO ** ppMap,
OUT LPDWORD lpdwNumNodes,
OUT PBOOL pbNt40 )
{
GUID DevGuid = GUID_DEVCLASS_NET;
SP_DEVINFO_DATA Device;
HDEVINFO hDevInfo = NULL;
HKEY hkDev = NULL, hkMachine = NULL;
DWORD dwErr = NO_ERROR, dwIndex, dwTotal, dwSize;
EL_NAMEMAP Map;
WCHAR pszMachine[512], pszTemp[512];
HANDLE hkCmMachine = NULL;
EL_ADAPTER_STATUS_INFO AdapterStatusInfo;
EL_ISNETCFGDEV_INFO IsNetCfgDevInfo;
CONFIGRET cr = CR_SUCCESS;
PMPR_IPINIP_INTERFACE_0 pIpIpTable;
DWORD dwIpIpCount;
// Validate parameters
if (!ppMap || !lpdwNumNodes || !pbNt40)
{
return ERROR_INVALID_PARAMETER;
}
*pbNt40 = FALSE;
// Initialize
//
ZeroMemory(&Map, sizeof(EL_NAMEMAP));
do
{
// Prepare the name of the computer
wcscpy(pszMachine, L"\\\\");
if (pszServer)
{
if (*pszServer == L'\\')
{
wcscpy(pszMachine, pszServer);
}
else
{
wcscat(pszMachine, pszServer);
}
}
else
{
dwSize = sizeof(pszTemp) / sizeof(WCHAR);
if (GetComputerName(pszTemp, &dwSize))
{
wcscat(pszMachine, pszTemp);
}
else
{
dwErr = ERROR_CAN_NOT_COMPLETE;
break;
}
}
// Find out if we're talking about an nt40 machine
dwErr = ElIsNt40Machine(pszMachine, pbNt40, &hkMachine);
if (dwErr != NO_ERROR)
{
break;
}
// If it is, we're done -- no mapping
if (*pbNt40)
{
*ppMap = NULL;
*lpdwNumNodes = 0;
dwErr = NO_ERROR;
break;
}
// Connect to the connection manager rpc instance
//
if (pszMachine)
{
cr = CM_Connect_MachineW(
pszMachine,
&hkCmMachine);
if (cr != CR_SUCCESS)
{
dwErr = cr;
break;
}
}
// Otherwise, read 'em in...
hDevInfo = SetupDiGetClassDevsExW(
&DevGuid,
NULL,
NULL,
DIGCF_PRESENT | DIGCF_PROFILE,
NULL,
pszMachine,
NULL);
if (hDevInfo == INVALID_HANDLE_VALUE)
{
*ppMap = NULL;
*lpdwNumNodes = 0;
dwErr = GetLastError();
break;
}
// Enumerate the devices
dwTotal = 0;
for (dwIndex = 0; ; dwIndex++)
{
// Get the next device
Device.cbSize = sizeof(SP_DEVINFO_DATA);
if (! SetupDiEnumDeviceInfo(hDevInfo, dwIndex, &Device))
{
break;
}
// Get its registry key
hkDev = SetupDiOpenDevRegKey(
hDevInfo,
&Device,
DICS_FLAG_GLOBAL,
0,
DIREG_DRV,
KEY_READ);
if ((hkDev == NULL) || (hkDev == INVALID_HANDLE_VALUE))
{
continue;
}
if (Map.dwNumNodes <= dwTotal + 1)
{
ElEnlargeMap (&Map, EL_MAP_GROW_FACTOR);
}
// Set up the data to be used by the filters
//
ZeroMemory(&IsNetCfgDevInfo, sizeof(IsNetCfgDevInfo));
ZeroMemory(&AdapterStatusInfo, sizeof(AdapterStatusInfo));
IsNetCfgDevInfo.pAdapter = &(Map.pNodes[dwTotal]);
AdapterStatusInfo.pAdapter = IsNetCfgDevInfo.pAdapter;
AdapterStatusInfo.hkCmMachine = hkCmMachine;
AdapterStatusInfo.pszPnpInstance = (PWCHAR)
IsNetCfgDevInfo.pszPnpInstance;
// Filter out the devices we aren't interested
// in.
if ((ElDevicePassesFilter (hkMachine,
hkDev,
0,
ElIsLanAdapter)) &&
(ElDevicePassesFilter (hkMachine,
hkDev,
(HANDLE)&IsNetCfgDevInfo,
ElIsNetcfgDevice)) &&
(ElDevicePassesFilter (hkMachine,
hkDev,
0,
ElIsNotHiddenDevice)) &&
(ElDevicePassesFilter (hkMachine,
hkDev,
(HANDLE)&AdapterStatusInfo,
ElGetAdapterStatus))
)
{
dwTotal++;
}
RegCloseKey(hkDev);
}
//
// Now read out the ip in ip interfaces
//
if(MprSetupIpInIpInterfaceFriendlyNameEnum(pszMachine,
(BYTE **)&pIpIpTable,
&dwIpIpCount) == NO_ERROR)
{
DWORD i;
//
// Grow the map
//
ElEnlargeMap (&Map, dwIpIpCount);
//
// Copy out the interface info
//
for(i = 0; i < dwIpIpCount; i++)
{
Map.pNodes[dwTotal].pszName = SysAllocString(pIpIpTable[i].wszFriendlyName);
Map.pNodes[dwTotal].guid = pIpIpTable[i].Guid;
Map.pNodes[dwTotal].dwStatus = EL_STATUS_OK;
dwTotal++;
}
}
// Assign the return values
*lpdwNumNodes = dwTotal;
if (dwTotal)
{
*ppMap = Map.pNodes;
}
else
{
ElCleanup(Map.pNodes, 0);
*ppMap = NULL;
}
} while (FALSE);
// Cleanup
{
if (hkMachine)
{
RegCloseKey(hkMachine);
}
if (hDevInfo)
{
SetupDiDestroyDeviceInfoList(hDevInfo);
}
if (hkCmMachine)
{
CM_Disconnect_Machine(hkCmMachine);
}
}
return dwErr;
}
//
// Cleans up the buffer returned from ElEnumLanAdapters
//
DWORD
ElCleanup (
IN EL_ADAPTER_INFO * pMap,
IN DWORD dwCount)
{
DWORD i;
for (i = 0; i < dwCount; i++) {
if (pMap[i].pszName)
SysFreeString(pMap[i].pszName);
}
Free (pMap);
return NO_ERROR;
}