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.

1107 lines
36 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 1992, 1998.
  5. //
  6. // File: domhash.c
  7. //
  8. // Contents: Implementation of public API for domain name lookup table
  9. //
  10. // History: SethuR -- Implemented
  11. // MikeSwa -- Modified for Domain Name lookup 2/98
  12. //
  13. // Notes:
  14. // 2/98 The major difference between the DFS version and the domain
  15. // name lookup is the size of the table, the ability for
  16. // wildcard lookups (*.foo.com), and the reverse order of the
  17. // lookup (com hashes first in foo.com). To make the code more
  18. // readable given its new purpose, the files, structures, and
  19. // functions have been given non DFS-centric names. A quick
  20. // mapping of the major files is (for those familiar with the
  21. // DFS code):
  22. // domhash.h (prefix.h) - Public include file
  23. // _domhash.h (prefixp.h) - Private include file
  24. // domhash.cpp (prefix.c) - Implementation of API
  25. // _domhash.cpp (prefixp.c) - Private helper functions.
  26. //
  27. //--------------------------------------------------------------------------
  28. #include "_domhash.h"
  29. #include <stdio.h>
  30. #define _ASSERT_DOMAIN_STRING(pstr) _ASSERT((_tcslen(pstr->Buffer)*sizeof(TCHAR)) == pstr->Length)
  31. //---[ DOMAIN_NAME_TABLE ]-----------------------------------------------------
  32. //
  33. //
  34. // Description:
  35. // Class constructor
  36. // Parameters:
  37. // -
  38. // Returns:
  39. // -
  40. //
  41. //-----------------------------------------------------------------------------
  42. DOMAIN_NAME_TABLE::DOMAIN_NAME_TABLE()
  43. {
  44. ULONG i;
  45. m_dwSignature = DOMAIN_NAME_TABLE_SIG;
  46. m_cLookupAttempts = 0;
  47. m_cLookupSuccesses = 0;
  48. m_cLookupCollisions = 0;
  49. m_cHashCollisions = 0;
  50. m_cStringCollisions = 0;
  51. m_cBucketsUsed = 0;
  52. INITIALIZE_DOMAIN_NAME_TABLE_ENTRY(&RootEntry);
  53. // Initialize the various buckets.
  54. for (i = 0;i < NO_OF_HASH_BUCKETS;i++)
  55. {
  56. INITIALIZE_BUCKET(Buckets[i]);
  57. }
  58. NamePageList.pFirstPage = NULL;
  59. }
  60. //---[ ~DOMAIN_NAME_TABLE ]----------------------------------------------------
  61. //
  62. //
  63. // Description:
  64. // Class destructor - Dumps some stats to stderr
  65. // Parameters:
  66. // -
  67. // Returns:
  68. // -
  69. //
  70. //-----------------------------------------------------------------------------
  71. DOMAIN_NAME_TABLE::~DOMAIN_NAME_TABLE()
  72. {
  73. PNAME_PAGE pCurrentPage = NamePageList.pFirstPage;
  74. PNAME_PAGE pNextPage = NULL;
  75. #ifdef DEBUG
  76. //$$TODO find a more appropriate way to dump this (don't use *printf)
  77. ULONG cTotalCollisions = m_cHashCollisions + m_cStringCollisions;
  78. ULONG ulPercentHash = cTotalCollisions ? (m_cHashCollisions*100/cTotalCollisions) : 0;
  79. ULONG ulPercentDesign = cTotalCollisions ? (m_cStringCollisions*100/cTotalCollisions) : 0;
  80. ULONG ulPercentCollisions = m_cLookupAttempts ? (m_cLookupCollisions*100/m_cLookupAttempts) : 0;
  81. ULONG ulAveCollisions = m_cLookupCollisions ? (cTotalCollisions/m_cLookupCollisions) : 0;
  82. fprintf(stderr, "\nHash statistics\n");
  83. fprintf(stderr, "==============================================\n");
  84. fprintf(stderr, "Total lookup attempts %d\n", m_cLookupAttempts);
  85. fprintf(stderr, "Total lookup successes %d\n", m_cLookupSuccesses);
  86. fprintf(stderr, "Total lookups with hash collisions %d\n", m_cLookupCollisions);
  87. fprintf(stderr, "%% of lookups with hash collisions %d%%\n", ulPercentCollisions);
  88. fprintf(stderr, "Total hash Collisions %d\n", cTotalCollisions);
  89. fprintf(stderr, "Average length of lookups collisions %d\n", ulAveCollisions);
  90. fprintf(stderr, "Hash collisions due to hash function %d\n", m_cHashCollisions);
  91. fprintf(stderr, "Hash collisions due to string parent %d\n", m_cStringCollisions);
  92. fprintf(stderr, "%% of collsions because of hash function %d%%\n", ulPercentHash);
  93. fprintf(stderr, "%% of collsions because of basic design %d%%\n", ulPercentDesign);
  94. fprintf(stderr, "Total number of buckets used %d\n", m_cBucketsUsed);
  95. fprintf(stderr, "%% buckets used %d%%\n", m_cBucketsUsed*100/NO_OF_HASH_BUCKETS);
  96. DumpTableContents();
  97. #endif //DEBUG
  98. //Free Name pages
  99. while (pCurrentPage)
  100. {
  101. pNextPage = pCurrentPage->pNextPage;
  102. FREE_NAME_PAGE(pCurrentPage);
  103. pCurrentPage = pNextPage;
  104. }
  105. }
  106. //+---------------------------------------------------------------------------
  107. //
  108. // Function: DOMAIN_NAME_TABLE::HrInit
  109. //
  110. // Synopsis: Member function for initializing the domain name table
  111. //
  112. // Returns: HRESULT - S_OK on success
  113. //
  114. // History: 04-18-94 SethuR Created (as DfsInitializePrefixTable)
  115. // 03-03-98 MikeSwa modified for Domain Table
  116. //
  117. // Notes:
  118. //
  119. //----------------------------------------------------------------------------
  120. HRESULT DOMAIN_NAME_TABLE::HrInit()
  121. {
  122. TraceFunctEnterEx((LPARAM) this, "DOMAIN_NAME_TABLE::HrInit");
  123. HRESULT hr = S_OK;
  124. // Initialize the name page list.
  125. NamePageList.pFirstPage = ALLOCATE_NAME_PAGE();
  126. if (NamePageList.pFirstPage != NULL)
  127. {
  128. INITIALIZE_NAME_PAGE(NamePageList.pFirstPage);
  129. }
  130. else
  131. {
  132. hr = E_OUTOFMEMORY;
  133. }
  134. TraceFunctLeave();
  135. return hr;
  136. }
  137. //+---------------------------------------------------------------------------
  138. //
  139. // Function: DOMAIN_NAME_TABLE::HrPrivInsertDomainName
  140. //
  141. // Synopsis: API for inserting a path in the prefix table
  142. //
  143. // Arguments: [pPath] -- the path to be looked up.
  144. //
  145. // [pvNewData] -- BLOB associated with the path
  146. //
  147. // [dwDomainNameTableFlags] -- flags that describe insert options
  148. // DMT_INSERT_AS_WILDCARD -
  149. // Set if the domain is NOT a wildcard
  150. // domain, but it should be treated as one (more efficient
  151. // than reallocated a string to prepend "*.").
  152. // DMT_REPLACE_EXISTRING -
  153. // Replace existing data if it exists. Old data is saved
  154. // in ppvOldData.
  155. //
  156. // [ppvOldData] -- Old Data (if any) that was previously associated
  157. // with this domain name. If NULL, previous data will
  158. // not be returned
  159. // Returns: HRESULT - S_OK on success
  160. //
  161. // History: 04-18-94 SethuR Created (as DfsInsertInPrefixTable)
  162. // 03-02-98 MikeSwa Modified for Domain Table
  163. // 05-11-98 MikeSwa... modified to support replace and treat
  164. // as wildcard options.
  165. //
  166. // Notes:
  167. //
  168. //----------------------------------------------------------------------------
  169. HRESULT DOMAIN_NAME_TABLE::HrPrivInsertDomainName(
  170. IN PDOMAIN_STRING pstrDomainName,
  171. IN DWORD dwDomainNameTableFlags,
  172. IN PVOID pvNewData,
  173. OUT PVOID *ppvOldData)
  174. {
  175. TraceFunctEnterEx((LPARAM) this, "DOMAIN_NAME_TABLE::HrPrivInsertDomainName");
  176. HRESULT hr = S_OK;
  177. TCHAR Buffer[MAX_PATH_SEGMENT_SIZE];
  178. PTCHAR NameBuffer = Buffer;
  179. USHORT cbNameBuffer = sizeof(Buffer);
  180. DOMAIN_STRING Path,Name;
  181. ULONG BucketNo;
  182. PDOMAIN_NAME_TABLE_ENTRY pEntry = NULL;
  183. PDOMAIN_NAME_TABLE_ENTRY pParentEntry = NULL;
  184. BOOL fNameFound = FALSE;
  185. BOOL fWildcard = FALSE;
  186. BOOL fReplaced = FALSE;
  187. _ASSERT_DOMAIN_STRING(pstrDomainName);
  188. // There is one special case, i.e., in which the domain name is '*'.
  189. // Since this is the WILDCARD_CHAR which is treated in a special
  190. // way, we do the processing upfront.
  191. if (pstrDomainName->Length == 0 || pvNewData == NULL)
  192. {
  193. hr = E_INVALIDARG;
  194. goto Exit;
  195. }
  196. if (ppvOldData)
  197. *ppvOldData = NULL;
  198. Path.Length = pstrDomainName->Length;
  199. Path.MaximumLength = pstrDomainName->MaximumLength;
  200. Path.Buffer = pstrDomainName->Buffer;
  201. pParentEntry = &RootEntry;
  202. //Check if wildcard "*."
  203. if (DMT_INSERT_AS_WILDCARD & dwDomainNameTableFlags)
  204. {
  205. fWildcard = TRUE;
  206. _ASSERT(!fAdjustPathIfWildcard(pstrDomainName, &Path));
  207. }
  208. else if (fAdjustPathIfWildcard(pstrDomainName, &Path))
  209. {
  210. fWildcard = TRUE;
  211. }
  212. else if (fIsWildcardRoot(pstrDomainName))
  213. {
  214. if (RootEntry.pWildCardData != NULL)
  215. {
  216. hr = DOMHASH_E_DOMAIN_EXISTS;
  217. }
  218. else
  219. {
  220. RootEntry.pWildCardData = pvNewData;
  221. }
  222. goto Exit;
  223. }
  224. if (Path.Length >= MAX_PATH_SEGMENT_SIZE) {
  225. NameBuffer = (PTCHAR) pvMalloc(Path.Length + sizeof(TCHAR));
  226. if (NameBuffer == NULL) {
  227. hr = E_OUTOFMEMORY;
  228. DebugTrace((LPARAM) hr, "ERROR: Unable to allocate %d non-paged bytes", (Path.Length + sizeof(TCHAR)) );
  229. goto Exit;
  230. } else {
  231. cbNameBuffer = Path.Length + sizeof(TCHAR);
  232. }
  233. }
  234. while (Path.Length > 0)
  235. {
  236. Name.Length = 0;
  237. Name.Buffer = NameBuffer;
  238. Name.MaximumLength = cbNameBuffer;
  239. // Process the name segment
  240. BucketNo = ulSplitCaseInsensitivePath(&Path,&Name);
  241. if (Name.Length > 0)
  242. {
  243. // Lookup the table to see if the name segment already exists.
  244. LookupBucket(&(Buckets[BucketNo]),&Name,pParentEntry,&pEntry,&fNameFound);
  245. DebugTrace((LPARAM) pEntry, "Returned pEntry");
  246. if (pEntry == NULL)
  247. {
  248. // Initialize the new entry and initialize the name segment.
  249. pEntry = ALLOCATE_DOMAIN_NAME_TABLE_ENTRY(this);
  250. if (!pEntry)
  251. {
  252. hr = E_OUTOFMEMORY;
  253. break;
  254. }
  255. INITIALIZE_DOMAIN_NAME_TABLE_ENTRY(pEntry);
  256. // Allocate the name space entry if there is no entry in the
  257. // name page.
  258. if (!fNameFound)
  259. {
  260. PTSTR pBuffer;
  261. // Allocate the entry in the name page.
  262. pBuffer = ALLOCATE_NAME_PAGE_ENTRY(NamePageList,(Name.Length/sizeof(TCHAR)));
  263. if (pBuffer != NULL)
  264. {
  265. RtlCopyMemory(pBuffer,Name.Buffer,Name.Length);
  266. pEntry->PathSegment = Name;
  267. pEntry->PathSegment.Buffer = pBuffer;
  268. }
  269. else
  270. {
  271. hr = E_OUTOFMEMORY;
  272. //We shan't leak memory
  273. FREE_DOMAIN_NAME_TABLE_ENTRY(pEntry);
  274. pEntry = NULL;
  275. break;
  276. }
  277. }
  278. else
  279. pEntry->PathSegment = Name;
  280. // thread the entry to point to the parent.
  281. pEntry->pParentEntry = pParentEntry;
  282. // Insert the entry in the bucket.
  283. if (0 == Buckets[BucketNo].NoOfEntries)
  284. InterlockedIncrement((PLONG) &m_cBucketsUsed);
  285. INSERT_IN_BUCKET(Buckets[BucketNo],pEntry);
  286. // Insert the entry in the parent's children list.
  287. INSERT_IN_CHILD_LIST(pEntry, pParentEntry);
  288. }
  289. else
  290. {
  291. // Increment the no. of children associated with this entry
  292. pEntry->NoOfChildren++;
  293. }
  294. pParentEntry = pEntry;
  295. }
  296. else
  297. {
  298. hr = E_INVALIDARG;
  299. DebugTrace((LPARAM) hr, "ERROR: Unable to insert domain name");
  300. break;
  301. }
  302. }
  303. if (SUCCEEDED(hr))
  304. {
  305. // The entry was successfully inserted in the prefix table. Update
  306. // the data (BLOB) associated with it.
  307. // We do it outside the loop to prevent redundant comparisons within
  308. // the loop.
  309. if (fWildcard)
  310. {
  311. if (pEntry->pWildCardData) //make sure we aren't writing over anything
  312. {
  313. if (ppvOldData)
  314. *ppvOldData = pEntry->pWildCardData;
  315. if (DMT_REPLACE_EXISTRING & dwDomainNameTableFlags)
  316. {
  317. fReplaced = TRUE;
  318. pEntry->pWildCardData = pvNewData;
  319. }
  320. else
  321. {
  322. hr = DOMHASH_E_DOMAIN_EXISTS;
  323. }
  324. }
  325. else
  326. {
  327. pEntry->pWildCardData = pvNewData;
  328. }
  329. }
  330. else
  331. {
  332. if (pEntry->pData) //make sure we aren't writing over anything
  333. {
  334. if (ppvOldData)
  335. *ppvOldData = pEntry->pData;
  336. if (DMT_REPLACE_EXISTRING & dwDomainNameTableFlags)
  337. {
  338. fReplaced = TRUE;
  339. pEntry->pData = pvNewData;
  340. }
  341. else
  342. {
  343. hr = DOMHASH_E_DOMAIN_EXISTS;
  344. }
  345. }
  346. else
  347. {
  348. pEntry->pData = pvNewData;
  349. }
  350. }
  351. }
  352. // If a new entry was not successfully inserted we need to walk up the chain
  353. // of parent entries to undo the increment to the reference count and
  354. // remove the entries from their parent links.
  355. if (FAILED(hr) || //hr could be set in above if statement
  356. fReplaced) //remove extra child counts
  357. {
  358. while (pParentEntry != NULL)
  359. {
  360. PDOMAIN_NAME_TABLE_ENTRY pMaybeTempEntry;
  361. pMaybeTempEntry = pParentEntry;
  362. pParentEntry = pParentEntry->pParentEntry;
  363. if (pParentEntry && --pMaybeTempEntry->NoOfChildren == 0) {
  364. //
  365. // If pParentEntry == NULL, pMaybeTempEntry is
  366. // RootEntry. Do not try to remove it.
  367. //
  368. _ASSERT(FAILED(hr) && "We shouldn't get here during replace");
  369. REMOVE_FROM_CHILD_LIST(pMaybeTempEntry);
  370. REMOVE_FROM_BUCKET(pMaybeTempEntry);
  371. FREE_DOMAIN_NAME_TABLE_ENTRY(pMaybeTempEntry);
  372. }
  373. }
  374. }
  375. Exit:
  376. TraceFunctLeave();
  377. return hr;
  378. }
  379. //+---------------------------------------------------------------------------
  380. //
  381. // Function: DOMAIN_NAME_TABLE::HrFindDomainName
  382. //
  383. // Synopsis: Method API for looking up a name segment in a prefix table
  384. //
  385. // Arguments: IN pPath -- the path to be looked up.
  386. //
  387. // OUT ppData -- placeholder for the BLOB for the prefix.
  388. //
  389. // IN fExtactMatch -- FALSE if wildcard matches are allowed
  390. //
  391. // Returns: HRESULT - S_OK on success
  392. //
  393. // History: 04-18-94 SethuR Created (as DfsLookupPrefixTable)
  394. // 03-02-98 MikeSwa Modified for Domain Table
  395. // 06-03-98 MikeSwa Modified to use new HrLookupDomainName
  396. //
  397. // Notes:
  398. //
  399. //----------------------------------------------------------------------------
  400. HRESULT DOMAIN_NAME_TABLE::HrFindDomainName(
  401. PDOMAIN_STRING pPath,
  402. PVOID *ppData,
  403. BOOL fExactMatch)
  404. {
  405. TraceFunctEnterEx((LPARAM) this, "DOMAIN_NAME_TABLE::HrFindDomainName");
  406. HRESULT hr = S_OK;
  407. PDOMAIN_NAME_TABLE_ENTRY pEntry = NULL;
  408. BOOL fExactMatchFound = FALSE;
  409. _ASSERT_DOMAIN_STRING(pPath);
  410. if (pPath->Length == 0)
  411. {
  412. hr = E_INVALIDARG;
  413. }
  414. else
  415. {
  416. hr = HrLookupDomainName(pPath, &fExactMatchFound, &pEntry);
  417. // Update the BLOB placeholder with the results of the lookup.
  418. if (SUCCEEDED(hr))
  419. {
  420. _ASSERT(pEntry);
  421. if (fExactMatchFound && pEntry->pData)
  422. {
  423. //exact match found & non-wildcard data is there... use it!
  424. *ppData = pEntry->pData;
  425. }
  426. else if (fExactMatch) //exact match requested, but none found
  427. {
  428. hr = DOMHASH_E_NO_SUCH_DOMAIN;
  429. }
  430. else //exact match not requested
  431. {
  432. //Find the first ancestor with wildcard data
  433. while (pEntry->pParentEntry && !pEntry->pWildCardData)
  434. {
  435. _ASSERT(pEntry != &RootEntry);
  436. pEntry = pEntry->pParentEntry;
  437. }
  438. *ppData = pEntry->pWildCardData;
  439. if (!*ppData) //no wildcard match found
  440. {
  441. _ASSERT(pEntry == &RootEntry); //We should search back to root
  442. hr = DOMHASH_E_NO_SUCH_DOMAIN;
  443. }
  444. }
  445. }
  446. else if (!fExactMatch && (DOMHASH_E_NO_SUCH_DOMAIN == hr))
  447. {
  448. //if we don't require an exact match.... check the wildcard root
  449. if (RootEntry.pWildCardData)
  450. {
  451. hr = S_OK;
  452. *ppData = RootEntry.pWildCardData;
  453. }
  454. }
  455. }
  456. TraceFunctLeave();
  457. return hr;
  458. }
  459. //+---------------------------------------------------------------------------
  460. //
  461. // Function: DOMAIN_NAME_TABLE::HrRemoveDomainName
  462. //
  463. // Synopsis: private fn. for looking up a name segment in a prefix table
  464. //
  465. // Arguments: [pPath] -- the path to be removed from table.
  466. // [ppvData] - Data that WAS stored in entry
  467. //
  468. // Returns: HRESULT
  469. // S_OK on success
  470. // DOMHASH_E_NO_SUCH_DOMAIN if not found
  471. //
  472. // History: 04-18-94 SethuR Created (as DfsRemoveFromPrefixTable)
  473. // 03-03-98 MikeSwa - Updated for Domain Table
  474. // 06-03-98 MikeSwa - Modified to use new HrLookupDomainName
  475. //
  476. // Notes:
  477. //
  478. //----------------------------------------------------------------------------
  479. HRESULT DOMAIN_NAME_TABLE::HrRemoveDomainName(PDOMAIN_STRING pPath, PVOID *ppvData)
  480. {
  481. TraceFunctEnterEx((LPARAM) this, "DOMAIN_NAME_TABLE::HrRemoveDomainName");
  482. HRESULT hr = S_OK;
  483. DOMAIN_STRING Path;
  484. BOOL fWildcard = FALSE;
  485. BOOL fExactMatchFound = FALSE;
  486. _ASSERT_DOMAIN_STRING(pPath);
  487. PDOMAIN_NAME_TABLE_ENTRY pEntry = NULL;
  488. PDOMAIN_NAME_TABLE_ENTRY pTempEntry = NULL;
  489. if (!ppvData)
  490. {
  491. hr = E_INVALIDARG;
  492. goto Exit;
  493. }
  494. if (pPath->Length == 0)
  495. {
  496. hr = E_INVALIDARG;
  497. goto Exit;
  498. }
  499. Path.Length = pPath->Length;
  500. Path.MaximumLength = pPath->MaximumLength;
  501. Path.Buffer = pPath->Buffer;
  502. if (fAdjustPathIfWildcard(pPath, &Path))
  503. {
  504. fWildcard = TRUE;
  505. }
  506. else if (fIsWildcardRoot(pPath))
  507. {
  508. *ppvData = RootEntry.pWildCardData;
  509. if (!*ppvData)
  510. {
  511. hr = DOMHASH_E_NO_SUCH_DOMAIN;
  512. }
  513. RootEntry.pWildCardData = NULL;
  514. goto Exit;
  515. }
  516. hr = HrLookupDomainName(&Path, &fExactMatchFound, &pEntry);
  517. if (SUCCEEDED(hr))
  518. {
  519. if (!fExactMatchFound)
  520. {
  521. //only a partial match was found
  522. hr = DOMHASH_E_NO_SUCH_DOMAIN;
  523. goto Exit;
  524. }
  525. // Destroy the association between the data associated with
  526. // this prefix.
  527. if (!fWildcard)
  528. {
  529. *ppvData = pEntry->pData;
  530. pEntry->pData = NULL;
  531. }
  532. else
  533. {
  534. *ppvData = pEntry->pWildCardData;
  535. pEntry->pWildCardData = NULL;
  536. }
  537. if (!*ppvData) //no data of of requested type in entry
  538. {
  539. //Make sure this isn't a completely NULL data leaf node (ie no way to delete it)
  540. _ASSERT(pEntry->pFirstChildEntry || pEntry->pData || pEntry->pWildCardData);
  541. hr = DOMHASH_E_NO_SUCH_DOMAIN;
  542. goto Exit;
  543. }
  544. // found an exact match for the given path name in the table.
  545. // traverse the list of parent pointers and delete them if
  546. // required.
  547. RemoveTableEntry(pEntry);
  548. }
  549. Exit:
  550. TraceFunctLeave();
  551. return hr;
  552. }
  553. //+---------------------------------------------------------------------------
  554. //
  555. // Function: DOMAIN_NAME_TABLE::HrLookupDomainName
  556. //
  557. // Synopsis: Private function for looking up an *entry* in the table. It
  558. // makes no guarantees that there is user data available for the
  559. // returned entry. This is the caller's responsibility. It will
  560. // match the longest partial path... check fExactMatch to see
  561. // if an exact match was found
  562. //
  563. // Arguments: IN pPath -- the path to be looked up.
  564. //
  565. // OUT pfExactMatch -- Exact Match was found
  566. //
  567. // OUT ppEntry -- The matching entry for the path.
  568. //
  569. //
  570. // Returns: HRESULT
  571. // S_OK on success
  572. // DOMHASH_E_NO_SUCH_DOMAIN if not found
  573. // E_OUTOFMEMORY
  574. //
  575. // History: 04-18-94 SethuR Created (as _LookupPrefixTable)
  576. // 03-03-98 MikeSwa Modified for Domain Table
  577. // 06-03-98 MikeSwa ExactMatch changed to OUT parameter
  578. //
  579. // Notes:
  580. //
  581. //----------------------------------------------------------------------------
  582. HRESULT DOMAIN_NAME_TABLE::HrLookupDomainName(
  583. DOMAIN_STRING *pPath,
  584. BOOL *pfExactMatch,
  585. PDOMAIN_NAME_TABLE_ENTRY *ppEntry)
  586. {
  587. TraceFunctEnterEx((LPARAM) this, "DOMAIN_NAME_TABLE::HrLookupDomainName");
  588. HRESULT hr = S_OK;
  589. DOMAIN_STRING Path = *pPath;
  590. TCHAR Buffer[MAX_PATH_SEGMENT_SIZE];
  591. PTCHAR NameBuffer = Buffer;
  592. USHORT cbNameBuffer = sizeof(Buffer);
  593. DOMAIN_STRING Name;
  594. ULONG BucketNo;
  595. BOOL fPrefixFound = FALSE;
  596. PDOMAIN_NAME_TABLE_ENTRY pEntry = NULL;
  597. PDOMAIN_NAME_TABLE_ENTRY pParentEntry = &RootEntry;
  598. BOOL fNameFound = FALSE;
  599. _ASSERT(Path.Buffer[0] != PATH_DELIMITER);
  600. *pfExactMatch = FALSE;
  601. if (Path.Length >= MAX_PATH_SEGMENT_SIZE) {
  602. NameBuffer = (PTCHAR) pvMalloc(Path.Length + sizeof(TCHAR));
  603. if (NameBuffer == NULL) {
  604. hr = E_OUTOFMEMORY;
  605. DebugTrace((LPARAM) hr, "ERROR: Unable to allocate %d non-paged bytes", (Path.Length + sizeof(TCHAR)) );
  606. goto Exit;
  607. } else {
  608. cbNameBuffer = Path.Length + sizeof(TCHAR);
  609. }
  610. }
  611. while (Path.Length > 0)
  612. {
  613. Name.Length = 0;
  614. Name.Buffer = NameBuffer;
  615. Name.MaximumLength = cbNameBuffer;
  616. BucketNo = ulSplitCaseInsensitivePath(&Path,&Name);
  617. if (Name.Length > 0)
  618. {
  619. // Process the name segment
  620. // Lookup the bucket to see if the entry exists.
  621. LookupBucket(&(Buckets[BucketNo]),&Name,pParentEntry,&pEntry,&fNameFound);
  622. DebugTrace((LPARAM) pEntry, "Returned pEntry");
  623. if (pEntry != NULL)
  624. {
  625. *pfExactMatch = TRUE;
  626. _ASSERT(fNameFound && "Lookup bucket is broken");
  627. // Cache the data available for this prefix if any.
  628. *ppEntry = pEntry;
  629. }
  630. else
  631. {
  632. *pfExactMatch = FALSE;
  633. break;
  634. }
  635. // set the stage for processing the next name segment.
  636. pParentEntry = pEntry;
  637. }
  638. }
  639. //Not even a partial match was found
  640. if (!*ppEntry)
  641. {
  642. _ASSERT(FALSE == *pfExactMatch);
  643. hr = DOMHASH_E_NO_SUCH_DOMAIN;
  644. DebugTrace((LPARAM) hr, "INFO: Path %s not found", pPath->Buffer);
  645. }
  646. Exit:
  647. TraceFunctLeave();
  648. return hr;
  649. }
  650. //---[ DOMAIN_NAME_TABLE::HrIterateOverSubDomains ]----------------------------
  651. //
  652. //
  653. // Description:
  654. //
  655. // Parameters:
  656. // IN strDomain - Domain string to search for subdomains of
  657. // (should not start with "*.")
  658. // IN pfn - Mapping function (described below)
  659. // IN pvContext - Context ptr pass to mapping function
  660. //
  661. // Notes:
  662. // VOID DomainTableInteratorFunction(
  663. // IN PVOID pvContext, //context passed to HrIterateOverSubDomains
  664. // IN PVOID pvData, //data entry to look at
  665. // IN BOOL fWildcardData, //true if data is a wildcard entry
  666. // OUT BOOL *pfContinue, //TRUE if iterator should continue to the next entry
  667. // OUT BOOL *pfRemoveEntry); //TRUE if entry should be deleted
  668. //
  669. // Returns:
  670. // S_OK on success
  671. // DOMHASH_E_NO_SUCH_DOMAIN if there is no matching domain or subdomains
  672. // History:
  673. // 6/5/98 - MikeSwa Created
  674. //
  675. //-----------------------------------------------------------------------------
  676. HRESULT DOMAIN_NAME_TABLE::HrIterateOverSubDomains(
  677. IN DOMAIN_STRING *pstrDomain,
  678. IN DOMAIN_ITR_FN pfn,
  679. IN PVOID pvContext)
  680. {
  681. HRESULT hr = S_OK;
  682. PDOMAIN_NAME_TABLE_ENTRY pEntry = NULL;
  683. PDOMAIN_NAME_TABLE_ENTRY pRootEntry = NULL;
  684. PDOMAIN_NAME_TABLE_ENTRY pNextEntry = NULL;
  685. BOOL fExactMatchFound = FALSE;
  686. BOOL fContinue = TRUE;
  687. BOOL fDelete = FALSE;
  688. DWORD cDomainsFound = 0;
  689. BOOL fWildcard = FALSE;
  690. _ASSERT(pfn && "Invalid Param - pfn");
  691. _ASSERT((!pstrDomain || (WILDCARD_CHAR != pstrDomain->Buffer[0])) && "Invalid param - string starts with '*'");
  692. if (pstrDomain)
  693. {
  694. hr = HrLookupDomainName(pstrDomain, &fExactMatchFound, &pEntry);
  695. if (FAILED(hr))
  696. goto Exit;
  697. if (!fExactMatchFound) //there must be an entry at root of subtree
  698. {
  699. hr = DOMHASH_E_NO_SUCH_DOMAIN;
  700. goto Exit;
  701. }
  702. _ASSERT(pEntry);
  703. }
  704. else
  705. {
  706. //if !pstrDomain.., iterate over entire hash table
  707. pEntry = &RootEntry;
  708. }
  709. pRootEntry = pEntry;
  710. //Traverse all the child entries of pRootEntry (preorder)
  711. while (pEntry)
  712. {
  713. //get next entry before it is deleted
  714. pNextEntry = pNextTableEntry(pEntry, pRootEntry);
  715. //This check must be done before call to RemoveTableEntry
  716. //If there is no wildcard data, then entry might be deleted
  717. //after call to RemoveTableEntry (if it has no children)
  718. fWildcard = (NULL != pEntry->pWildCardData);
  719. if (pEntry->pData)
  720. {
  721. cDomainsFound++;
  722. pfn(pvContext, pEntry->pData, FALSE, &fContinue, &fDelete);
  723. if (fDelete)
  724. {
  725. pEntry->pData = NULL;
  726. RemoveTableEntry(pEntry);
  727. }
  728. if (!fContinue)
  729. break;
  730. }
  731. if (fWildcard)
  732. {
  733. cDomainsFound++;
  734. pfn(pvContext, pEntry->pWildCardData, TRUE, &fContinue, &fDelete);
  735. if (fDelete)
  736. {
  737. pEntry->pWildCardData = NULL;
  738. RemoveTableEntry(pEntry);
  739. }
  740. if (!fContinue)
  741. break;
  742. }
  743. pEntry = pNextEntry;
  744. }
  745. if (!cDomainsFound)
  746. hr = DOMHASH_E_NO_SUCH_DOMAIN;
  747. Exit:
  748. return hr;
  749. }
  750. //+----------------------------------------------------------------------------
  751. //
  752. // Function: DOMAIN_NAME_TABLE::pvNextDomainName
  753. //
  754. // Synopsis: Enumerates the entries in the table in ordered fashion.
  755. // Note that state is maintained between calls to
  756. // pvNextDomainName - the caller must ensure that the table
  757. // is not modified between calls to pNextTableEntry by acquiring
  758. // an external read lock
  759. //
  760. // Arguments: IN OUT PVOID *ppvContext - context used to hold place
  761. //
  762. // Returns: Valid pointer to data associated with the next Prefix Table
  763. // entry, or NULL if at the end of the enumeration.
  764. //
  765. //-----------------------------------------------------------------------------
  766. PVOID DOMAIN_NAME_TABLE::pvNextDomainName(IN OUT PVOID *ppvContext)
  767. {
  768. PDOMAIN_NAME_TABLE_ENTRY pEntry, pNextEntry;
  769. PVOID pvData = NULL;
  770. bool fDataUsed = false;
  771. _ASSERT(ppvContext);
  772. if ((PVOID) this == *ppvContext)
  773. {
  774. *ppvContext = NULL;
  775. goto Exit;
  776. }
  777. //Find entry to get data for
  778. if (!*ppvContext)
  779. {
  780. //We're starting over
  781. pNextEntry = &RootEntry;
  782. //Find first entry with valid data
  783. while (pNextEntry != NULL &&
  784. pNextEntry->pData == NULL &&
  785. pNextEntry->pWildCardData == NULL)
  786. {
  787. pNextEntry = pNextTableEntry(pNextEntry);
  788. }
  789. }
  790. else
  791. {
  792. //Use context provided as starting point
  793. if (ENTRY_SIG == **((DWORD**) ppvContext))
  794. {
  795. pNextEntry = (PDOMAIN_NAME_TABLE_ENTRY) *ppvContext;
  796. }
  797. else
  798. {
  799. _ASSERT(WILDCARD_SIG == **((DWORD **) ppvContext));
  800. pNextEntry = CONTAINING_RECORD(*ppvContext, DOMAIN_NAME_TABLE_ENTRY, dwWildCardSig);
  801. _ASSERT(ENTRY_SIG == pNextEntry->dwEntrySig);
  802. fDataUsed = true;
  803. }
  804. //If this is a next entry... either pData or pWildCard should be non-NULL
  805. _ASSERT(pNextEntry->pData || pNextEntry->pWildCardData);
  806. }
  807. pEntry = pNextEntry;
  808. //Save data to return in pvData
  809. if (pEntry != NULL)
  810. {
  811. if (pEntry->pData && !fDataUsed)
  812. {
  813. pvData = pEntry->pData;
  814. }
  815. else
  816. {
  817. _ASSERT(pEntry->pWildCardData);
  818. pvData = pEntry->pWildCardData;
  819. }
  820. }
  821. //Determine what context to return
  822. if (pNextEntry != NULL)
  823. {
  824. if (!fDataUsed && pNextEntry->pWildCardData && pEntry->pData)
  825. {
  826. //use wildcard data next time through
  827. *ppvContext = (PVOID) &(pNextEntry->dwWildCardSig);
  828. }
  829. else
  830. {
  831. do //find next entry that does not point to NULL info
  832. {
  833. pNextEntry = pNextTableEntry( pNextEntry );
  834. } while ( pNextEntry != NULL &&
  835. pNextEntry->pData == NULL &&
  836. pNextEntry->pWildCardData == NULL);
  837. *ppvContext = (PVOID) pNextEntry;
  838. _ASSERT(*ppvContext != (PVOID) this); //so our sentinal value works
  839. if (NULL == *ppvContext)
  840. {
  841. *ppvContext = (PVOID) this;
  842. }
  843. }
  844. }
  845. Exit:
  846. return pvData;
  847. }
  848. //+----------------------------------------------------------------------------
  849. //
  850. // Function: pNextTableEntry
  851. //
  852. // Synopsis: Given a pointer to a Prefix Table Entry, this function will
  853. // return a pointer to the "next" prefix table entry.
  854. //
  855. // The "next" entry is chosen as follows:
  856. // If the start entry has a valid child, the child is
  857. // is returned.
  858. // else if the start entry has a valid sibling, the sibling
  859. // is returned
  860. // else the first valid sibling of the closest ancestor is
  861. // returned.
  862. //
  863. // Arguments: [pEntry] -- The entry to start from.
  864. // [pRootEntry] -- Root node of subtree being enumerated
  865. // (NULL or address of root entry will do all)
  866. //
  867. // Returns: Pointer to the next DOMAIN_NAME_TABLE_ENTRY that has a valid
  868. // pData, or NULL if there are no more entries.
  869. //
  870. // Note: You must have a read lock over a sequence of calls into this
  871. // function (you cannot release it between calls).
  872. // History;
  873. // 06/09/98 - Mikeswa modified to accept RootEntry
  874. //
  875. //-----------------------------------------------------------------------------
  876. PDOMAIN_NAME_TABLE_ENTRY
  877. DOMAIN_NAME_TABLE::pNextTableEntry(IN PDOMAIN_NAME_TABLE_ENTRY pEntry,
  878. IN PDOMAIN_NAME_TABLE_ENTRY pRootEntry)
  879. {
  880. PDOMAIN_NAME_TABLE_ENTRY pNextEntry = NULL;
  881. _ASSERT(pEntry);
  882. if (pEntry->pFirstChildEntry != NULL)
  883. {
  884. pNextEntry = pEntry->pFirstChildEntry;
  885. }
  886. else if ((pEntry->pSiblingEntry != NULL) && //if there is a sibling entry
  887. (pEntry != pRootEntry)) //this is not the root entry
  888. {
  889. //Should have same parent
  890. _ASSERT(pEntry->pParentEntry == pEntry->pSiblingEntry->pParentEntry);
  891. pNextEntry = pEntry->pSiblingEntry;
  892. }
  893. else
  894. {
  895. for (pNextEntry = pEntry->pParentEntry;
  896. pNextEntry != NULL &&
  897. pNextEntry->pSiblingEntry == NULL &&
  898. pNextEntry != pRootEntry;
  899. pNextEntry = pNextEntry->pParentEntry)
  900. {
  901. //NOTHING;
  902. }
  903. if (pNextEntry == pRootEntry)
  904. {
  905. pNextEntry = NULL;
  906. }
  907. else if (pNextEntry != NULL)
  908. {
  909. pNextEntry = pNextEntry->pSiblingEntry;
  910. }
  911. }
  912. return pNextEntry;
  913. }
  914. //---[ DOMAIN_NAME_TABLE::DumpTableContents ]----------------------------------
  915. //
  916. //
  917. // Description:
  918. // Print out contents of table. Intended primarily for leak detection
  919. // during table destructor
  920. // Parameters:
  921. // -
  922. // Returns:
  923. // -
  924. //
  925. //-----------------------------------------------------------------------------
  926. void DOMAIN_NAME_TABLE::DumpTableContents()
  927. {
  928. PDOMAIN_NAME_TABLE_ENTRY pEntry = NULL;
  929. DOMAIN_STRING Path;
  930. CHAR Buffer[MAX_PATH_SEGMENT_SIZE+1];
  931. DWORD cLeaks = 0;
  932. Path.Length = 0;
  933. Path.MaximumLength = MAX_PATH_SEGMENT_SIZE;
  934. Path.Buffer = Buffer;
  935. //Check for leaked entries
  936. pEntry = pNextTableEntry(&RootEntry);
  937. if (pEntry)
  938. {
  939. fprintf(stderr, "\nFOUND LEAKED ENTRIES!!\n\n");
  940. fprintf(stderr, "Entry ID # Children pData pWildCard Path\n");
  941. fprintf(stderr, "===========================================================================\n");
  942. while(pEntry)
  943. {
  944. _ASSERT(pEntry);
  945. GET_DOMAIN_NAME_TABLE_ENTRY_PATH(pEntry, &Path);
  946. fprintf(stderr, "0x%p %10.10d 0x%p 0x%p %s\n", pEntry,
  947. pEntry->NoOfChildren, pEntry->pData, pEntry->pWildCardData, Path.Buffer);
  948. cLeaks++;
  949. pEntry = pNextTableEntry(pEntry);
  950. }
  951. fprintf(stderr, "===========================================================================\n");
  952. fprintf(stderr, "Total Leaks: %d\n", cLeaks);
  953. }
  954. }
  955. //---[ DOMAIN_NAME_TABLE::RemoveTableEntry ]------------------------------------
  956. //
  957. //
  958. // Description:
  959. // Removes an entry from the table
  960. // Parameters:
  961. // IN pentry - Entry to remove
  962. // Returns:
  963. // -
  964. // History:
  965. // 6/8/98 - MikeSwa Created
  966. //
  967. //-----------------------------------------------------------------------------
  968. void DOMAIN_NAME_TABLE::RemoveTableEntry(IN PDOMAIN_NAME_TABLE_ENTRY pEntry)
  969. {
  970. PDOMAIN_NAME_TABLE_ENTRY pTempEntry = NULL;
  971. while (pEntry != NULL)
  972. {
  973. pTempEntry = pEntry;
  974. pEntry = pEntry->pParentEntry;
  975. if (pEntry && (--pTempEntry->NoOfChildren) == 0)
  976. {
  977. _ASSERT(!pTempEntry->pData && !pTempEntry->pWildCardData);
  978. //
  979. // pEntry == NULL means pTempEntry is pTable->RootEntry.
  980. // Do not try to remove it. (we also do not maintain a child count
  981. // on it).
  982. //
  983. REMOVE_FROM_CHILD_LIST(pTempEntry);
  984. REMOVE_FROM_BUCKET(pTempEntry);
  985. FREE_DOMAIN_NAME_TABLE_ENTRY(pTempEntry);
  986. }
  987. }
  988. }