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.

1435 lines
30 KiB

  1. /*++
  2. Copyright (c) 1997-2000 Microsoft Corporation
  3. Module Name:
  4. cache.c
  5. Abstract:
  6. DNS Resolver Service
  7. Cache routines
  8. Author:
  9. Glenn Curtis (glennc) March 1997
  10. Revision History:
  11. Jim Gilroy (jamesg) February 2000 cleanup
  12. --*/
  13. #include "local.h"
  14. //
  15. // Global Declarations
  16. //
  17. #define IS_STATIC_TTL_RECORD(prr) IS_HOSTS_FILE_RR(prr)
  18. //
  19. // Cache hash table
  20. //
  21. extern PCACHE_ENTRY * g_HashTable;
  22. extern DWORD g_CacheEntryCount;
  23. extern DWORD g_CacheRecordSetCount;
  24. extern DWORD g_CurrentCacheTime;
  25. #define INITIAL_CACHE_HEAP_SIZE (16*1024)
  26. //
  27. // Compute a hash table index value for a string
  28. //
  29. #define EOS (L'\0')
  30. #define COMPUTE_STRING_HASH_1( _String, _ulHashTableSize, _lpulHash ) \
  31. { \
  32. PWCHAR p; \
  33. ULOND h = 0, g; \
  34. \
  35. for ( p = _String; *p != EOS; p = p + 1 ) \
  36. { \
  37. h = ( h << 4 ) + (DWORD) (*p); \
  38. if ( g = h&0xf0000000 ) \
  39. { \
  40. h = h ^ ( g >> 24 ); \
  41. h = h ^ g; \
  42. } \
  43. } \
  44. *_lpulHash = h % _ulHashTableSize; \
  45. }
  46. //
  47. // Compute a hash table index value for a string
  48. // which is invairant to case
  49. //
  50. #define COMPUTE_STRING_HASH_2( _String, _ulHashTableSize, _lpulHash ) \
  51. { \
  52. PWCHAR _p = _String; \
  53. PWCHAR _ep = _p + wcslen( _String ); \
  54. ULONG h = 0; \
  55. \
  56. while( _p < _ep ) \
  57. { \
  58. h <<= 1; \
  59. h ^= *_p++; \
  60. } \
  61. \
  62. *_lpulHash = h % _ulHashTableSize; \
  63. }
  64. //
  65. // Private prototypes
  66. //
  67. BOOL
  68. IsInvalidNegativeCacheEntry(
  69. IN PDNS_RECORD
  70. );
  71. PDNS_RECORD
  72. BuildDNSServerRecord(
  73. IN IP_ADDRESS Address
  74. );
  75. PDNS_RECORD
  76. BuildLocalAddressRecords(
  77. IN PSTR Name
  78. );
  79. BOOL
  80. IsLocalAddress(
  81. IN IP_ADDRESS Ip
  82. );
  83. //
  84. // From ncache.c
  85. //
  86. BOOL
  87. makeCannonicalCacheName(
  88. OUT PWCHAR pNameBuffer,
  89. IN DWORD BufferLength,
  90. IN PWSTR pName,
  91. IN DWORD NameLength OPTIONAL
  92. );
  93. DWORD
  94. getHashIndex(
  95. IN PWSTR pName,
  96. IN DWORD NameLength OPTIONAL
  97. );
  98. //
  99. // Define to new routine
  100. //
  101. #define PrepareRecordSetForRpc(prr) Cache_RestoreRecordListForRpc(prr)
  102. #define IsCacheTtlStillValid(prr) Cache_IsRecordTtlValid(prr)
  103. #define GetCacheLock() LOCK_CACHE()
  104. #define ReleaseCacheLock() UNLOCK_CACHE()
  105. //
  106. // Cache entry routines
  107. //
  108. PCACHE_ENTRY
  109. CreateCacheEntry(
  110. IN PWSTR pName,
  111. IN BOOL fLoadingHostsFile
  112. )
  113. /*++
  114. Routine Description:
  115. Create cache entry, including allocation.
  116. Arguments:
  117. pName -- name to create entry for
  118. fLoadingHostsFile -- TRUE if loading from host file; FALSE otherwise
  119. Return Value:
  120. Ptr to newly allocated cache entry.
  121. NULL on error.
  122. --*/
  123. {
  124. ULONG index = 0;
  125. PCACHE_ENTRY pentry = NULL;
  126. DWORD nameLength;
  127. PWCHAR pnameCache = NULL;
  128. DNSDBG( TRACE, (
  129. "CreateCacheEntry( %S, hostfile=%d )\n",
  130. pName,
  131. fLoadingHostsFile ));
  132. if ( !pName || !g_HashTable )
  133. {
  134. return NULL;
  135. }
  136. //
  137. // build cache entry
  138. //
  139. // DCR_PERF: alloc for name within entry?
  140. //
  141. pentry = (PCACHE_ENTRY) CACHE_HEAP_ALLOC_ZERO( sizeof(CACHE_ENTRY) );
  142. if ( !pentry )
  143. {
  144. goto Fail;
  145. }
  146. //
  147. // build the name
  148. //
  149. nameLength = wcslen( pName );
  150. pnameCache = CACHE_HEAP_ALLOC_ZERO( sizeof(WCHAR) * (nameLength + 1) );
  151. if ( !pnameCache )
  152. {
  153. goto Fail;
  154. }
  155. if ( !makeCannonicalCacheName(
  156. pnameCache,
  157. nameLength+1,
  158. pName,
  159. nameLength ) )
  160. {
  161. goto Fail;
  162. }
  163. pentry->pName = pnameCache;
  164. pentry->fHostsFileEntry = fLoadingHostsFile;
  165. //
  166. // insert cache entry into cache -- first entry in bucket
  167. //
  168. index = getHashIndex( pnameCache, 0 );
  169. pentry->pNext = g_HashTable[ index ];
  170. g_HashTable[ index ] = pentry;
  171. g_CacheEntryCount++;
  172. if ( fLoadingHostsFile )
  173. {
  174. ResizeCacheBucket( index, &g_CacheHashTableBucketSize );
  175. }
  176. else
  177. {
  178. //
  179. // Trim the hash bucket to keep the number of entries below
  180. // g_CacheHashTableBucketSize.
  181. //
  182. // DCR: goofy cache limit
  183. // DCR: cache limit should be total size above hosts file
  184. // not individual buckets
  185. // monitor thread should just wake up and and clear dead
  186. // wood from cache
  187. //
  188. TrimCacheBucket( index, g_CacheHashTableBucketSize, TRUE );
  189. }
  190. return pentry;
  191. Fail:
  192. // dump entry
  193. if ( pentry )
  194. {
  195. CACHE_HEAP_FREE( pentry );
  196. }
  197. if ( pnameCache )
  198. {
  199. CACHE_HEAP_FREE( pnameCache );
  200. }
  201. return NULL;
  202. }
  203. //raw
  204. VOID
  205. FreeCacheEntry(
  206. IN OUT PCACHE_ENTRY pEntry
  207. )
  208. /*++
  209. Routine Description:
  210. Free cache entry.
  211. Arguments:
  212. pEntry -- cache entry to free
  213. Globals:
  214. g_CacheEntryCount -- decremented appropriately
  215. g_NumberOfRecordsInCache -- decremented appropriately
  216. Return Value:
  217. None
  218. --*/
  219. {
  220. register INT iter;
  221. DNSDBG( TRACE, (
  222. "FreeCacheEntry( %p )\n",
  223. pEntry ));
  224. //
  225. // free entry
  226. // - name
  227. // - records
  228. // - entry itself
  229. //
  230. // QUESTION: are global counters safe? should lock?
  231. //
  232. if ( pEntry )
  233. {
  234. for ( iter = 0;
  235. iter < BASE_NUMBER_OF_RECORDS;
  236. iter++ )
  237. {
  238. if ( pEntry->Records[iter] )
  239. {
  240. Dns_RecordListFree( pEntry->Records[iter] );
  241. g_CacheRecordSetCount--;
  242. }
  243. }
  244. if ( pEntry->pName )
  245. {
  246. CACHE_HEAP_FREE( pEntry->pName );
  247. }
  248. #if 0
  249. if ( pEntry->pNext )
  250. {
  251. DNSLOG_F1( "FreeCacheEntry is deleting an entry that still points to other entries!" );
  252. }
  253. #endif
  254. CACHE_HEAP_FREE( pEntry );
  255. g_CacheEntryCount--;
  256. }
  257. }
  258. //raw
  259. VOID
  260. AddRecordToCacheEntry(
  261. IN OUT PCACHE_ENTRY pEntry,
  262. IN PDNS_RECORD pRecord,
  263. IN BOOL fFlushExisting,
  264. IN BOOL fLoadingHostsFile
  265. )
  266. {
  267. WORD iter;
  268. WORD type = pRecord->wType;
  269. DNSDBG( TRACE, (
  270. "AddRecordToCacheEntry( e=%p, rr=%p, type=%d )\n",
  271. pEntry,
  272. pRecord,
  273. type ));
  274. //
  275. // don't overwrite host file entries
  276. //
  277. if ( !fLoadingHostsFile &&
  278. pEntry->fHostsFileEntry &&
  279. ( type == DNS_TYPE_A ||
  280. type == DNS_TYPE_PTR ) )
  281. {
  282. return;
  283. }
  284. //
  285. // clean up existing records at node
  286. // - remove stale records
  287. // - remove records of same type UNLESS not flushing existing
  288. //
  289. for ( iter = 0;
  290. iter < BASE_NUMBER_OF_RECORDS;
  291. iter++ )
  292. {
  293. PDNS_RECORD prrExistSet = pEntry->Records[iter];
  294. //
  295. // skip sets of different type that are still valid
  296. //
  297. if ( !prrExistSet ||
  298. ( prrExistSet->wType != type &&
  299. IsCacheTtlStillValid( prrExistSet ) ) )
  300. {
  301. continue;
  302. }
  303. //
  304. // dump
  305. // - stale records
  306. // - records of same type (when flush existing)
  307. //
  308. // note, that previous test means we're here with same
  309. // type record OR record is invalid
  310. //
  311. if ( fFlushExisting ||
  312. pEntry->Records[iter]->wType != type )
  313. {
  314. Dns_RecordListFree( prrExistSet );
  315. pEntry->Records[iter] = NULL;
  316. g_CacheRecordSetCount--;
  317. continue;
  318. }
  319. //
  320. // NOT flushing AND matching type -- host file load case
  321. // => add new record to list
  322. //
  323. // the way this works
  324. // - start at "record" which is addr of record ptr entry
  325. // making pNext field the actual pointer
  326. // - delete duplicates
  327. // - tack new RR on end
  328. // - blow away new RR name if existing record
  329. //
  330. //
  331. // DCR: should have simple "make cache RR set" function that
  332. // handles name and TTL issues
  333. //
  334. // DCR: broken if non-flush load hits wire data; wire data
  335. // may have multiple RR sets
  336. //
  337. else
  338. {
  339. PDNS_RECORD prr;
  340. PDNS_RECORD prrPrev = (PDNS_RECORD) &pEntry->Records[iter];
  341. while ( prr = prrPrev->pNext )
  342. {
  343. // matches existing record?
  344. // - cut existing record from list and free
  345. if ( Dns_RecordCompare( prr, pRecord ) )
  346. {
  347. prrPrev->pNext = prr->pNext;
  348. prr->pNext = NULL;
  349. Dns_RecordListFree( prr );
  350. }
  351. else
  352. {
  353. prrPrev = prr;
  354. }
  355. }
  356. //
  357. // tack entry on to end
  358. // - if existing records of type delete name
  359. //
  360. if ( prrPrev != (PDNS_RECORD)&pEntry->Records[iter] )
  361. {
  362. if ( IS_FREE_OWNER(pRecord) )
  363. {
  364. RECORD_HEAP_FREE( pRecord->pName );
  365. pRecord->pName = NULL;
  366. }
  367. }
  368. prrPrev->pNext = pRecord;
  369. return;
  370. }
  371. }
  372. //
  373. // put record into cache entry
  374. // - note record list may now contain pre-existing records
  375. // from above
  376. //
  377. for ( iter = 0;
  378. iter < BASE_NUMBER_OF_RECORDS;
  379. iter++ )
  380. {
  381. if ( pEntry->Records[iter] == NULL )
  382. {
  383. pEntry->Records[iter] = pRecord;
  384. g_CacheRecordSetCount++;
  385. return;
  386. }
  387. }
  388. //
  389. // must find free spot in list -- get rid of last one
  390. //
  391. // DCR: realloc and push out if need more space
  392. //
  393. Dns_RecordListFree( pEntry->Records[BASE_NUMBER_OF_RECORDS-1] );
  394. pEntry->Records[BASE_NUMBER_OF_RECORDS-1] = pRecord;
  395. return;
  396. }
  397. VOID
  398. FlushCacheEntry(
  399. IN PWSTR pName
  400. )
  401. /*++
  402. Routine Description:
  403. Flush cache entry corresponding to a name.
  404. Arguments:
  405. pName -- name to delete from the cache
  406. Return Value:
  407. ERROR_SUCCESS if successful.
  408. ErrorCode on failure.
  409. --*/
  410. {
  411. ULONG index = 0;
  412. PCACHE_ENTRY pentry = NULL;
  413. PCACHE_ENTRY pprevEntry = NULL;
  414. WCHAR hashName[ DNS_MAX_NAME_BUFFER_LENGTH+4 ];
  415. DNSDBG( TRACE, (
  416. "FlushCacheEntry( %S )\n",
  417. pName ));
  418. if ( !g_HashTable )
  419. {
  420. return;
  421. }
  422. //
  423. // build cache name
  424. //
  425. if ( !makeCannonicalCacheName(
  426. hashName,
  427. DNS_MAX_NAME_BUFFER_LENGTH,
  428. pName,
  429. 0 ) )
  430. {
  431. return;
  432. }
  433. //
  434. // find entry in cache
  435. //
  436. // DCR: consider a find routine that lets us cut
  437. // - return prev pointer
  438. // - or know we're at front of list and have index
  439. //
  440. index = getHashIndex( hashName, 0 );
  441. GetCacheLock();
  442. pentry = g_HashTable[ index ];
  443. while( pentry )
  444. {
  445. if ( DnsNameCompare_W( hashName, pentry->pName ) )
  446. {
  447. //
  448. // Can't purge cache entries that hold records that were
  449. // read in from the hosts file . . .
  450. //
  451. if ( pentry->fHostsFileEntry )
  452. {
  453. goto Done;
  454. }
  455. //
  456. // Found it!
  457. //
  458. if ( pprevEntry )
  459. {
  460. //
  461. // There is an entry in front of the one we found
  462. // in the list.
  463. //
  464. pprevEntry->pNext = pentry->pNext;
  465. }
  466. else
  467. {
  468. //
  469. // There isn't an entry in front of the one we found.
  470. //
  471. g_HashTable[ index ] = pentry->pNext;
  472. }
  473. pentry->pNext = NULL;
  474. FreeCacheEntry( pentry );
  475. goto Done;
  476. }
  477. pprevEntry = pentry;
  478. pentry = pentry->pNext;
  479. }
  480. Done:
  481. ReleaseCacheLock();
  482. }
  483. VOID
  484. FlushCacheEntryRecord(
  485. IN PWSTR pName,
  486. IN WORD Type
  487. )
  488. /*++
  489. Routine Description:
  490. Flush cached records corresponding to a name and type.
  491. Arguments:
  492. pName -- name of records to delete
  493. Type -- type of records to delete
  494. Return Value:
  495. ERROR_SUCCESS if successful.
  496. ErrorCode on failure.
  497. --*/
  498. {
  499. WORD iter;
  500. PCACHE_ENTRY pentry;
  501. DNSDBG( TRACE, (
  502. "FlushCacheEntryRecord( %S, %d )\n",
  503. pName,
  504. Type ));
  505. //
  506. // find entry in cache
  507. //
  508. GetCacheLock();
  509. pentry = FindCacheEntry( pName );
  510. if ( !pentry )
  511. {
  512. goto Done;
  513. }
  514. //
  515. // free records for entry
  516. // - not from hosts file
  517. // - matches type
  518. // - or type zero (for deleting ALL records)
  519. // - or name error
  520. //
  521. for ( iter = 0;
  522. iter < BASE_NUMBER_OF_RECORDS;
  523. iter++ )
  524. {
  525. PDNS_RECORD prr = pentry->Records[iter];
  526. if ( prr &&
  527. ! IS_HOSTS_FILE_RR(prr) &&
  528. ( prr->wType == Type ||
  529. Type == 0 ||
  530. ( prr->wType == DNS_TYPE_ANY &&
  531. prr->wDataLength == 0 ) ) )
  532. {
  533. Dns_RecordListFree( prr );
  534. pentry->Records[iter] = NULL;
  535. g_CacheRecordSetCount--;
  536. }
  537. }
  538. Done:
  539. ReleaseCacheLock();
  540. }
  541. //raw
  542. VOID
  543. TrimCache(
  544. VOID
  545. )
  546. /*++
  547. Routine Description:
  548. Trim back cache. Trim every bucket in cache.
  549. Arguments:
  550. None
  551. Return Value:
  552. None
  553. --*/
  554. {
  555. WORD hashIter;
  556. if ( !g_HashTable )
  557. return;
  558. for ( hashIter = 0;
  559. hashIter < g_CacheHashTableSize;
  560. hashIter++ )
  561. {
  562. TrimCacheBucket( hashIter,
  563. g_CacheHashTableBucketSize,
  564. FALSE );
  565. }
  566. }
  567. //raw
  568. VOID
  569. TrimCacheBucket(
  570. IN ULONG Index,
  571. IN DWORD dwBucketSize,
  572. IN BOOL fSkipFirst
  573. )
  574. /*++
  575. Routine Description:
  576. Trim back cache bucket.
  577. Arguments:
  578. Index -- Index of hash bucket to trim.
  579. dwBucketSize -- size to trim bucket to
  580. fSkipFirst -- skip first entry while triming
  581. Return Value:
  582. None
  583. --*/
  584. {
  585. PCACHE_ENTRY pentry = g_HashTable[ Index ];
  586. PCACHE_ENTRY pprevEntry = NULL;
  587. DWORD EntryCount = 0;
  588. //
  589. // skip first entry in bucket
  590. //
  591. if ( pentry && fSkipFirst )
  592. {
  593. pprevEntry = pentry;
  594. pentry = pentry->pNext;
  595. EntryCount++;
  596. }
  597. while ( pentry )
  598. {
  599. PCACHE_ENTRY pnextEntry = pentry->pNext;
  600. WORD recordCount = 0;
  601. WORD iter;
  602. for ( iter = 0;
  603. iter < BASE_NUMBER_OF_RECORDS;
  604. iter++ )
  605. {
  606. //
  607. // delete stale entries
  608. // - not from host file
  609. // - TTL expired
  610. //
  611. if ( ! pentry->fHostsFileEntry &&
  612. pentry->Records[iter] &&
  613. !IsCacheTtlStillValid( pentry->Records[iter] ) )
  614. {
  615. Dns_RecordListFree( pentry->Records[iter] );
  616. pentry->Records[iter] = NULL;
  617. g_CacheRecordSetCount--;
  618. recordCount++;
  619. }
  620. else if ( !pentry->Records[iter] )
  621. {
  622. recordCount++;
  623. }
  624. }
  625. //
  626. // if entry is empty -- delete
  627. //
  628. if ( recordCount == BASE_NUMBER_OF_RECORDS )
  629. {
  630. if ( pprevEntry )
  631. pprevEntry->pNext = pentry->pNext;
  632. else
  633. g_HashTable[ Index ] = pentry->pNext;
  634. pentry->pNext = NULL;
  635. FreeCacheEntry( pentry );
  636. }
  637. else
  638. {
  639. pprevEntry = pentry;
  640. EntryCount++;
  641. }
  642. pentry = pnextEntry;
  643. }
  644. //
  645. // if still too many entries we need to delete
  646. //
  647. // DCR: cache size limitation is weak
  648. // - ideally time out based on query, for instances pick an interval
  649. // say ten minutes and time out all older stuff
  650. // - or better yet do LRU timeout
  651. //
  652. if ( EntryCount >= dwBucketSize )
  653. {
  654. PCACHE_ENTRY pPrevPurgeEntry = NULL;
  655. PCACHE_ENTRY pPurgeEntry = NULL;
  656. pentry = g_HashTable[ Index ];
  657. pprevEntry = NULL;
  658. //
  659. // skip first when required
  660. //
  661. if ( pentry && fSkipFirst )
  662. {
  663. pprevEntry = pentry;
  664. pentry = pentry->pNext;
  665. }
  666. //
  667. // Loop through all cache entries looking for potential ones
  668. // to get rid of, the last one in the list will be the one that
  669. // is purged (Least Recently Used).
  670. //
  671. while ( pentry )
  672. {
  673. if ( ! pentry->fHostsFileEntry )
  674. {
  675. //
  676. // Found a potential entry to purge
  677. //
  678. pPrevPurgeEntry = pprevEntry;
  679. pPurgeEntry = pentry;
  680. }
  681. pprevEntry = pentry;
  682. pentry = pentry->pNext;
  683. }
  684. //
  685. // Now get rid of the entry that was found
  686. //
  687. // FIXME: how many times are we going to clone this code???
  688. //
  689. if ( pPurgeEntry )
  690. {
  691. WORD iter;
  692. if ( pPrevPurgeEntry )
  693. {
  694. pPrevPurgeEntry->pNext = pPurgeEntry->pNext;
  695. }
  696. else
  697. {
  698. g_HashTable[ Index ] = pPurgeEntry->pNext;
  699. }
  700. pPurgeEntry->pNext = NULL;
  701. FreeCacheEntry( pPurgeEntry );
  702. }
  703. }
  704. }
  705. //raw
  706. VOID
  707. ResizeCacheBucket(
  708. IN ULONG Index,
  709. IN PDWORD pdwBucketSize
  710. )
  711. {
  712. PCACHE_ENTRY pentry = g_HashTable[ Index ];
  713. DWORD Count = 0;
  714. while ( pentry )
  715. {
  716. Count++;
  717. pentry = pentry->pNext;
  718. }
  719. if ( (*pdwBucketSize - Count) < 10 )
  720. {
  721. *pdwBucketSize += 10;
  722. }
  723. }
  724. //raw
  725. PCACHE_ENTRY
  726. FindCacheEntry(
  727. IN PWSTR pName
  728. )
  729. {
  730. ULONG index;
  731. PCACHE_ENTRY pentry;
  732. PCACHE_ENTRY pprevEntry = NULL;
  733. WCHAR hashName[ DNS_MAX_NAME_BUFFER_LENGTH+4 ];
  734. if ( !g_HashTable )
  735. {
  736. return NULL;
  737. }
  738. if ( !pName )
  739. {
  740. DNS_ASSERT( FALSE );
  741. return NULL;
  742. }
  743. DNSDBG( TRACE, (
  744. "FindCacheEntry( %S )\n",
  745. pName ));
  746. //
  747. // build cache name
  748. // - if invalid (too long) bail
  749. //
  750. if ( !makeCannonicalCacheName(
  751. hashName,
  752. DNS_MAX_NAME_BUFFER_LENGTH,
  753. pName,
  754. 0 ) )
  755. {
  756. return NULL;
  757. }
  758. //
  759. // find entry in cache
  760. //
  761. index = getHashIndex( hashName, 0 );
  762. pentry = g_HashTable[ index ];
  763. DNSDBG( TRACE, (
  764. "in FindCacheEntry\n"
  765. "\tname = %S\n"
  766. "\tindex = %d\n"
  767. "\tpentry = %p\n",
  768. hashName,
  769. index,
  770. pentry ));
  771. while( pentry )
  772. {
  773. if ( DnsNameCompare_W( hashName, pentry->pName ) )
  774. {
  775. //
  776. // found entry
  777. // - move to front, if not already there
  778. if ( pprevEntry )
  779. {
  780. pprevEntry->pNext = pentry->pNext;
  781. pentry->pNext = g_HashTable[ index ];
  782. g_HashTable[ index ] = pentry;
  783. }
  784. break;
  785. }
  786. ELSE
  787. {
  788. DNSDBG( OFF, (
  789. "in FindCacheEntry -- failed name compare\n"
  790. "\tout name = %S\n"
  791. "\tpentry = %p\n"
  792. "\tname = %S\n",
  793. hashName,
  794. pentry,
  795. pentry->pName ));
  796. }
  797. pprevEntry = pentry;
  798. pentry = pentry->pNext;
  799. }
  800. DNSDBG( TRACE, (
  801. "Leave FindCacheEntry\n"
  802. "\tname = %S\n"
  803. "\tindex = %d\n"
  804. "\tpentry = %p\n",
  805. hashName,
  806. index,
  807. pentry ));
  808. return pentry;
  809. }
  810. PCACHE_ENTRY
  811. FindOrCreateCacheEntry(
  812. IN PWSTR pName,
  813. IN BOOL fHostsFile
  814. )
  815. /*++
  816. Routine Description:
  817. Find or create entry for name in cache.
  818. Arguments:
  819. pName -- name to find
  820. Return Value:
  821. Ptr to cache entry -- if successful.
  822. NULL on failure.
  823. --*/
  824. {
  825. PCACHE_ENTRY pentry;
  826. DNSDBG( TRACE, (
  827. "FindOrCreateCacheEntry( %S, hosts=%d )\n",
  828. pName,
  829. fHostsFile ));
  830. //
  831. // find entry?
  832. //
  833. pentry = FindCacheEntry( pName );
  834. if ( !pentry )
  835. {
  836. pentry = CreateCacheEntry(
  837. pName,
  838. fHostsFile
  839. );
  840. }
  841. DNSDBG( TRACE, (
  842. "Leave FindOrCreateCacheEntry( %S, hosts=%d ) => %p\n",
  843. pName,
  844. fHostsFile,
  845. pentry ));
  846. return pentry;
  847. }
  848. PDNS_RECORD
  849. FindCacheEntryRecord(
  850. IN PCACHE_ENTRY pEntry,
  851. IN WORD Type
  852. )
  853. /*++
  854. Routine Description:
  855. Find entry in cache.
  856. Arguments:
  857. pEntry -- cache entry to check
  858. Type -- record type to find
  859. Return Value:
  860. Ptr to record set of desired type -- if found.
  861. NULL if not found.
  862. --*/
  863. {
  864. WORD iter;
  865. PDNS_RECORD prr;
  866. DNSDBG( TRACE, (
  867. "FindCacheEntryRecord( %p, type=%d )\n",
  868. pEntry,
  869. Type ));
  870. //
  871. // check all the records at the cache entry
  872. //
  873. for ( iter = 0;
  874. iter < BASE_NUMBER_OF_RECORDS;
  875. iter++ )
  876. {
  877. prr = pEntry->Records[iter];
  878. if ( prr &&
  879. ( prr->wType == Type ||
  880. prr->wType == DNS_TYPE_CNAME ||
  881. ( prr->wType == DNS_TYPE_ANY &&
  882. prr->wDataLength == 0 ) ) )
  883. {
  884. //
  885. // if expired dump RR list
  886. //
  887. // DCR: if different RR sets in list, then TTL check here not sufficient
  888. //
  889. // DCR: functionalize this
  890. //
  891. if ( !IsCacheTtlStillValid( prr ) ||
  892. IsInvalidNegativeCacheEntry( prr ) )
  893. {
  894. DNSDBG( TRACE, (
  895. "Whacking timed out record %p at cache entry %p\n",
  896. prr,
  897. pEntry ));
  898. Dns_RecordListFree( prr );
  899. pEntry->Records[iter] = NULL;
  900. g_CacheRecordSetCount--;
  901. prr = NULL;
  902. goto Done;
  903. }
  904. //
  905. // If the cached record is a CNAME, walk the record
  906. // list to see if the CNAME chain points to a record
  907. // that is the type we are looking for.
  908. //
  909. if ( prr->wType == DNS_TYPE_CNAME &&
  910. Type != DNS_TYPE_CNAME )
  911. {
  912. PDNS_RECORD prrChain = prr->pNext;
  913. while ( prrChain )
  914. {
  915. if ( prrChain->wType == Type )
  916. {
  917. // chain to desired type -- take RR set
  918. goto Done;
  919. }
  920. prrChain = prrChain->pNext;
  921. }
  922. prr = NULL;
  923. goto Done;
  924. }
  925. // take RR set
  926. goto Done;
  927. }
  928. }
  929. // type not found
  930. prr = NULL;
  931. Done:
  932. DNSDBG( TRACE, (
  933. "Leave FindCacheEntryRecord() => %p\n",
  934. prr ));
  935. return prr;
  936. }
  937. PDNS_RECORD
  938. FindCacheRecords(
  939. IN PWSTR pwsName,
  940. IN WORD wType
  941. )
  942. /*++
  943. Routine Description:
  944. Find records of given name and type in cache.
  945. Arguments:
  946. pwsName -- name
  947. Type -- record type to find
  948. Return Value:
  949. Ptr to record set of desired type -- if found.
  950. NULL if not found.
  951. --*/
  952. {
  953. PCACHE_ENTRY pentry;
  954. PDNS_RECORD prr = NULL;
  955. DNSDBG( TRACE, (
  956. "FindCacheRecords( %S, type=%d )\n",
  957. pwsName,
  958. wType ));
  959. pentry = FindCacheEntry( pwsName );
  960. if ( pentry )
  961. {
  962. prr = FindCacheEntryRecord(
  963. pentry,
  964. wType );
  965. }
  966. DNSDBG( TRACE, (
  967. "Leave FindCacheRecords( %S, type=%d ) => %p\n",
  968. pwsName,
  969. wType,
  970. prr ));
  971. return prr;
  972. }
  973. //raw
  974. VOID
  975. CacheAnyAdditionalRecords(
  976. IN OUT PDNS_RECORD pRecord,
  977. IN BOOL fHostFile
  978. )
  979. {
  980. BOOL fcnameAnswer = FALSE;
  981. PCACHE_ENTRY pentry = NULL;
  982. PDNS_RECORD pnextRR = pRecord;
  983. PDNS_RECORD prr;
  984. DNSDBG( TRACE, (
  985. "CacheAnyAdditionalRecords( rr=%p, hosts=%d )\n",
  986. pRecord,
  987. fHostFile ));
  988. //
  989. // cache "additional" records
  990. //
  991. // this is really cache additional OR
  992. // cache the answer records for a CNAME at that CNAME
  993. //
  994. // background: Glenn's caching paradigm was to cache all answer
  995. // data at the queried name in the API call (name might be short).
  996. // However, not caching the CNAME data can cause problems, so this
  997. // was tacked on.
  998. //
  999. // For CNAME caching we throw away the CNAMEs themselves and just
  1000. // cache the actually data (address) records at the CNAME node.
  1001. //
  1002. //
  1003. // cache additional records
  1004. //
  1005. while ( prr = pnextRR )
  1006. {
  1007. BOOL fcacheSet = FALSE;
  1008. pnextRR = Dns_RecordSetDetach( prr );
  1009. //
  1010. // do NOT cache
  1011. // - answer records for queried name (not CNAME)
  1012. // - CNAME records when doing caching of answer data under CNAME
  1013. // - authority section records (NS, SOA, etc)
  1014. // - OPT records
  1015. //
  1016. // DCR: have some sort of "cacheable type" test
  1017. // which would screen out any transactional records
  1018. //
  1019. if ( fHostFile )
  1020. {
  1021. fcacheSet = TRUE;
  1022. }
  1023. else if ( prr->Flags.S.Section == DNSREC_ANSWER )
  1024. {
  1025. if ( prr->wType == DNS_TYPE_CNAME )
  1026. {
  1027. fcnameAnswer = TRUE;
  1028. }
  1029. else if ( fcnameAnswer )
  1030. {
  1031. fcacheSet = TRUE;
  1032. }
  1033. }
  1034. else if ( prr->Flags.S.Section == DNSREC_ADDITIONAL )
  1035. {
  1036. if ( prr->wType != DNS_TYPE_OPT )
  1037. {
  1038. fcacheSet = TRUE;
  1039. }
  1040. }
  1041. if ( !fcacheSet )
  1042. {
  1043. Dns_RecordListFree( prr );
  1044. continue;
  1045. }
  1046. //
  1047. // cache the set
  1048. //
  1049. // flip the section field to "Answer" section
  1050. //
  1051. // DCR: section caching?
  1052. //
  1053. // note: section fields in cache indicate whether
  1054. // answer data (or additional) once out of
  1055. // cache;
  1056. // this is necessary since we cache everything
  1057. // at node and return it in one RR list; we'd
  1058. // to change must
  1059. // - return in different lists with some indication
  1060. // in cache of what's what
  1061. // OR
  1062. // - another indication of what's what
  1063. //
  1064. pentry = FindOrCreateCacheEntry(
  1065. prr->pName,
  1066. fHostFile
  1067. );
  1068. if ( !pentry )
  1069. {
  1070. Dns_RecordListFree( prr );
  1071. }
  1072. else
  1073. {
  1074. //if ( !fHostFile )
  1075. // currently HostFile entries get answer too
  1076. {
  1077. PDNS_RECORD ptemp = prr;
  1078. while ( ptemp )
  1079. {
  1080. ptemp->Flags.S.Section = DNSREC_ANSWER;
  1081. ptemp = ptemp->pNext;
  1082. }
  1083. }
  1084. PrepareRecordSetForCache( prr );
  1085. AddRecordToCacheEntry(
  1086. pentry,
  1087. prr,
  1088. ! fHostFile, // flush if NOT hostfile load
  1089. fHostFile
  1090. );
  1091. }
  1092. }
  1093. DNSDBG( TRACE, ( "Leave CacheAnyAdditionalRecords()\n" ));
  1094. }
  1095. //raw
  1096. BOOL
  1097. IsInvalidNegativeCacheEntry(
  1098. IN PDNS_RECORD pRecord
  1099. )
  1100. {
  1101. DWORD cacheTime;
  1102. if ( pRecord->wDataLength != 0 )
  1103. {
  1104. return FALSE;
  1105. }
  1106. //
  1107. // recover record cache time
  1108. //
  1109. cacheTime = g_MaxNegativeCacheTtl;
  1110. if ( pRecord->wType == DNS_TYPE_SOA )
  1111. {
  1112. cacheTime = g_NegativeSOACacheTime;
  1113. }
  1114. // should NEVER have absolute time less than time we cached for
  1115. ASSERT( cacheTime <= pRecord->dwTtl );
  1116. //
  1117. // check if last PnP AFTER this record was cached
  1118. //
  1119. DNSDBG( TRACE, (
  1120. "IsInvalidNegativeCacheEntry( rr=%p ) => %d\n",
  1121. pRecord,
  1122. ( g_TimeOfLastPnPUpdate > (pRecord->dwTtl - cacheTime))
  1123. ));
  1124. return( g_TimeOfLastPnPUpdate > (pRecord->dwTtl - cacheTime) );
  1125. }
  1126. //
  1127. // End cache.c
  1128. //