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.

748 lines
18 KiB

  1. /*
  2. File enumlan.c
  3. Implementation of functions to enumerate lan interfaces
  4. on a given machine. This implementation actually bypasses
  5. netman and gets the information using setup api's.
  6. Paul Mayfield, 5/13/98
  7. */
  8. #include <nt.h>
  9. #include <ntrtl.h>
  10. #include <nturtl.h>
  11. #include <windows.h>
  12. #include <winerror.h>
  13. #include <netcfgx.h>
  14. #include <netcon.h>
  15. #include <setupapi.h>
  16. #include <devguid.h>
  17. #include <cfgmgr32.h>
  18. #include <mprapi.h>
  19. #include "rtcfg.h"
  20. #include "enumlan.h"
  21. #define EL_MAP_GROW_FACTOR 25
  22. //
  23. // Determines whether a given machine is nt40
  24. //
  25. DWORD
  26. IsNt40Machine (
  27. IN HKEY hkeyMachine,
  28. OUT PBOOL pbIsNt40);
  29. //
  30. // Structure represents a growable array of name map nodes.
  31. //
  32. typedef struct _EL_NAMEMAP
  33. {
  34. DWORD dwNumNodes;
  35. EL_ADAPTER_INFO *pNodes;
  36. } EL_NAMEMAP;
  37. //
  38. // Structure contains data manipulated by ElIsNetcfgDevice
  39. //
  40. typedef struct _EL_ISNETCFGDEV_INFO
  41. {
  42. EL_ADAPTER_INFO* pAdapter; // IN OUT
  43. WCHAR pszPnpInstance[MAX_PATH]; // OUT
  44. } EL_ISNETCFGDEV_INFO;
  45. //
  46. // Structure contains data manipulated by ElGetAdapterStatus
  47. //
  48. typedef struct _EL_ADAPTER_STATUS_INFO
  49. {
  50. EL_ADAPTER_INFO* pAdapter; // IN OUT
  51. HANDLE hkCmMachine; // IN
  52. PWCHAR pszPnpInstance; // IN
  53. } EL_ADAPTER_STATUS_INFO;
  54. //
  55. // Defines a filter function (used by lan adapter enumeration)
  56. //
  57. typedef
  58. DWORD
  59. (*DevFilterFuncPtr)(
  60. HKEY,
  61. HKEY,
  62. HANDLE,
  63. PBOOL);
  64. //
  65. // Stolen from netcfg project
  66. //
  67. #define IA_INSTALLED 1
  68. const WCHAR c_szRegKeyInterfacesFromInstance[] = L"Ndi\\Interfaces";
  69. const WCHAR c_szRegValueUpperRange[] = L"UpperRange";
  70. const WCHAR c_szBiNdis4[] = L"ndis4";
  71. const WCHAR c_szBiNdis5[] = L"ndis5";
  72. const WCHAR c_szBiNdisAtm[] = L"ndisatm";
  73. const WCHAR c_szBiNdis1394[] = L"ndis1394";
  74. const WCHAR c_szCharacteristics[] = L"Characteristics";
  75. const WCHAR c_szRegValueNetCfgInstanceId[] = L"NetCfgInstanceID";
  76. const WCHAR c_szRegValueInstallerAction[] = L"InstallerAction";
  77. const WCHAR c_szRegKeyConnection[] = L"Connection";
  78. const WCHAR c_szRegValueConName[] = L"Name";
  79. const WCHAR c_szRegValuePnpInstanceId[] = L"PnpInstanceID";
  80. const WCHAR c_szRegKeyComponentClasses[] =
  81. L"SYSTEM\\CurrentControlSet\\Control\\Network";
  82. //
  83. // Maps a CM_PROB_* value to a EL_STATUS_* value
  84. //
  85. DWORD
  86. ElMapCmStatusToElStatus(
  87. IN DWORD dwCmStatus,
  88. OUT LPDWORD lpdwElStatus)
  89. {
  90. return NO_ERROR;
  91. }
  92. //
  93. // Adapted version of HrIsLanCapableAdapterFromHkey determines
  94. // whether an adapter is lan capable based on its registry key.
  95. //
  96. DWORD
  97. ElIsLanAdapter(
  98. IN HKEY hkMachine,
  99. IN HKEY hkey,
  100. OUT HANDLE hData,
  101. OUT PBOOL pbIsLan)
  102. {
  103. HKEY hkeyInterfaces;
  104. WCHAR pszBuf[256], *pszCur, *pszEnd;
  105. DWORD dwErr, dwType = REG_SZ, dwSize = sizeof(pszBuf);
  106. *pbIsLan = FALSE;
  107. // Open the interfaces key
  108. dwErr = RegOpenKeyEx( hkey,
  109. c_szRegKeyInterfacesFromInstance,
  110. 0,
  111. KEY_READ,
  112. &hkeyInterfaces);
  113. if (dwErr != ERROR_SUCCESS)
  114. return dwErr;
  115. // Read in the upper range
  116. dwErr = RegQueryValueExW (hkeyInterfaces,
  117. c_szRegValueUpperRange,
  118. NULL,
  119. &dwType,
  120. (LPBYTE)pszBuf,
  121. &dwSize);
  122. if (dwErr != ERROR_SUCCESS)
  123. return NO_ERROR;
  124. // See if this buffer has the magic strings in it
  125. pszCur = pszBuf;
  126. while (TRUE) {
  127. pszEnd = wcsstr(pszCur, L",");
  128. if (pszEnd != NULL)
  129. *pszEnd = (WCHAR)0;
  130. if ((lstrcmpi (pszCur, c_szBiNdis4) == 0) ||
  131. (lstrcmpi (pszCur, c_szBiNdis5) == 0) ||
  132. (lstrcmpi (pszCur, c_szBiNdis1394) == 0) ||
  133. (lstrcmpi (pszCur, c_szBiNdisAtm) == 0))
  134. {
  135. *pbIsLan = TRUE;
  136. break;
  137. }
  138. if (pszEnd == NULL)
  139. break;
  140. else
  141. pszCur = pszEnd + 1;
  142. }
  143. RegCloseKey(hkeyInterfaces);
  144. return NO_ERROR;
  145. }
  146. //
  147. // Filters netcfg devices. If a device passes this filter
  148. // it will have its guid and freindly name returned through
  149. // the hData parameter (option user defined data).
  150. //
  151. DWORD
  152. ElIsNetcfgDevice(
  153. IN HKEY hkMachine,
  154. IN HKEY hkDev,
  155. OUT HANDLE hData,
  156. OUT PBOOL pbOk)
  157. {
  158. EL_ISNETCFGDEV_INFO* pInfo = (EL_ISNETCFGDEV_INFO*)hData;
  159. EL_ADAPTER_INFO *pNode = pInfo->pAdapter;
  160. GUID Guid = GUID_DEVCLASS_NET;
  161. WCHAR pszBuf[1024], pszPath[256], pszClassGuid[256];
  162. DWORD dwErr = NO_ERROR, dwType = REG_SZ, dwSize = sizeof(pszBuf), dwAction;
  163. HKEY hkeyNetCfg = NULL;
  164. *pbOk = FALSE;
  165. // Read in the netcfg instance
  166. dwErr = RegQueryValueExW (
  167. hkDev,
  168. c_szRegValueNetCfgInstanceId,
  169. NULL,
  170. &dwType,
  171. (LPBYTE)pszBuf,
  172. &dwSize);
  173. if (dwErr != NO_ERROR)
  174. {
  175. return dwErr;
  176. }
  177. // Generate path in registry for lookup
  178. StringFromGUID2(
  179. &Guid,
  180. pszClassGuid,
  181. sizeof(pszClassGuid));
  182. wsprintf(
  183. pszPath,
  184. L"%s\\%s\\%s\\%s",
  185. c_szRegKeyComponentClasses,
  186. pszClassGuid,
  187. pszBuf,
  188. c_szRegKeyConnection);
  189. do
  190. {
  191. // Open the key
  192. dwErr = RegOpenKeyEx(
  193. hkMachine,
  194. pszPath,
  195. 0,
  196. KEY_READ,
  197. &hkeyNetCfg);
  198. if (dwErr != ERROR_SUCCESS)
  199. {
  200. break;
  201. }
  202. // Pass the filter
  203. *pbOk = TRUE;
  204. // Store the guid
  205. pszBuf[wcslen(pszBuf) - 1] = (WCHAR)0;
  206. if (UuidFromString(pszBuf + 1, &(pNode->guid)) != RPC_S_OK)
  207. return ERROR_NOT_ENOUGH_MEMORY;
  208. // Read in the adapter name
  209. //
  210. dwType = REG_SZ;
  211. dwSize = sizeof(pszBuf);
  212. dwErr = RegQueryValueEx(
  213. hkeyNetCfg,
  214. c_szRegValueConName,
  215. NULL,
  216. &dwType,
  217. (LPBYTE)pszBuf,
  218. &dwSize);
  219. if (dwErr == ERROR_SUCCESS)
  220. {
  221. pNode->pszName = SysAllocString(pszBuf);
  222. if (pNode->pszName == NULL)
  223. {
  224. dwErr = ERROR_NOT_ENOUGH_MEMORY;
  225. break;
  226. }
  227. }
  228. // Read in the adapter pnp instance id
  229. //
  230. dwType = REG_SZ;
  231. dwSize = sizeof(pInfo->pszPnpInstance);
  232. dwErr = RegQueryValueEx(
  233. hkeyNetCfg,
  234. c_szRegValuePnpInstanceId,
  235. NULL,
  236. &dwType,
  237. (LPBYTE)(pInfo->pszPnpInstance),
  238. &dwSize);
  239. if (dwErr != ERROR_SUCCESS)
  240. {
  241. break;
  242. }
  243. } while (FALSE);
  244. // Cleanup
  245. {
  246. if (hkeyNetCfg)
  247. {
  248. RegCloseKey(hkeyNetCfg);
  249. }
  250. }
  251. return dwErr;
  252. }
  253. //
  254. // Filters hidden devices
  255. //
  256. DWORD
  257. ElIsNotHiddenDevice (
  258. IN HKEY hkMachine,
  259. IN HKEY hkDev,
  260. OUT HANDLE hData,
  261. OUT PBOOL pbOk)
  262. {
  263. DWORD dwErr, dwType = REG_DWORD,
  264. dwSize = sizeof(DWORD), dwChars;
  265. dwErr = RegQueryValueEx ( hkDev,
  266. c_szCharacteristics,
  267. NULL,
  268. &dwType,
  269. (LPBYTE)&dwChars,
  270. &dwSize);
  271. if (dwErr != ERROR_SUCCESS)
  272. return dwErr;
  273. *pbOk = !(dwChars & NCF_HIDDEN);
  274. return NO_ERROR;
  275. }
  276. //
  277. // Filter that simply loads the adapter status
  278. //
  279. DWORD
  280. ElGetAdapterStatus(
  281. IN HKEY hkMachine,
  282. IN HKEY hkDev,
  283. OUT HANDLE hData,
  284. OUT PBOOL pbOk)
  285. {
  286. EL_ADAPTER_STATUS_INFO* pInfo = (EL_ADAPTER_STATUS_INFO*)hData;
  287. DEVINST DevInst;
  288. CONFIGRET cr = CR_SUCCESS;
  289. ULONG ulStatus = 0, ulProblem = 0;
  290. // Validate parameters
  291. //
  292. if (pInfo == NULL)
  293. {
  294. return ERROR_INVALID_PARAMETER;
  295. }
  296. // Find the device
  297. //
  298. cr = CM_Locate_DevNode_ExW(
  299. &DevInst,
  300. pInfo->pszPnpInstance,
  301. CM_LOCATE_DEVNODE_NORMAL,
  302. pInfo->hkCmMachine);
  303. if (cr != CR_SUCCESS)
  304. {
  305. return ERROR_CAN_NOT_COMPLETE;
  306. }
  307. // Get the device status
  308. //
  309. cr = CM_Get_DevNode_Status_Ex(
  310. &ulStatus,
  311. &ulProblem,
  312. DevInst,
  313. 0,
  314. pInfo->hkCmMachine);
  315. if (cr != CR_SUCCESS)
  316. {
  317. return ERROR_CAN_NOT_COMPLETE;
  318. }
  319. // Map CM's status to our own
  320. //
  321. switch (ulProblem)
  322. {
  323. // No problem, we're connected
  324. case 0:
  325. pInfo->pAdapter->dwStatus = EL_STATUS_OK;
  326. break;
  327. // Device not present
  328. case CM_PROB_DEVICE_NOT_THERE:
  329. case CM_PROB_MOVED:
  330. pInfo->pAdapter->dwStatus = EL_STATUS_NOT_THERE;
  331. break;
  332. // Device was disabled via Device Manager
  333. case CM_PROB_HARDWARE_DISABLED:
  334. pInfo->pAdapter->dwStatus = EL_STATUS_HWDISABLED;
  335. break;
  336. // Device was disconnected
  337. case CM_PROB_DISABLED:
  338. pInfo->pAdapter->dwStatus = EL_STATUS_DISABLED;
  339. break;
  340. // All other problems
  341. default:
  342. pInfo->pAdapter->dwStatus = EL_STATUS_OTHER;
  343. break;
  344. }
  345. // Make sure this device passes the filter
  346. //
  347. *pbOk = TRUE;
  348. return NO_ERROR;
  349. }
  350. //
  351. // Returns TRUE if the given device passes the filter.
  352. // Returns FALSE otherwise.
  353. //
  354. BOOL
  355. ElDevicePassesFilter (
  356. IN HKEY hkMachine,
  357. IN HKEY hkDev,
  358. IN HANDLE hData,
  359. IN DevFilterFuncPtr pFilter)
  360. {
  361. BOOL bOk = TRUE;
  362. DWORD dwErr;
  363. dwErr = (*pFilter)(hkMachine, hkDev, hData, &bOk);
  364. if ((dwErr == NO_ERROR) && (bOk == TRUE))
  365. return TRUE;
  366. return FALSE;
  367. }
  368. //
  369. // Allocates additional space in a EL_NAMEMAP
  370. //
  371. DWORD
  372. ElEnlargeMap (
  373. IN OUT EL_NAMEMAP * pMap,
  374. DWORD dwAmount)
  375. {
  376. EL_ADAPTER_INFO * pNewNodes;
  377. DWORD dwNewSize, i;
  378. // Figure out the new size
  379. dwNewSize = pMap->dwNumNodes + dwAmount;
  380. // Resize the array
  381. pNewNodes = (EL_ADAPTER_INFO *) Malloc (dwNewSize * sizeof(EL_ADAPTER_INFO));
  382. if (!pNewNodes)
  383. return ERROR_NOT_ENOUGH_MEMORY;
  384. ZeroMemory(pNewNodes, dwNewSize * sizeof(EL_ADAPTER_INFO));
  385. // Initialize the arrays.
  386. CopyMemory(pNewNodes, pMap->pNodes, pMap->dwNumNodes * sizeof(EL_ADAPTER_INFO));
  387. // Free old data if needed
  388. if (pMap->dwNumNodes)
  389. Free (pMap->pNodes);
  390. // Assign the new arrays
  391. pMap->pNodes = pNewNodes;
  392. pMap->dwNumNodes = dwNewSize;
  393. return NO_ERROR;
  394. }
  395. //
  396. // Find out if given server is NT 4
  397. //
  398. DWORD
  399. ElIsNt40Machine (
  400. IN PWCHAR pszMachine,
  401. OUT PBOOL pbNt40,
  402. OUT HKEY* phkMachine)
  403. {
  404. DWORD dwErr;
  405. dwErr = RegConnectRegistry (
  406. pszMachine,
  407. HKEY_LOCAL_MACHINE,
  408. phkMachine);
  409. if (dwErr != ERROR_SUCCESS)
  410. return dwErr;
  411. return IsNt40Machine (*phkMachine, pbNt40);
  412. }
  413. //
  414. // Obtains the map of connection names to guids on the given server
  415. // from its netman service.
  416. //
  417. // Parameters:
  418. // pszServer: Server on which to obtain map (NULL = local)
  419. // ppMap: Returns array of EL_ADAPTER_INFO's
  420. // lpdwCount Returns number of elements read into ppMap
  421. // pbNt40: Returns whether server is nt4 installation
  422. //
  423. DWORD
  424. ElEnumLanAdapters (
  425. IN PWCHAR pszServer,
  426. OUT EL_ADAPTER_INFO ** ppMap,
  427. OUT LPDWORD lpdwNumNodes,
  428. OUT PBOOL pbNt40 )
  429. {
  430. GUID DevGuid = GUID_DEVCLASS_NET;
  431. SP_DEVINFO_DATA Device;
  432. HDEVINFO hDevInfo = NULL;
  433. HKEY hkDev = NULL, hkMachine = NULL;
  434. DWORD dwErr = NO_ERROR, dwIndex, dwTotal, dwSize;
  435. EL_NAMEMAP Map;
  436. WCHAR pszMachine[512], pszTemp[512];
  437. HANDLE hkCmMachine = NULL;
  438. EL_ADAPTER_STATUS_INFO AdapterStatusInfo;
  439. EL_ISNETCFGDEV_INFO IsNetCfgDevInfo;
  440. CONFIGRET cr = CR_SUCCESS;
  441. PMPR_IPINIP_INTERFACE_0 pIpIpTable;
  442. DWORD dwIpIpCount;
  443. // Validate parameters
  444. if (!ppMap || !lpdwNumNodes || !pbNt40)
  445. {
  446. return ERROR_INVALID_PARAMETER;
  447. }
  448. *pbNt40 = FALSE;
  449. // Initialize
  450. //
  451. ZeroMemory(&Map, sizeof(EL_NAMEMAP));
  452. do
  453. {
  454. // Prepare the name of the computer
  455. wcscpy(pszMachine, L"\\\\");
  456. if (pszServer)
  457. {
  458. if (*pszServer == L'\\')
  459. {
  460. wcscpy(pszMachine, pszServer);
  461. }
  462. else
  463. {
  464. wcscat(pszMachine, pszServer);
  465. }
  466. }
  467. else
  468. {
  469. dwSize = sizeof(pszTemp) / sizeof(WCHAR);
  470. if (GetComputerName(pszTemp, &dwSize))
  471. {
  472. wcscat(pszMachine, pszTemp);
  473. }
  474. else
  475. {
  476. dwErr = ERROR_CAN_NOT_COMPLETE;
  477. break;
  478. }
  479. }
  480. // Find out if we're talking about an nt40 machine
  481. dwErr = ElIsNt40Machine(pszMachine, pbNt40, &hkMachine);
  482. if (dwErr != NO_ERROR)
  483. {
  484. break;
  485. }
  486. // If it is, we're done -- no mapping
  487. if (*pbNt40)
  488. {
  489. *ppMap = NULL;
  490. *lpdwNumNodes = 0;
  491. dwErr = NO_ERROR;
  492. break;
  493. }
  494. // Connect to the connection manager rpc instance
  495. //
  496. if (pszMachine)
  497. {
  498. cr = CM_Connect_MachineW(
  499. pszMachine,
  500. &hkCmMachine);
  501. if (cr != CR_SUCCESS)
  502. {
  503. dwErr = cr;
  504. break;
  505. }
  506. }
  507. // Otherwise, read 'em in...
  508. hDevInfo = SetupDiGetClassDevsExW(
  509. &DevGuid,
  510. NULL,
  511. NULL,
  512. DIGCF_PRESENT | DIGCF_PROFILE,
  513. NULL,
  514. pszMachine,
  515. NULL);
  516. if (hDevInfo == INVALID_HANDLE_VALUE)
  517. {
  518. *ppMap = NULL;
  519. *lpdwNumNodes = 0;
  520. dwErr = GetLastError();
  521. break;
  522. }
  523. // Enumerate the devices
  524. dwTotal = 0;
  525. for (dwIndex = 0; ; dwIndex++)
  526. {
  527. // Get the next device
  528. Device.cbSize = sizeof(SP_DEVINFO_DATA);
  529. if (! SetupDiEnumDeviceInfo(hDevInfo, dwIndex, &Device))
  530. {
  531. break;
  532. }
  533. // Get its registry key
  534. hkDev = SetupDiOpenDevRegKey(
  535. hDevInfo,
  536. &Device,
  537. DICS_FLAG_GLOBAL,
  538. 0,
  539. DIREG_DRV,
  540. KEY_READ);
  541. if ((hkDev == NULL) || (hkDev == INVALID_HANDLE_VALUE))
  542. {
  543. continue;
  544. }
  545. if (Map.dwNumNodes <= dwTotal + 1)
  546. {
  547. ElEnlargeMap (&Map, EL_MAP_GROW_FACTOR);
  548. }
  549. // Set up the data to be used by the filters
  550. //
  551. ZeroMemory(&IsNetCfgDevInfo, sizeof(IsNetCfgDevInfo));
  552. ZeroMemory(&AdapterStatusInfo, sizeof(AdapterStatusInfo));
  553. IsNetCfgDevInfo.pAdapter = &(Map.pNodes[dwTotal]);
  554. AdapterStatusInfo.pAdapter = IsNetCfgDevInfo.pAdapter;
  555. AdapterStatusInfo.hkCmMachine = hkCmMachine;
  556. AdapterStatusInfo.pszPnpInstance = (PWCHAR)
  557. IsNetCfgDevInfo.pszPnpInstance;
  558. // Filter out the devices we aren't interested
  559. // in.
  560. if ((ElDevicePassesFilter (hkMachine,
  561. hkDev,
  562. 0,
  563. ElIsLanAdapter)) &&
  564. (ElDevicePassesFilter (hkMachine,
  565. hkDev,
  566. (HANDLE)&IsNetCfgDevInfo,
  567. ElIsNetcfgDevice)) &&
  568. (ElDevicePassesFilter (hkMachine,
  569. hkDev,
  570. 0,
  571. ElIsNotHiddenDevice)) &&
  572. (ElDevicePassesFilter (hkMachine,
  573. hkDev,
  574. (HANDLE)&AdapterStatusInfo,
  575. ElGetAdapterStatus))
  576. )
  577. {
  578. dwTotal++;
  579. }
  580. RegCloseKey(hkDev);
  581. }
  582. //
  583. // Now read out the ip in ip interfaces
  584. //
  585. if(MprSetupIpInIpInterfaceFriendlyNameEnum(pszMachine,
  586. (BYTE **)&pIpIpTable,
  587. &dwIpIpCount) == NO_ERROR)
  588. {
  589. DWORD i;
  590. //
  591. // Grow the map
  592. //
  593. ElEnlargeMap (&Map, dwIpIpCount);
  594. //
  595. // Copy out the interface info
  596. //
  597. for(i = 0; i < dwIpIpCount; i++)
  598. {
  599. Map.pNodes[dwTotal].pszName = SysAllocString(pIpIpTable[i].wszFriendlyName);
  600. Map.pNodes[dwTotal].guid = pIpIpTable[i].Guid;
  601. Map.pNodes[dwTotal].dwStatus = EL_STATUS_OK;
  602. dwTotal++;
  603. }
  604. }
  605. // Assign the return values
  606. *lpdwNumNodes = dwTotal;
  607. if (dwTotal)
  608. {
  609. *ppMap = Map.pNodes;
  610. }
  611. else
  612. {
  613. ElCleanup(Map.pNodes, 0);
  614. *ppMap = NULL;
  615. }
  616. } while (FALSE);
  617. // Cleanup
  618. {
  619. if (hkMachine)
  620. {
  621. RegCloseKey(hkMachine);
  622. }
  623. if (hDevInfo)
  624. {
  625. SetupDiDestroyDeviceInfoList(hDevInfo);
  626. }
  627. if (hkCmMachine)
  628. {
  629. CM_Disconnect_Machine(hkCmMachine);
  630. }
  631. }
  632. return dwErr;
  633. }
  634. //
  635. // Cleans up the buffer returned from ElEnumLanAdapters
  636. //
  637. DWORD
  638. ElCleanup (
  639. IN EL_ADAPTER_INFO * pMap,
  640. IN DWORD dwCount)
  641. {
  642. DWORD i;
  643. for (i = 0; i < dwCount; i++) {
  644. if (pMap[i].pszName)
  645. SysFreeString(pMap[i].pszName);
  646. }
  647. Free (pMap);
  648. return NO_ERROR;
  649. }