Leaked source code of windows server 2003
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.

698 lines
18 KiB

  1. //***********************************************************
  2. // Copyright (C) Microsoft Corporation, 1996 - 1998
  3. //
  4. // metasnap.cpp
  5. //
  6. // Description: Metabase Snapshot utility tool main
  7. //
  8. // History: 15-July-98 Tamas Nemeth (t-tamasn) Created.
  9. //
  10. //***********************************************************
  11. #define INITGUID
  12. #define E_UNKNOWN_ARG 0x10000
  13. #define E_WRONG_NUMBER_ARGS 0x20000
  14. #define E_NULL_PTR 0x30000
  15. #define DEFAULT_MD_TIMEOUT 0x1000
  16. #define DEFAULT_GETALL_BUFFER_SIZE 4096
  17. //#define DBG_ASSERT(exp)
  18. //# define DBG_ASSERT(exp) ((void)0) /* Do Nothing */
  19. //#include "stdafx.h"
  20. //#include "winsock.h"
  21. //#undef dllexp
  22. //#include "tcpdllp.hxx"
  23. //#define _RDNS_STANDALONE
  24. //#include "afx.h"
  25. #include <objbase.h>
  26. #include <coguid.h>
  27. #include <stdio.h>
  28. #include <stdlib.h>
  29. #include <mbstring.h>
  30. #include "iadmw.h"
  31. #include "iiscnfg.h"
  32. #include "uiutils.h"
  33. //#include <pudebug.h>
  34. //extern "C" DEBUG_PRINTS * g_pDebug = NULL;
  35. //#undef dllexp
  36. //#include "tcpdllp.hxx"
  37. //#define _RDNS_STANDALONE
  38. enum ERROR_PARAMETER
  39. {
  40. MACHINE_NAME,
  41. START_KEY_NAME,
  42. };
  43. struct _CMD_PARAMS
  44. {
  45. LPWSTR szMachineName;
  46. LPWSTR szStartKey;
  47. BOOL bShowSecure; //
  48. DWORD dwErrParameter; // to determine which parameter is incorrect
  49. };
  50. typedef struct _CMD_PARAMS CMD_PARAMS;
  51. //typedef CString* pCString;
  52. // Global variables:
  53. //DWORD* dwSortArray;
  54. PBYTE pbGetAllBuffer;
  55. // Function prototypes:
  56. HRESULT PrintKeyRecursively(IMSAdminBase *pcAdmCom, WCHAR *lpwstrFullPath, CMD_PARAMS* pcpCommandStructure);
  57. HRESULT PrintAllPropertiesAtKey (IMSAdminBase *pcAdmCom, METADATA_HANDLE hmdHandle,
  58. CMD_PARAMS* pcpCommandStructure);
  59. VOID PrintProperty(METADATA_GETALL_RECORD & mdr, BOOL bShowSecure);
  60. VOID PrintDataTypeAndValue(METADATA_GETALL_RECORD *pmdgr, BOOL bShowSecure);
  61. DWORD ParseCommands (int argc, char *argv[], CMD_PARAMS *pcpCommandStructure);
  62. VOID DisplayHelp();
  63. // new stuff
  64. DWORD
  65. AddAccessEntries(
  66. IN ADDRESS_CHECK & ac,
  67. IN BOOL fName,
  68. IN BOOL fGrant,
  69. //OUT CObListPlus & oblAccessList,
  70. OUT DWORD & cEntries
  71. )
  72. /*++
  73. Routine Description:
  74. Add specific kind of addresses from the list to the oblist of
  75. access entries
  76. Arguments:
  77. ADDRESS_CHECK & ac : Address list input object
  78. BOOL fName : TRUE for names, FALSE for ip
  79. BOOL fGrant : TRUE for granted, FALSE for denied
  80. CObListPlus & oblAccessList : ObList to add access entries to
  81. int & cEntries : Returns the number of entries
  82. Return Value:
  83. Error code
  84. Notes:
  85. Sentinel entries (ip 0.0.0.0) are not added to the oblist, but
  86. are reflected in the cEntries return value
  87. --*/
  88. {
  89. DWORD i;
  90. DWORD dwFlags;
  91. if (fName)
  92. {
  93. //
  94. // Domain names
  95. //
  96. LPSTR lpName;
  97. cEntries = ac.GetNbName(fGrant);
  98. //printf("Number of names: %ld.\n",cEntries);
  99. for (i = 0L; i < cEntries; ++i)
  100. {
  101. if (ac.GetName(fGrant, i, &lpName, &dwFlags))
  102. {
  103. if (fGrant)
  104. printf("\tGranted to %s.\n",lpName);
  105. else
  106. printf("\tDenied to %s.\n",lpName);
  107. /*CString strDomain(lpName);
  108. if (!(dwFlags & DNSLIST_FLAG_NOSUBDOMAIN))
  109. {
  110. strDomain = _T("*.") + strDomain;
  111. }*/
  112. //oblAccessList.AddTail(new CIPAccessDescriptor(fGrant, strDomain));
  113. }
  114. }
  115. }
  116. else
  117. {
  118. //
  119. // IP Addresses
  120. //
  121. LPBYTE lpMask;
  122. LPBYTE lpAddr;
  123. cEntries = ac.GetNbAddr(fGrant);
  124. //printf("Number of addresses: %ld.\n",cEntries);
  125. for (i = 0L; i < cEntries; ++i)
  126. {
  127. if (ac.GetAddr(fGrant, i, &dwFlags, &lpMask, &lpAddr))
  128. {
  129. if (lpAddr[0] != 0 || lpAddr[1] != 0 || lpAddr[2] !=0 || lpAddr[3] !=0)
  130. {
  131. if (lpAddr[0] != 0 || lpAddr[1] != 0 || lpAddr[2] !=0 || lpAddr[3] !=0)
  132. if (fGrant)
  133. printf("\tGranted to %d",lpAddr[0]);
  134. else
  135. printf("\tDenied to %d",lpAddr[0]);
  136. for (int j = 1; j<4; j++)
  137. printf(".%d",lpAddr[j]);
  138. if (lpMask[0] != 255 || lpMask[1] != 255 || lpMask[2] !=255 || lpMask[3] !=255)
  139. {
  140. printf(" (Mask: %d",lpMask[0]);
  141. for (int j = 1; j<4; j++)
  142. printf(".%d",lpMask[j]);
  143. printf(")");
  144. }
  145. printf(".\n");
  146. }
  147. else
  148. printf("\tDenied to everyone.\n");
  149. }
  150. }
  151. }
  152. return ERROR_SUCCESS;
  153. }
  154. DWORD
  155. BuildIplOblistFromBlob(
  156. IN METADATA_GETALL_RECORD & mdgr
  157. //OUT CObListPlus & oblAccessList,
  158. // OUT BOOL & fGrantByDefault
  159. )
  160. {
  161. //oblAccessList.RemoveAll();
  162. if (mdgr.dwMDDataLen == 0)
  163. {
  164. return ERROR_SUCCESS;
  165. }
  166. ADDRESS_CHECK ac;
  167. ac.BindCheckList(mdgr.pbMDData, mdgr.dwMDDataLen);
  168. DWORD cGrantAddr, cGrantName, cDenyAddr, cDenyName;
  169. // Name/IP Granted/Deny
  170. // ============================================================
  171. AddAccessEntries(ac, TRUE, TRUE, cGrantName);
  172. AddAccessEntries(ac, FALSE, TRUE, cGrantAddr);
  173. AddAccessEntries(ac, TRUE, FALSE, cDenyName);
  174. AddAccessEntries(ac, FALSE, FALSE, cDenyAddr);
  175. ac.UnbindCheckList();
  176. // fGrantByDefault = (cDenyAddr + cDenyName != 0L)
  177. // || (cGrantAddr + cGrantName == 0L);
  178. return ERROR_SUCCESS;
  179. }
  180. // end new
  181. VOID __cdecl main (int argc, char *argv[])
  182. {
  183. if (argc == 1)
  184. {
  185. DisplayHelp();
  186. return;
  187. }
  188. CMD_PARAMS pcpCommands;
  189. DWORD dwRetVal = ParseCommands (argc, argv, &pcpCommands);
  190. if (dwRetVal != ERROR_SUCCESS)
  191. {
  192. if (dwRetVal == E_OUTOFMEMORY)
  193. fprintf (stderr, "ERROR: Out of memory.");
  194. else if (dwRetVal == E_WRONG_NUMBER_ARGS)
  195. fprintf (stderr, "ERROR: Invalid number of arguments.");
  196. else if (dwRetVal == E_INVALIDARG)
  197. {
  198. fprintf (stderr, "ERROR: Invalid input value");
  199. switch (pcpCommands.dwErrParameter)
  200. {
  201. case (MACHINE_NAME):
  202. fputs (" for MachineName.", stderr);
  203. break;
  204. case (START_KEY_NAME):
  205. fputs (" for StartKey.", stderr);
  206. break;
  207. default:
  208. fputs (".", stderr);
  209. break;
  210. }
  211. }
  212. else
  213. fprintf (stderr, "ERROR: Unknown error in processing arguments.");
  214. fputs(" Enter \"metasnap\" without arguments to display help.\n", stderr);
  215. return;
  216. }
  217. IMSAdminBase *pcAdmCom = NULL; //interface pointer
  218. IClassFactory * pcsfFactory = NULL;
  219. COSERVERINFO csiMachineName;
  220. COSERVERINFO *pcsiParam = NULL;
  221. // Fill the structure for CoGetClassObject:
  222. csiMachineName.pAuthInfo = NULL;
  223. csiMachineName.dwReserved1 = 0;
  224. csiMachineName.dwReserved2 = 0;
  225. pcsiParam = &csiMachineName;
  226. csiMachineName.pwszName = pcpCommands.szMachineName;
  227. // Initialize COM:
  228. HRESULT hresError = CoInitializeEx(NULL, COINIT_MULTITHREADED);
  229. if (FAILED(hresError))
  230. {
  231. fprintf (stderr, "ERROR: COM Initialization failed. Error: %d (%#x)\n", hresError, hresError);
  232. return;
  233. }
  234. hresError = CoGetClassObject(GETAdminBaseCLSID(TRUE), CLSCTX_SERVER, pcsiParam,
  235. IID_IClassFactory, (void**) &pcsfFactory);
  236. if (FAILED(hresError))
  237. {
  238. switch (hresError)
  239. {
  240. case HRESULT_FROM_WIN32(REGDB_E_CLASSNOTREG):
  241. fprintf(stderr, "ERROR: IIS Metabase does not exist.\n");
  242. break;
  243. case HRESULT_FROM_WIN32(E_ACCESSDENIED):
  244. fprintf(stderr, "ERROR: Access to Metabase denied.\n");
  245. break;
  246. case HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE):
  247. fprintf(stderr, "ERROR: The specified host is unavailable.\n");
  248. break;
  249. default:
  250. fprintf (stderr, "ERROR: Couldn't get Metabase Object. Error: %d (%#x)\n", hresError, hresError);
  251. break;
  252. }
  253. return;
  254. }
  255. hresError = pcsfFactory->CreateInstance(NULL, IID_IMSAdminBase, (void **) &pcAdmCom);
  256. if (FAILED(hresError))
  257. {
  258. switch (hresError)
  259. {
  260. case HRESULT_FROM_WIN32(RPC_S_SEC_PKG_ERROR):
  261. fprintf (stderr, "ERROR: A security-related error occurred.\n");
  262. break;
  263. case HRESULT_FROM_WIN32(E_OUTOFMEMORY):
  264. fprintf (stderr, "ERROR: There is not enough memory available.\n");
  265. break;
  266. default:
  267. fprintf (stderr, "ERROR: Couldn't create Metabase Instance. Error: %d (%#x)\n", hresError, hresError);
  268. break;
  269. }
  270. pcsfFactory->Release();
  271. return;
  272. }
  273. pcsfFactory->Release();
  274. METADATA_HANDLE hmdHandle;
  275. hresError = pcAdmCom->OpenKey (
  276. METADATA_MASTER_ROOT_HANDLE,
  277. pcpCommands.szStartKey,
  278. METADATA_PERMISSION_READ,
  279. DEFAULT_MD_TIMEOUT,
  280. &hmdHandle);
  281. if (FAILED(hresError))
  282. {
  283. switch (hresError)
  284. {
  285. case HRESULT_FROM_WIN32(ERROR_PATH_BUSY):
  286. fprintf (stderr, "ERROR: The specified key is already in use.\n");
  287. break;
  288. case HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND):
  289. fprintf (stderr, "ERROR: The specified key is not found.\n");
  290. break;
  291. default:
  292. fprintf (stderr, "ERROR: Couldn't open Metabase Key. Error: %d (%#x)\n", hresError, hresError);
  293. break;
  294. }
  295. pcAdmCom->Release();
  296. return;
  297. }
  298. // Recurse and dump children
  299. printf("\nIP address and domain name access restrictions:\n");
  300. hresError = PrintKeyRecursively(pcAdmCom, pcpCommands.szStartKey, &pcpCommands);
  301. if (hresError != ERROR_SUCCESS)
  302. {
  303. switch (hresError)
  304. {
  305. case HRESULT_FROM_WIN32(ERROR_PATH_BUSY):
  306. fprintf (stderr, "ERROR: Could not open a key because it is already in use.\n");
  307. break;
  308. case HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY):
  309. fprintf (stderr, "ERROR: There is not enough memory available.\n");
  310. break;
  311. default:
  312. fprintf (stderr, "ERROR: Failed dumping metabase. Error: %u (%#x)\n", hresError, hresError);
  313. break;
  314. }
  315. }
  316. pcAdmCom->CloseKey (hmdHandle);
  317. pcAdmCom->Release();
  318. if (hresError == ERROR_SUCCESS)
  319. fputs("Successfully printed IP Security information.\n", stderr);
  320. }
  321. DWORD ParseCommands (int argc, char *argv [], CMD_PARAMS *pcpCommandStructure)
  322. {
  323. if (pcpCommandStructure == NULL)
  324. return E_NULL_PTR;
  325. if ( argc > 3 )
  326. return E_WRONG_NUMBER_ARGS;
  327. // Set default values:
  328. pcpCommandStructure->szMachineName = (LPWSTR) HeapAlloc (GetProcessHeap(),
  329. HEAP_ZERO_MEMORY, (9 + 1) * sizeof (WCHAR) );
  330. if (pcpCommandStructure->szMachineName == NULL)
  331. return E_OUTOFMEMORY;
  332. wcscpy(pcpCommandStructure->szMachineName,L"localhost");
  333. pcpCommandStructure->bShowSecure = FALSE;
  334. // Handle StartKey:
  335. DWORD dwStartKeyLen = _mbstrlen(argv[1]) + 3;
  336. pcpCommandStructure->szStartKey = (LPWSTR) HeapAlloc (GetProcessHeap(),
  337. HEAP_ZERO_MEMORY, (dwStartKeyLen + 1) * sizeof (WCHAR));
  338. LPWSTR lpwstrTemp = (LPWSTR) HeapAlloc (GetProcessHeap(),
  339. HEAP_ZERO_MEMORY, (dwStartKeyLen + 1) * sizeof (WCHAR));
  340. // _mbscpy(lpwstrTemp,"/LM");
  341. // wcscat(lpwstrTemp, argv[1]);
  342. // printf("%S\n",lpwstrTemp);
  343. // wcscpy(pcpCommands.szStartKey, lpwstrTemp);
  344. if (pcpCommandStructure->szStartKey == NULL)
  345. return E_OUTOFMEMORY;
  346. DWORD dwResult = MultiByteToWideChar(
  347. CP_ACP,
  348. 0,
  349. argv[1],
  350. dwStartKeyLen + 1,
  351. pcpCommandStructure->szStartKey,
  352. dwStartKeyLen + 1);
  353. if (dwResult == 0)
  354. {
  355. pcpCommandStructure->dwErrParameter = START_KEY_NAME;
  356. return E_INVALIDARG;
  357. }
  358. // Add /lm to StartKey:
  359. wcscpy(lpwstrTemp,L"/LM");
  360. wcscat(lpwstrTemp, pcpCommandStructure->szStartKey);
  361. wcscpy(pcpCommandStructure->szStartKey, lpwstrTemp);
  362. // Chop off trailing slashes:
  363. LPWSTR lpwchTemp = pcpCommandStructure->szStartKey;
  364. for (DWORD i=0; i < dwStartKeyLen-1; i++)
  365. lpwchTemp++;
  366. if (!wcscmp(lpwchTemp, TEXT("/") ) || !wcscmp(lpwchTemp, TEXT("\\")) )
  367. *(lpwchTemp) = (WCHAR)'\0';
  368. // Look for MachineName:
  369. if ( argc > 2 && strcmp("-s",argv[2]))
  370. {
  371. DWORD dwMachineNameLen = _mbstrlen(argv[2]);
  372. pcpCommandStructure->szMachineName = (LPWSTR) HeapAlloc (GetProcessHeap(),
  373. HEAP_ZERO_MEMORY, (dwMachineNameLen + 1) * sizeof (WCHAR) );
  374. if (pcpCommandStructure->szMachineName == NULL)
  375. return E_OUTOFMEMORY;
  376. dwResult = MultiByteToWideChar(
  377. CP_ACP,
  378. 0,
  379. argv[2],
  380. dwMachineNameLen + 1,
  381. pcpCommandStructure->szMachineName,
  382. dwMachineNameLen + 1);
  383. if (dwResult == 0)
  384. {
  385. pcpCommandStructure->dwErrParameter = MACHINE_NAME;
  386. return E_INVALIDARG;
  387. }
  388. // Check for "-s" flag:
  389. if (argc == 4)
  390. {
  391. if ( !strcmp("-s",argv[3]) )
  392. pcpCommandStructure->bShowSecure = TRUE;
  393. else
  394. return E_INVALIDARG;
  395. }
  396. }
  397. else if (argc == 3 && !strcmp("-s",argv[2]))
  398. pcpCommandStructure->bShowSecure = TRUE;
  399. else if (argc > 2)
  400. return E_INVALIDARG;
  401. return ERROR_SUCCESS;
  402. }
  403. HRESULT PrintKeyRecursively(IMSAdminBase *pcAdmCom, WCHAR *lpwstrFullPath, CMD_PARAMS* pcpCommandStructure)
  404. {
  405. METADATA_HANDLE hmdHandle;
  406. HRESULT hresError = pcAdmCom->OpenKey(
  407. METADATA_MASTER_ROOT_HANDLE,
  408. lpwstrFullPath,
  409. METADATA_PERMISSION_READ,
  410. DEFAULT_MD_TIMEOUT,
  411. &hmdHandle);
  412. if (hresError != ERROR_SUCCESS)
  413. return hresError;
  414. // Get all data into a buffer:
  415. DWORD dwNumDataEntries ;
  416. DWORD dwDataSetNumber;
  417. DWORD dwRequestBufferSize = DEFAULT_GETALL_BUFFER_SIZE;
  418. DWORD dwRequiredDataLen;
  419. // Allocate a default buffer size
  420. pbGetAllBuffer = (PBYTE)HeapAlloc
  421. (GetProcessHeap(),
  422. HEAP_ZERO_MEMORY,
  423. DEFAULT_GETALL_BUFFER_SIZE);
  424. if (pbGetAllBuffer == NULL)
  425. return HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
  426. hresError = pcAdmCom -> GetAllData (
  427. hmdHandle,
  428. TEXT ("/"),
  429. 0,
  430. 0,
  431. 0,
  432. &dwNumDataEntries,
  433. &dwDataSetNumber,
  434. dwRequestBufferSize,
  435. pbGetAllBuffer,
  436. &dwRequiredDataLen);
  437. if (hresError == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER))
  438. {
  439. // retry the GetAllData with the new buffer size
  440. dwRequestBufferSize = dwRequiredDataLen;
  441. pbGetAllBuffer = (PBYTE)HeapReAlloc
  442. (GetProcessHeap(),
  443. 0,
  444. pbGetAllBuffer,
  445. dwRequestBufferSize);
  446. if (!pbGetAllBuffer)
  447. return HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
  448. hresError = pcAdmCom -> GetAllData (
  449. hmdHandle,
  450. TEXT ("/"),
  451. 0,
  452. 0,
  453. 0,
  454. &dwNumDataEntries,
  455. &dwDataSetNumber,
  456. dwRequestBufferSize,
  457. pbGetAllBuffer,
  458. &dwRequiredDataLen);
  459. }
  460. if (hresError != ERROR_SUCCESS)
  461. {
  462. HeapFree (GetProcessHeap(), 0, pbGetAllBuffer);
  463. return hresError;
  464. }
  465. METADATA_GETALL_RECORD *pmdgr = NULL;
  466. for (DWORD dwIndex = 0; dwIndex < dwNumDataEntries; dwIndex ++)
  467. {
  468. pmdgr = &(((METADATA_GETALL_RECORD *) pbGetAllBuffer)[dwIndex]);
  469. pmdgr->pbMDData = pmdgr->dwMDDataOffset + pbGetAllBuffer;
  470. if (pmdgr->dwMDIdentifier == 6019 && pmdgr->dwMDDataType == BINARY_METADATA &&
  471. pmdgr->dwMDDataLen > 0)
  472. {
  473. printf(" [%S]\n",lpwstrFullPath);
  474. // PrintProperty(*pmdgr, pcpCommandStructure->bShowSecure);
  475. BuildIplOblistFromBlob( *pmdgr);
  476. }
  477. }
  478. WCHAR *lpwstrTempPath = (WCHAR*) HeapAlloc
  479. (GetProcessHeap(),
  480. HEAP_ZERO_MEMORY,
  481. METADATA_MAX_NAME_LEN * sizeof (WCHAR));
  482. if (lpwstrTempPath == NULL)
  483. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  484. // Find out number of the children:
  485. DWORD dwChildCount = 0;
  486. while (1)
  487. {
  488. hresError = pcAdmCom->EnumKeys (
  489. hmdHandle,
  490. TEXT("/"),
  491. lpwstrTempPath,
  492. dwChildCount);
  493. if (hresError != ERROR_SUCCESS)
  494. break;
  495. dwChildCount++;
  496. }
  497. if (dwChildCount == 0)
  498. return ERROR_SUCCESS;
  499. // Dynamically allocate arrays:
  500. LPWSTR * lpwstrChildPath = new LPWSTR[dwChildCount];
  501. DWORD * dwSortedIndex = new DWORD[dwChildCount];
  502. // Initialization:
  503. for (dwIndex = 0; dwIndex < dwChildCount; dwIndex++)
  504. {
  505. dwSortedIndex[dwIndex] = dwIndex;
  506. hresError = pcAdmCom->EnumKeys (
  507. hmdHandle,
  508. TEXT("/"),
  509. lpwstrTempPath,
  510. dwIndex);
  511. lpwstrChildPath[dwIndex] = (WCHAR*) HeapAlloc
  512. (GetProcessHeap(),
  513. HEAP_ZERO_MEMORY,
  514. (wcslen (lpwstrTempPath) + 1) * sizeof (WCHAR));
  515. if (lpwstrChildPath[dwIndex] == NULL)
  516. {
  517. hresError = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  518. break;
  519. }
  520. else
  521. wcscpy(lpwstrChildPath[dwIndex], lpwstrTempPath);
  522. }
  523. if (hresError == ERROR_SUCCESS)
  524. {
  525. // Sort children lexicographically (here we assume that dwChildCount is small)
  526. if (dwChildCount > 1 )
  527. {
  528. DWORD dwTemp;
  529. for (DWORD i = 1; i < dwChildCount; i++)
  530. for (DWORD j=0; j < dwChildCount-i; j++)
  531. {
  532. if (wcscmp(lpwstrChildPath[dwSortedIndex[j]],lpwstrChildPath[dwSortedIndex[j+1]]) > 0)
  533. {
  534. dwTemp = dwSortedIndex[j+1];
  535. dwSortedIndex[j+1] = dwSortedIndex[j];
  536. dwSortedIndex[j] = dwTemp;
  537. }
  538. }
  539. }
  540. for (dwIndex = 0; dwIndex < dwChildCount; dwIndex++)
  541. {
  542. // create the full path name for the child:
  543. wsprintf(lpwstrTempPath,TEXT("%s/%s"),lpwstrFullPath,lpwstrChildPath[dwSortedIndex[dwIndex]]);
  544. HeapFree (GetProcessHeap(), 0, lpwstrChildPath[dwSortedIndex[dwIndex]]);
  545. hresError = PrintKeyRecursively (pcAdmCom, lpwstrTempPath, pcpCommandStructure);
  546. if (hresError != ERROR_SUCCESS)
  547. break;
  548. }
  549. }
  550. // Close keys, free memory and exit
  551. pcAdmCom->CloseKey(hmdHandle);
  552. delete lpwstrChildPath;
  553. delete dwSortedIndex;
  554. HeapFree (GetProcessHeap(), 0, lpwstrTempPath);
  555. return hresError;
  556. }
  557. VOID DisplayHelp()
  558. {
  559. fprintf (stderr, "\n DESCRIPTION: Displays the IP address/domain name restictions.\n\n");
  560. fprintf (stderr, " FORMAT: ipperm <StartKey> <MachineName>\n\n");
  561. fprintf (stderr, " <StartKey> : metabase key to start at.\n");
  562. fprintf (stderr, " <MachineName>: name of host (optional, default: localhost).\n\n");
  563. fprintf (stderr, " EXAMPLES: ipperm /w3svc/1 t-tamasn2\n");
  564. fprintf (stderr, " ipperm / > dump.txt (dump all to text)\n\n");
  565. }