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.

3215 lines
68 KiB

  1. /*++
  2. Copyright (c) 2000-2001 Microsoft Corporation
  3. Module Name:
  4. ncache.c
  5. Abstract:
  6. DNS Resolver Service
  7. Cache routines
  8. Author:
  9. Jim Gilroy (jamesg) April 2001
  10. Revision History:
  11. --*/
  12. #include "local.h"
  13. //
  14. // Heap corruption tracking
  15. //
  16. #define HEAPPROB 1
  17. #define BAD_PTR (PVOID)(-1)
  18. //
  19. // Cache entry definitions
  20. //
  21. // Starting cache record count
  22. //
  23. #define CACHE_DEFAULT_SET_COUNT 3
  24. #if 0
  25. // Should be private but is exposed in remote
  26. // cache enum routines.
  27. typedef struct _CacheEntry
  28. {
  29. struct _CacheEntry * pNext;
  30. PWSTR pName;
  31. DWORD Reserved;
  32. DWORD MaxCount;
  33. PDNS_RECORD Records[ 1 ];
  34. }
  35. CACHE_ENTRY, *PCACHE_ENTRY;
  36. #endif
  37. //
  38. // Cache heap
  39. //
  40. HANDLE g_CacheHeap = NULL;
  41. //
  42. // Cache hash table
  43. //
  44. PCACHE_ENTRY * g_HashTable = NULL;
  45. #define INITIAL_CACHE_HEAP_SIZE (16*1024)
  46. //
  47. // Runtime globals
  48. //
  49. DWORD g_CurrentCacheTime;
  50. DWORD g_RecordSetCount;
  51. DWORD g_RecordSetCountLimit;
  52. DWORD g_RecordSetCountThreshold;
  53. DWORD g_RecordSetCache;
  54. DWORD g_RecordSetFree;
  55. DWORD g_EntryCount;
  56. DWORD g_EntryAlloc;
  57. DWORD g_EntryFree;
  58. BOOL g_fLoadingHostsFile;
  59. //
  60. // Garbage collection
  61. //
  62. BOOL g_GarbageCollectFlag = FALSE;
  63. DWORD g_NextGarbageIndex = 0;
  64. DWORD g_NextGarbageTime = 0;
  65. #define GARBAGE_LOCKOUT_INTERVAL (600) // no more then every ten minutes
  66. //
  67. // Wakeup flag
  68. //
  69. BOOL g_WakeFlag = FALSE;
  70. //
  71. // Cache limits
  72. // - min count of records to hold
  73. // - size of band in which garbage collection occurs
  74. //
  75. #if DBG
  76. #define MIN_DYNAMIC_RECORD_COUNT (20)
  77. #define CLEANUP_RECORD_COUNT_BAND (5)
  78. #else
  79. #define MIN_DYNAMIC_RECORD_COUNT (50)
  80. #define CLEANUP_RECORD_COUNT_BAND (30)
  81. #endif
  82. //
  83. // Static records (hosts file)
  84. //
  85. #define IS_STATIC_RR(prr) (IS_HOSTS_FILE_RR(prr) || IS_CLUSTER_RR(prr))
  86. //
  87. // Compute a hash table index value for a string
  88. //
  89. #define EOS (L'\0')
  90. #define COMPUTE_STRING_HASH_1( _String, _ulHashTableSize, _lpulHash ) \
  91. { \
  92. PWCHAR p; \
  93. ULOND h = 0, g; \
  94. \
  95. for ( p = _String; *p != EOS; p = p + 1 ) \
  96. { \
  97. h = ( h << 4 ) + (DWORD) (*p); \
  98. if ( g = h&0xf0000000 ) \
  99. { \
  100. h = h ^ ( g >> 24 ); \
  101. h = h ^ g; \
  102. } \
  103. } \
  104. *_lpulHash = h % _ulHashTableSize; \
  105. }
  106. //
  107. // Compute a hash table index value for a string
  108. // which is invairant to case
  109. //
  110. #define COMPUTE_STRING_HASH_2( _String, _ulHashTableSize, _lpulHash ) \
  111. { \
  112. PWCHAR _p = _String; \
  113. PWCHAR _ep = _p + wcslen( _String ); \
  114. ULONG h = 0; \
  115. \
  116. while( _p < _ep ) \
  117. { \
  118. h <<= 1; \
  119. h ^= *_p++; \
  120. } \
  121. \
  122. *_lpulHash = h % _ulHashTableSize; \
  123. }
  124. //
  125. // Private prototypes
  126. //
  127. BOOL
  128. Cache_FlushEntryRecords(
  129. IN OUT PCACHE_ENTRY pEntry,
  130. IN DWORD Level,
  131. IN WORD wType
  132. );
  133. VOID
  134. Cache_FlushBucket(
  135. IN ULONG Index,
  136. IN WORD FlushLevel
  137. );
  138. //
  139. // Cache Implementation
  140. //
  141. // Cache is implemented as a hash on name, with chaining in the individual
  142. // buckets. Individual name entries are blocks with name pointer and array
  143. // of up to 3 RR set pointers. The new names\entries are put at the front of
  144. // the bucket chain, so the oldest are at the rear.
  145. //
  146. //
  147. // Cleanup:
  148. //
  149. // The cleanup strategy is to time out all RR sets and cleanup everything
  150. // possible as a result. Then entries beyond a max bucket size (a resizable
  151. // global) are deleted, the oldest queries deleted first.
  152. //
  153. // Ideally, we'd like to keep the most useful entries in the cache while
  154. // being able to limit the overall cache size.
  155. //
  156. // A few observations:
  157. //
  158. // 1) max bucket size is worthless; if sufficient for pruning, it would be
  159. // too small to allow non-uniform distributions
  160. //
  161. // 2) LRU should be required; on busy cache shouldn't prune something queried
  162. // "a while" ago that is being used all the time; that adds much more traffic
  163. // than something recently queried but then unused;
  164. //
  165. // 3) if necessary an LRU index could be kept; but probably some time bucket
  166. // counting to know how "deep" pruning must be is adequate
  167. //
  168. //
  169. // Memory:
  170. //
  171. // Currently hash itself and hash entries come from private resolver heap.
  172. // However, RR sets are built by record parsing of messages received in dnsapi.dll
  173. // and hence are built by the default dnsapi.dll allocator. We must match it.
  174. //
  175. // The downside of this is twofold:
  176. // 1) By being in process heap, we are exposed (debug wise) to any poor code
  177. // in services.exe. Hopefully, there are getting better, but anything that
  178. // trashes memory is likely to cause us to have to debug, because we are the highest
  179. // use service.
  180. // 2) Flush \ cleanup is easy. Just kill the heap.
  181. //
  182. // There are several choices:
  183. //
  184. // 0) Copy the records. We are still susceptible to memory corruption ... but the
  185. // interval is shorter, since we don't keep anything in process heap.
  186. //
  187. // 1) Query can directly call dnslib.lib query routines. Since dnslib.lib is
  188. // explicitly compiled in, it's global for holding the allocators is modules, rather
  189. // than process specific.
  190. //
  191. // 2) Add some parameter to query routines that allows pass of allocator down to
  192. // lowest level. At high level this is straightforward. At lower level it maybe
  193. // problematic. There may be a way to do it with a flag where the allocator is
  194. // "optional" and used only when a flag is set.
  195. //
  196. //
  197. // Cache functions
  198. //
  199. DNS_STATUS
  200. Cache_Lock(
  201. IN BOOL fNoStart
  202. )
  203. /*++
  204. Routine Description:
  205. Lock the cache
  206. Arguments:
  207. None.
  208. Return Value:
  209. NO_ERROR if successful -- cache is locked.
  210. ErrorCode on init if cache init failed.
  211. --*/
  212. {
  213. DNSDBG( LOCK, ( "Enter Cache_Lock() ..." ));
  214. EnterCriticalSection( &CacheCS );
  215. DNSDBG( LOCK, (
  216. "through lock (r=%d)\n",
  217. CacheCS.RecursionCount ));
  218. // update global time (for TTL set and timeout)
  219. //
  220. // this allows us to eliminate multiple time calls
  221. // within cache
  222. g_CurrentCacheTime = Dns_GetCurrentTimeInSeconds();
  223. //
  224. // if cache not loaded -- load
  225. // this allows us to avoid load on every PnP until we
  226. // are actually queried
  227. //
  228. if ( !fNoStart && !g_HashTable )
  229. {
  230. DNS_STATUS status;
  231. DNSDBG( ANY, (
  232. "No hash table when took lock -- initializing!\n" ));
  233. status = Cache_Initialize();
  234. if ( status != NO_ERROR )
  235. {
  236. Cache_Unlock();
  237. return status;
  238. }
  239. }
  240. return NO_ERROR;
  241. }
  242. VOID
  243. Cache_Unlock(
  244. VOID
  245. )
  246. /*++
  247. Routine Description:
  248. Unlock the cache
  249. Arguments:
  250. None.
  251. Return Value:
  252. None.
  253. --*/
  254. {
  255. DNSDBG( LOCK, (
  256. "Cache_Unlock() r=%d\n",
  257. CacheCS.RecursionCount ));
  258. LeaveCriticalSection( &CacheCS );
  259. }
  260. DNS_STATUS
  261. Cache_Initialize(
  262. VOID
  263. )
  264. /*++
  265. Routine Description:
  266. Initialize the cache.
  267. Create events and locks and setup basic hash.
  268. Arguments:
  269. None.
  270. Return Value:
  271. ERROR_SUCCESS if successful.
  272. ErrorCode on failure.
  273. --*/
  274. {
  275. DNS_STATUS status;
  276. DWORD carryCount;
  277. DNSDBG( INIT, ( "Cache_Initialize()\n" ));
  278. //
  279. // lock -- with "no-start" set to avoid recursion
  280. //
  281. LOCK_CACHE_NO_START();
  282. //
  283. // create cache heap
  284. //
  285. // want to have own heap
  286. // 1) to simplify flush\shutdown
  287. // 2) keep us from "entanglements" with poor services
  288. //
  289. g_CacheHeap = HeapCreate( 0, INITIAL_CACHE_HEAP_SIZE, 0 );
  290. if ( !g_CacheHeap )
  291. {
  292. status = ERROR_NOT_ENOUGH_MEMORY;
  293. g_HashTable = NULL;
  294. goto Done;
  295. }
  296. g_HashTable = CACHE_HEAP_ALLOC_ZERO(
  297. sizeof(PCACHE_ENTRY) * g_HashTableSize );
  298. if ( !g_HashTable )
  299. {
  300. status = ERROR_NOT_ENOUGH_MEMORY;
  301. HeapDestroy( g_CacheHeap );
  302. g_CacheHeap = NULL;
  303. goto Done;
  304. }
  305. g_WakeFlag = FALSE;
  306. g_EntryCount = 0;
  307. g_EntryAlloc = 0;
  308. g_EntryFree = 0;
  309. g_RecordSetCount = 0;
  310. g_RecordSetCache = 0;
  311. g_RecordSetFree = 0;
  312. // eliminate cache size checks during hosts file load
  313. g_RecordSetCountLimit = MAXDWORD;
  314. g_RecordSetCountThreshold = MAXDWORD;
  315. //
  316. // load hosts file into cache
  317. //
  318. g_fLoadingHostsFile = TRUE;
  319. InitCacheWithHostFile();
  320. g_fLoadingHostsFile = FALSE;
  321. //
  322. // set cache size limit
  323. // - above what loaded from hosts file
  324. // - always allow some dynamic space regardless of
  325. // g_MaxCacheSize
  326. // - create slightly higher threshold value for kicking
  327. // off cleanup so cleanup not running all the time
  328. //
  329. carryCount = g_MaxCacheSize;
  330. if ( carryCount < MIN_DYNAMIC_RECORD_COUNT )
  331. {
  332. carryCount = MIN_DYNAMIC_RECORD_COUNT;
  333. }
  334. g_RecordSetCountLimit = g_RecordSetCount + carryCount;
  335. g_RecordSetCountThreshold = g_RecordSetCountLimit + CLEANUP_RECORD_COUNT_BAND;
  336. status = NO_ERROR;
  337. Done:
  338. UNLOCK_CACHE();
  339. return status;
  340. }
  341. DNS_STATUS
  342. Cache_Shutdown(
  343. VOID
  344. )
  345. /*++
  346. Routine Description:
  347. Shutdown the cache.
  348. Arguments:
  349. None.
  350. Return Value:
  351. ERROR_SUCCESS if successful.
  352. ErrorCode on failure.
  353. --*/
  354. {
  355. DNSDBG( INIT, ( "Cache_Shutdown()\n" ));
  356. //
  357. // clean out cache and delete cache heap
  358. // - currently Cache_Flush() does just this
  359. //
  360. return Cache_Flush();
  361. }
  362. DNS_STATUS
  363. Cache_Flush(
  364. VOID
  365. )
  366. /*++
  367. Routine Description:
  368. Flush the cache.
  369. This flushes all the cache data and rereads host file but does NOT
  370. shut down and restart cache threads (host file monitor or multicast).
  371. Arguments:
  372. None
  373. Return Value:
  374. ERROR_SUCCESS if successful.
  375. ErrorCode on rebuild failure.
  376. --*/
  377. {
  378. DWORD status = ERROR_SUCCESS;
  379. WORD ihash;
  380. WORD RecordIter;
  381. DNSDBG( ANY, ( "\nCache_Flush()\n" ));
  382. //
  383. // wake\stop garbage collection
  384. //
  385. g_WakeFlag = TRUE;
  386. //
  387. // lock with "no start" flag
  388. // - avoids creating cache structs if they don't exist
  389. //
  390. LOCK_CACHE_NO_START();
  391. DNSLOG_F1( "Flushing DNS Cache" );
  392. DNSLOG_F3(
  393. " Before Cache_Flush(): entries %d, record %d",
  394. g_EntryCount,
  395. g_RecordSetCount );
  396. //
  397. // clear entries in each hash bucket
  398. //
  399. if ( g_HashTable )
  400. {
  401. for ( ihash = 0;
  402. ihash < g_HashTableSize;
  403. ihash++ )
  404. {
  405. Cache_FlushBucket(
  406. ihash,
  407. FLUSH_LEVEL_CLEANUP );
  408. }
  409. }
  410. DNSDBG( CACHE, (
  411. "After flushing cache:\n"
  412. "\trecord count = %d\n"
  413. "\tentry count = %d\n",
  414. g_RecordSetCount,
  415. g_EntryCount ));
  416. DNSLOG_F3(
  417. " After Cache_Flush() flush: entries %d, record %d",
  418. g_EntryCount,
  419. g_RecordSetCount );
  420. //DNS_ASSERT( g_RecordSetCount == 0 );
  421. //DNS_ASSERT( g_EntryCount == 0 );
  422. g_RecordSetCount = 0;
  423. g_EntryCount = 0;
  424. //
  425. // Note: can NOT delete the cache without stopping mcast
  426. // thread which currently uses cache heap
  427. //
  428. // DCR: have all data in cache in single heap
  429. // - protected
  430. // - single destroy cleans up
  431. // once cleaned up, delete heap
  432. if ( g_CacheHeap )
  433. {
  434. HeapDestroy( g_CacheHeap );
  435. g_CacheHeap = NULL;
  436. }
  437. g_HashTable = NULL;
  438. //
  439. // dump local IP list
  440. // - not dumping on shutdown as the IP cleanup happens
  441. // first and takes away the CS;
  442. //
  443. // note to reviewer:
  444. // this is equivalent to the previous behavior where
  445. // Cache_Flush() FALSE was shutdown and
  446. // everything else used TRUE (for restart) which did a
  447. // RefreshLocalAddrArray() to rebuild IP list
  448. // now we simply dump the IP list rather than rebuilding
  449. //
  450. if ( !g_StopFlag )
  451. {
  452. // FIX6: no longer keep separate addr array separate from netinfo
  453. //ClearLocalAddrArray();
  454. }
  455. DNSDBG( ANY, ( "Leave Cache_Flush()\n\n" ));
  456. UNLOCK_CACHE();
  457. return( status );
  458. }
  459. //
  460. // Cache utilities
  461. //
  462. BOOL
  463. Cache_IsRecordTtlValid(
  464. IN PDNS_RECORD pRecord
  465. )
  466. /*++
  467. Routine Description:
  468. Check if TTL is still valid (or has timed out).
  469. Arguments:
  470. pRecord -- record to check
  471. Return Value:
  472. TRUE -- if TTL is still valid
  473. FALSE -- if TTL has timed out
  474. --*/
  475. {
  476. //
  477. // static or TTL not timed out => valid
  478. //
  479. // note: currently flushing all records on PnP, but this is
  480. // not strickly necessary; if stop this then MUST change
  481. // this to whack negative cache entries that are older
  482. // than last PnP time
  483. //
  484. if ( IS_STATIC_RR(pRecord) )
  485. {
  486. return( TRUE );
  487. }
  488. else
  489. {
  490. return( (LONG)(pRecord->dwTtl - g_CurrentCacheTime) > 0 );
  491. }
  492. }
  493. //
  494. // Cache entry routines
  495. //
  496. DWORD
  497. getHashIndex(
  498. IN PWSTR pName,
  499. IN DWORD NameLength OPTIONAL
  500. )
  501. /*++
  502. Routine Description:
  503. Create cannonical cache form of name.
  504. Note: no test for adequacy of buffer is done.
  505. Arguments:
  506. pName -- name
  507. NameLength -- NameLength, OPTIONAL
  508. Return Value:
  509. None
  510. --*/
  511. {
  512. register PWCHAR pstring;
  513. register WCHAR wch;
  514. register DWORD hash = 0;
  515. //
  516. // build hash by XORing characters
  517. //
  518. pstring = pName;
  519. while ( wch = *pstring++ )
  520. {
  521. hash <<= 1;
  522. hash ^= wch;
  523. }
  524. //
  525. // mod over hash table size
  526. //
  527. return( hash % g_HashTableSize );
  528. }
  529. BOOL
  530. makeCannonicalCacheName(
  531. OUT PWCHAR pNameBuffer,
  532. IN DWORD BufferLength,
  533. IN PWSTR pName,
  534. IN DWORD NameLength OPTIONAL
  535. )
  536. /*++
  537. Routine Description:
  538. Create cannonical cache form of name.
  539. Arguments:
  540. pNameBuffer -- buffer to hold cache name
  541. BufferLength -- length of buffer
  542. pName -- ptr to name string
  543. NameLength -- optional, saves wsclen() call if known
  544. Return Value:
  545. TRUE if successful.
  546. FALSE on bogus name.
  547. --*/
  548. {
  549. INT count;
  550. DNSDBG( TRACE, (
  551. "makeCannonicalCacheName( %S )\n",
  552. pName ));
  553. //
  554. // get length if not specified
  555. //
  556. if ( NameLength == 0 )
  557. {
  558. NameLength = wcslen( pName );
  559. }
  560. //
  561. // copy and downcase string
  562. // - "empty" buffer for prefix happiness
  563. //
  564. *pNameBuffer = (WCHAR) 0;
  565. count = Dns_MakeCanonicalNameW(
  566. pNameBuffer,
  567. BufferLength,
  568. pName,
  569. NameLength+1 // convert null terminator
  570. );
  571. if ( count == 0 )
  572. {
  573. ASSERT( GetLastError() == ERROR_INSUFFICIENT_BUFFER );
  574. return( FALSE );
  575. }
  576. ASSERT( count == (INT)NameLength+1 );
  577. //
  578. // whack any trailing dot
  579. // - except for root node
  580. //
  581. count--; // account for null terminator
  582. DNS_ASSERT( count == NameLength );
  583. if ( count > 1 &&
  584. pNameBuffer[count - 1] == L'.' )
  585. {
  586. pNameBuffer[count - 1] = 0;
  587. }
  588. return( TRUE );
  589. }
  590. PCACHE_ENTRY
  591. Cache_CreateEntry(
  592. IN PWSTR pName,
  593. IN BOOL fCanonical
  594. )
  595. /*++
  596. Routine Description:
  597. Create cache entry, including allocation.
  598. Arguments:
  599. pName -- name
  600. fCanonical -- TRUE if name already in cannonical form
  601. Return Value:
  602. Ptr to newly allocated cache entry.
  603. NULL on error.
  604. --*/
  605. {
  606. ULONG index = 0;
  607. PCACHE_ENTRY pentry = NULL;
  608. DWORD nameLength;
  609. DWORD fixedLength;
  610. PWCHAR pnameCache = NULL;
  611. DNSDBG( TRACE, (
  612. "Cache_CreateEntry( %S )\n",
  613. pName ));
  614. if ( !pName || !g_HashTable )
  615. {
  616. return NULL;
  617. }
  618. //
  619. // alloc
  620. //
  621. nameLength = wcslen( pName );
  622. fixedLength = sizeof(CACHE_ENTRY) +
  623. (sizeof(PDNS_RECORD) * (CACHE_DEFAULT_SET_COUNT-1));
  624. pentry = (PCACHE_ENTRY) CACHE_HEAP_ALLOC_ZERO(
  625. fixedLength +
  626. sizeof(WCHAR) * (nameLength+1) );
  627. if ( !pentry )
  628. {
  629. goto Fail;
  630. }
  631. pentry->MaxCount = CACHE_DEFAULT_SET_COUNT;
  632. pnameCache = (PWSTR) ((PBYTE)pentry + fixedLength);
  633. //
  634. // build the name
  635. //
  636. if ( fCanonical )
  637. {
  638. wcscpy( pnameCache, pName );
  639. }
  640. else
  641. {
  642. if ( !makeCannonicalCacheName(
  643. pnameCache,
  644. nameLength+1,
  645. pName,
  646. nameLength ) )
  647. {
  648. goto Fail;
  649. }
  650. }
  651. pentry->pName = pnameCache;
  652. //
  653. // insert cache entry into cache -- first entry in bucket
  654. //
  655. index = getHashIndex( pnameCache, nameLength );
  656. pentry->pNext = g_HashTable[ index ];
  657. g_HashTable[ index ] = pentry;
  658. g_EntryCount++;
  659. g_EntryAlloc++;
  660. //
  661. // DCR: need overload detection
  662. //
  663. return pentry;
  664. Fail:
  665. // dump entry
  666. if ( pentry )
  667. {
  668. CACHE_HEAP_FREE( pentry );
  669. }
  670. return NULL;
  671. }
  672. VOID
  673. Cache_FreeEntry(
  674. IN OUT PCACHE_ENTRY pEntry
  675. )
  676. /*++
  677. Routine Description:
  678. Free cache entry.
  679. Arguments:
  680. pEntry -- cache entry to free
  681. Globals:
  682. g_EntryCount -- decremented appropriately
  683. g_NumberOfRecordsInCache -- decremented appropriately
  684. Return Value:
  685. None
  686. --*/
  687. {
  688. INT iter;
  689. DNSDBG( TRACE, (
  690. "Cache_FreeEntry( %p )\n",
  691. pEntry ));
  692. //
  693. // free entry
  694. // - records
  695. // - name
  696. // - entry itself
  697. //
  698. if ( pEntry )
  699. {
  700. Cache_FlushEntryRecords(
  701. pEntry,
  702. FLUSH_LEVEL_CLEANUP,
  703. 0 );
  704. #if 0
  705. if ( pEntry->pNext )
  706. {
  707. DNSLOG_F1( "Cache_FreeEntry is deleting an entry that still points to other entries!" );
  708. }
  709. #endif
  710. #if HEAPPROB
  711. pEntry->pNext = DNS_BAD_PTR;
  712. #endif
  713. CACHE_HEAP_FREE( pEntry );
  714. g_EntryFree--;
  715. g_EntryCount--;
  716. }
  717. }
  718. PCACHE_ENTRY
  719. Cache_FindEntry(
  720. IN PWSTR pName,
  721. IN BOOL fCreate
  722. )
  723. /*++
  724. Routine Description:
  725. Find or create entry for name in cache.
  726. Arguments:
  727. pName -- name to find
  728. fCreate -- TRUE to create if not found
  729. Return Value:
  730. Ptr to cache entry -- if successful.
  731. NULL on failure.
  732. --*/
  733. {
  734. ULONG index;
  735. PCACHE_ENTRY pentry;
  736. PCACHE_ENTRY pprevEntry = NULL;
  737. WCHAR hashName[ DNS_MAX_NAME_BUFFER_LENGTH+4 ];
  738. if ( !g_HashTable )
  739. {
  740. return NULL;
  741. }
  742. if ( !pName )
  743. {
  744. DNS_ASSERT( FALSE );
  745. return NULL;
  746. }
  747. DNSDBG( TRACE, (
  748. "Cache_FindEntry( %S, create=%d )\n",
  749. pName,
  750. fCreate ));
  751. //
  752. // build cache name
  753. // - if invalid (too long) bail
  754. //
  755. if ( !makeCannonicalCacheName(
  756. hashName,
  757. DNS_MAX_NAME_BUFFER_LENGTH,
  758. pName,
  759. 0 ) )
  760. {
  761. return NULL;
  762. }
  763. //
  764. // find entry in cache
  765. //
  766. if ( LOCK_CACHE() != NO_ERROR )
  767. {
  768. return NULL;
  769. }
  770. index = getHashIndex( hashName, 0 );
  771. pentry = g_HashTable[ index ];
  772. DNSDBG( OFF, (
  773. "in Cache_FindEntry\n"
  774. "\tname = %S\n"
  775. "\tindex = %d\n"
  776. "\tpentry = %p\n",
  777. hashName,
  778. index,
  779. pentry ));
  780. while( pentry )
  781. {
  782. if ( DnsNameCompare_W( hashName, pentry->pName ) )
  783. {
  784. //
  785. // found entry
  786. // - move to front, if not already there
  787. if ( pprevEntry )
  788. {
  789. pprevEntry->pNext = pentry->pNext;
  790. pentry->pNext = g_HashTable[ index ];
  791. g_HashTable[ index ] = pentry;
  792. }
  793. break;
  794. }
  795. ELSE
  796. {
  797. DNSDBG( OFF, (
  798. "in Cache_FindEntry -- failed name compare\n"
  799. "\tout name = %S\n"
  800. "\tpentry = %p\n"
  801. "\tname = %S\n",
  802. hashName,
  803. pentry,
  804. pentry->pName ));
  805. }
  806. pprevEntry = pentry;
  807. pentry = pentry->pNext;
  808. }
  809. //
  810. // if not found -- create?
  811. //
  812. // DCR: optimize for create
  813. //
  814. if ( !pentry && fCreate )
  815. {
  816. pentry = Cache_CreateEntry(
  817. hashName,
  818. TRUE // name already canonical
  819. );
  820. }
  821. DNS_ASSERT( !pentry || g_HashTable[ index ] == pentry );
  822. UNLOCK_CACHE();
  823. DNSDBG( TRACE, (
  824. "Leave Cache_FindEntry\n"
  825. "\tname = %S\n"
  826. "\tindex = %d\n"
  827. "\tpentry = %p\n",
  828. hashName,
  829. index,
  830. pentry ));
  831. return pentry;
  832. }
  833. PDNS_RECORD
  834. Cache_FindEntryRecords(
  835. OUT PDNS_RECORD ** pppRRList,
  836. IN PCACHE_ENTRY pEntry,
  837. IN WORD wType
  838. )
  839. /*++
  840. Routine Description:
  841. Find entry in cache.
  842. Arguments:
  843. pppRRList -- addr to recv addr of entry's ptr to RR list
  844. pEntry -- cache entry to check
  845. Type -- record type to find
  846. Return Value:
  847. Ptr to record set of desired type -- if found.
  848. NULL if not found.
  849. --*/
  850. {
  851. WORD iter;
  852. PDNS_RECORD prr;
  853. PDNS_RECORD * prrAddr = NULL;
  854. DNSDBG( TRACE, (
  855. "Cache_FindEntryRecords( %p, e=%p, type=%d )\n",
  856. pppRRList,
  857. pEntry,
  858. wType ));
  859. //
  860. // check all the records at the cache entry
  861. //
  862. for ( iter = 0;
  863. iter < pEntry->MaxCount;
  864. iter++ )
  865. {
  866. prrAddr = &pEntry->Records[iter];
  867. prr = *prrAddr;
  868. if ( !prr )
  869. {
  870. continue;
  871. }
  872. if ( !Cache_IsRecordTtlValid( prr ) )
  873. {
  874. DNSDBG( TRACE, (
  875. "Whacking timed out record %p at cache entry %p\n",
  876. prr,
  877. pEntry ));
  878. Dns_RecordListFree( prr );
  879. pEntry->Records[iter] = NULL;
  880. g_RecordSetCount--;
  881. g_RecordSetFree--;
  882. continue;
  883. }
  884. //
  885. // find matching type
  886. // - direct type match
  887. // - NAME_ERROR
  888. //
  889. if ( prr->wType == wType ||
  890. ( prr->wType == DNS_TYPE_ANY &&
  891. prr->wDataLength == 0 ) )
  892. {
  893. goto Done;
  894. }
  895. //
  896. // CNAME match
  897. // - walk list and determine if for matching type
  898. if ( prr->wType == DNS_TYPE_CNAME &&
  899. wType != DNS_TYPE_CNAME )
  900. {
  901. PDNS_RECORD prrChain = prr->pNext;
  902. while ( prrChain )
  903. {
  904. if ( prrChain->wType == wType )
  905. {
  906. // chain to desired type -- take RR set
  907. goto Done;
  908. }
  909. prrChain = prrChain->pNext;
  910. }
  911. }
  912. // records for another type -- continue
  913. }
  914. // type not found
  915. prr = NULL;
  916. Done:
  917. if ( pppRRList )
  918. {
  919. *pppRRList = prrAddr;
  920. }
  921. DNSDBG( TRACE, (
  922. "Leave Cache_FindEntryRecords => %p\n",
  923. prr ));
  924. return prr;
  925. }
  926. BOOL
  927. Cache_FlushEntryRecords(
  928. IN OUT PCACHE_ENTRY pEntry,
  929. IN DWORD Level,
  930. IN WORD wType
  931. )
  932. /*++
  933. Routine Description:
  934. Free cache entry.
  935. Arguments:
  936. pEntry -- cache entry to flush
  937. FlushLevel -- flush level
  938. FLUSH_LEVEL_NORMAL -- flush matching type, invalid, NAME_ERROR
  939. FLUSH_LEVEL_WIRE -- to flush all wire data, but leave hosts and cluster
  940. FLUSH_LEVEL_INVALID -- flush only invalid records
  941. FLUSH_LEVEL_STRONG -- to flush all but hosts file
  942. FLUSH_LEVEL_CLEANUP -- to flush all records for full cache flush
  943. wType -- flush type for levels with type
  944. DNS type -- to flush specifically this type
  945. Globals:
  946. g_EntryCount -- decremented appropriately
  947. g_NumberOfRecordsInCache -- decremented appropriately
  948. Return Value:
  949. TRUE if entry flushed completely.
  950. FALSE if records left.
  951. --*/
  952. {
  953. INT iter;
  954. BOOL recordsLeft = FALSE;
  955. DNSDBG( TRACE, (
  956. "Cache_FlushEntryRecords( %p, %08x, %d )\n",
  957. pEntry,
  958. Level,
  959. wType ));
  960. //
  961. // loop through records sets -- flush where appropriate
  962. //
  963. // CLEANUP flush
  964. // - everything
  965. //
  966. // STRONG (user initiated) flush
  967. // - all cached records, including cluster
  968. // but hostsfile saved
  969. //
  970. // WIRE flush
  971. // - all wire cached records
  972. // hosts file AND cluster saved
  973. //
  974. // INVALID flush
  975. // - timedout only
  976. //
  977. // NORMAL flush (regular flush done on caching)
  978. // - timed out records
  979. // - records of desired type
  980. // - NAME_ERROR
  981. //
  982. for ( iter = 0;
  983. iter < (INT)pEntry->MaxCount;
  984. iter++ )
  985. {
  986. PDNS_RECORD prr = pEntry->Records[iter];
  987. BOOL flush;
  988. if ( !prr )
  989. {
  990. continue;
  991. }
  992. //
  993. // switch on flush type
  994. // yes there are optimizations, but this is simple
  995. //
  996. if ( Level == FLUSH_LEVEL_NORMAL )
  997. {
  998. flush = ( !IS_STATIC_RR(prr)
  999. &&
  1000. ( prr->wType == wType ||
  1001. ( prr->wType == DNS_TYPE_ANY &&
  1002. prr->wDataLength == 0 ) ) );
  1003. }
  1004. else if ( Level == FLUSH_LEVEL_WIRE )
  1005. {
  1006. flush = !IS_STATIC_RR(prr);
  1007. }
  1008. else if ( Level == FLUSH_LEVEL_INVALID )
  1009. {
  1010. flush = !Cache_IsRecordTtlValid(prr);
  1011. }
  1012. else if ( Level == FLUSH_LEVEL_CLEANUP )
  1013. {
  1014. flush = TRUE;
  1015. }
  1016. else
  1017. {
  1018. DNS_ASSERT( Level == FLUSH_LEVEL_STRONG );
  1019. flush = !IS_HOSTS_FILE_RR(prr);
  1020. }
  1021. if ( flush )
  1022. {
  1023. pEntry->Records[iter] = NULL;
  1024. Dns_RecordListFree( prr );
  1025. g_RecordSetCount--;
  1026. g_RecordSetFree--;
  1027. }
  1028. else
  1029. {
  1030. recordsLeft = TRUE;
  1031. }
  1032. }
  1033. return !recordsLeft;
  1034. }
  1035. VOID
  1036. Cache_FlushBucket(
  1037. IN ULONG Index,
  1038. IN WORD FlushLevel
  1039. )
  1040. /*++
  1041. Routine Description:
  1042. Cleanup cache bucket.
  1043. Arguments:
  1044. Index -- Index of hash bucket to trim.
  1045. FlushLevel -- level of flush desired
  1046. see Cache_FlushEntryRecords() for description of
  1047. flush levels
  1048. Return Value:
  1049. None
  1050. --*/
  1051. {
  1052. PCACHE_ENTRY pentry;
  1053. PCACHE_ENTRY pprev;
  1054. INT countCompleted;
  1055. DNSDBG( CACHE, (
  1056. "Cache_FlushBucket( %d, %08x )\n",
  1057. Index,
  1058. FlushLevel ));
  1059. //
  1060. // flush entries in this bucket
  1061. //
  1062. // note: using hack here that hash table pointer can
  1063. // be treated as cache entry for purposes of accessing
  1064. // it's next pointer (since it's the first field in
  1065. // a CACHE_ENTRY)
  1066. // if this changes, must explicitly fix up "first entry"
  1067. // case or move to double-linked list that can free
  1068. // empty penty without regard to it's location
  1069. //
  1070. if ( !g_HashTable )
  1071. {
  1072. return;
  1073. }
  1074. //
  1075. // flush entries
  1076. //
  1077. // avoid holding lock too long by handling no more then
  1078. // fifty entries at a time
  1079. // note: generally 50 entries will cover entire bucket but
  1080. // can still be completed in reasonable time;
  1081. //
  1082. // DCR: smarter flush -- avoid lock\unlock
  1083. // peer into CS and don't unlock when no one waiting
  1084. // if waiting unlock and give up timeslice
  1085. // DCR: some LRU flush for garbage collection
  1086. //
  1087. countCompleted = 0;
  1088. while ( 1 )
  1089. {
  1090. INT count = 0;
  1091. INT countStop = countCompleted + 50;
  1092. LOCK_CACHE_NO_START();
  1093. if ( !g_HashTable )
  1094. {
  1095. UNLOCK_CACHE();
  1096. break;
  1097. }
  1098. DNSDBG( CACHE, (
  1099. "locked for bucket flush -- completed=%d, stop=%d\n",
  1100. count,
  1101. countStop ));
  1102. pprev = (PCACHE_ENTRY) &g_HashTable[ Index ];
  1103. while ( pentry = pprev->pNext )
  1104. {
  1105. // bypass any previously checked entries
  1106. if ( count++ < countCompleted )
  1107. {
  1108. pprev = pentry;
  1109. continue;
  1110. }
  1111. if ( count > countStop )
  1112. {
  1113. break;
  1114. }
  1115. // flush -- if successful cut from list and
  1116. // drop counts so countCompleted used in bypass
  1117. // will be correct and won't skip anyone
  1118. if ( Cache_FlushEntryRecords(
  1119. pentry,
  1120. FlushLevel,
  1121. 0 ) )
  1122. {
  1123. pprev->pNext = pentry->pNext;
  1124. Cache_FreeEntry( pentry );
  1125. count--;
  1126. countStop--;
  1127. continue;
  1128. }
  1129. pprev = pentry;
  1130. }
  1131. UNLOCK_CACHE();
  1132. countCompleted = count;
  1133. // stop when
  1134. // - cleared all the entries in the bucket
  1135. // - shutdown, except exempt the shutdown flush itself
  1136. if ( !pentry ||
  1137. (g_StopFlag && FlushLevel != FLUSH_LEVEL_CLEANUP) )
  1138. {
  1139. break;
  1140. }
  1141. }
  1142. DNSDBG( CACHE, (
  1143. "Leave Cache_FlushBucket( %d, %08x )\n"
  1144. "\trecord count = %d\n"
  1145. "\tentry count = %d\n",
  1146. Index,
  1147. FlushLevel,
  1148. g_RecordSetCount,
  1149. g_EntryCount ));
  1150. }
  1151. //
  1152. // Cache interface routines
  1153. //
  1154. VOID
  1155. Cache_PrepareRecordList(
  1156. IN OUT PDNS_RECORD pRecordList
  1157. )
  1158. /*++
  1159. Routine Description:
  1160. Prepare record list for cache.
  1161. Arguments:
  1162. pRecordList - record list to put in cache
  1163. Return Value:
  1164. Ptr to screened, prepared record list.
  1165. --*/
  1166. {
  1167. PDNS_RECORD prr = pRecordList;
  1168. PDNS_RECORD pnext;
  1169. DWORD ttl;
  1170. DWORD maxTtl;
  1171. DNSDBG( TRACE, (
  1172. "Cache_PrepareRecordList( rr=%p )\n",
  1173. prr ));
  1174. if ( !prr )
  1175. {
  1176. return;
  1177. }
  1178. //
  1179. // static (currently host file) TTL records
  1180. //
  1181. // currently no action required -- records come one
  1182. // at a time and no capability to even to the pName=NULL
  1183. // step
  1184. //
  1185. if ( IS_STATIC_RR(prr) )
  1186. {
  1187. return;
  1188. }
  1189. //
  1190. // wire records get relative TTL
  1191. // - compute minimum TTL for set
  1192. // - save TTL as timeout (offset by TTL from current time)
  1193. //
  1194. // DCR: TTL still not per set
  1195. // - but this is at least better than Win2K where
  1196. // multiple sets and did NOT find minimum
  1197. //
  1198. maxTtl = g_MaxCacheTtl;
  1199. if ( prr->wType == DNS_TYPE_SOA )
  1200. {
  1201. maxTtl = g_MaxSOACacheEntryTtlLimit;
  1202. }
  1203. //
  1204. // get caching TTL
  1205. // - minimum TTL in set
  1206. // - offset from current time
  1207. ttl = Dns_RecordListGetMinimumTtl( prr );
  1208. if ( ttl > maxTtl )
  1209. {
  1210. ttl = maxTtl;
  1211. }
  1212. ttl += g_CurrentCacheTime;
  1213. #if 0
  1214. // screening done at higher level now
  1215. //
  1216. // screen records
  1217. // - no non-RPCable types
  1218. // - no Authority records
  1219. //
  1220. if ( prr->wType != 0 )
  1221. {
  1222. prr = Dns_RecordListScreen(
  1223. prr,
  1224. SCREEN_OUT_AUTHORITY | SCREEN_OUT_NON_RPC );
  1225. DNS_ASSERT( prr );
  1226. }
  1227. #endif
  1228. //
  1229. // set timeout on all records in set
  1230. //
  1231. // note: FreeOwner handling depends on leading record
  1232. // in having owner name set, otherwise this produces
  1233. // bogus name owner fields
  1234. //
  1235. // DCR: set record list TTL function in dnslib
  1236. //
  1237. pnext = prr;
  1238. while ( pnext )
  1239. {
  1240. pnext->dwTtl = ttl;
  1241. if ( !FLAG_FreeOwner( pnext ) )
  1242. {
  1243. pnext->pName = NULL;
  1244. }
  1245. pnext = pnext->pNext;
  1246. }
  1247. }
  1248. VOID
  1249. Cache_RestoreRecordListForRpc(
  1250. IN OUT PDNS_RECORD pRecordList
  1251. )
  1252. /*++
  1253. Routine Description:
  1254. Restore cache record list for RPC.
  1255. Arguments:
  1256. pRecordList - record list to put in cache
  1257. Return Value:
  1258. None
  1259. --*/
  1260. {
  1261. PDNS_RECORD prr = pRecordList;
  1262. DWORD currentTime;
  1263. DNSDBG( TRACE, (
  1264. "Cache_RestoreRecordListForRpc( rr=%p )\n",
  1265. prr ));
  1266. if ( !prr )
  1267. {
  1268. DNS_ASSERT( FALSE );
  1269. return;
  1270. }
  1271. //
  1272. // static TTL records need no action
  1273. //
  1274. if ( IS_STATIC_RR(prr) )
  1275. {
  1276. return;
  1277. }
  1278. //
  1279. // turn timeouts back into TTLs
  1280. //
  1281. currentTime = g_CurrentCacheTime;
  1282. while ( prr )
  1283. {
  1284. DWORD ttl = prr->dwTtl - currentTime;
  1285. if ( (LONG)ttl < 0 )
  1286. {
  1287. ttl = 0;
  1288. }
  1289. prr->dwTtl = ttl;
  1290. prr = prr->pNext;
  1291. }
  1292. }
  1293. VOID
  1294. Cache_RecordSetAtomic(
  1295. IN PWSTR pwsName,
  1296. IN WORD wType,
  1297. IN PDNS_RECORD pRecordSet
  1298. )
  1299. /*++
  1300. Routine Description:
  1301. Cache record set atomically at entry.
  1302. Cache_RecordList() handles breakup of record list
  1303. and appropriate placing of records. This does caching
  1304. of single blob at particular location.
  1305. Arguments:
  1306. pRecordSet -- record list to add
  1307. Globals:
  1308. g_EntryCount -- decremented appropriately
  1309. g_NumberOfRecordsInCache -- decremented appropriately
  1310. Return Value:
  1311. None
  1312. --*/
  1313. {
  1314. INT iter;
  1315. WORD wtype;
  1316. PWSTR pname;
  1317. BOOL fstatic;
  1318. PCACHE_ENTRY pentry;
  1319. BOOL fretry;
  1320. WORD flushLevel;
  1321. DNSDBG( TRACE, (
  1322. "Cache_RecordSetAtomic( %S, type=%d, rr=%p )\n",
  1323. pwsName,
  1324. wType,
  1325. pRecordSet ));
  1326. if ( !pRecordSet )
  1327. {
  1328. return;
  1329. }
  1330. fstatic = IS_STATIC_RR(pRecordSet);
  1331. DNS_ASSERT( !fstatic ||
  1332. pRecordSet->pNext == NULL ||
  1333. (pRecordSet->wType==DNS_TYPE_CNAME) )
  1334. //
  1335. // determine caching type
  1336. // - specified OR from records
  1337. // CNAMEs will be at the head of a lookup from another type
  1338. //
  1339. wtype = wType;
  1340. if ( !wtype )
  1341. {
  1342. wtype = pRecordSet->wType;
  1343. }
  1344. //
  1345. // if name specified use it, otherwise use from records
  1346. //
  1347. pname = pwsName;
  1348. if ( !pname )
  1349. {
  1350. pname = pRecordSet->pName;
  1351. }
  1352. //
  1353. // prepare RR set for cache
  1354. //
  1355. Cache_PrepareRecordList( pRecordSet );
  1356. //
  1357. // find\create cache entry and cache
  1358. //
  1359. if ( LOCK_CACHE() != NO_ERROR )
  1360. {
  1361. LOCK_CACHE_NO_START();
  1362. goto Failed;
  1363. }
  1364. pentry = Cache_FindEntry(
  1365. pname,
  1366. TRUE // create
  1367. );
  1368. if ( !pentry )
  1369. {
  1370. goto Failed;
  1371. }
  1372. //
  1373. // clean up existing records at node
  1374. // - remove stale records
  1375. // - remove records of same type
  1376. // - if NAME_ERROR caching remove everything
  1377. // from wire
  1378. //
  1379. flushLevel = FLUSH_LEVEL_NORMAL;
  1380. if ( wtype == DNS_TYPE_ALL &&
  1381. pRecordSet->wDataLength == 0 )
  1382. {
  1383. flushLevel = FLUSH_LEVEL_WIRE;
  1384. }
  1385. Cache_FlushEntryRecords(
  1386. pentry,
  1387. flushLevel,
  1388. wtype );
  1389. //
  1390. // check for matching record type still there
  1391. //
  1392. for ( iter = 0;
  1393. iter < (INT)pentry->MaxCount;
  1394. iter++ )
  1395. {
  1396. PDNS_RECORD prrExist = pentry->Records[iter];
  1397. if ( !prrExist ||
  1398. prrExist->wType != wtype )
  1399. {
  1400. continue;
  1401. }
  1402. // matching type still there after flush
  1403. // - if trying to cache wire set at hostfile entry, fail
  1404. DNS_ASSERT( IS_STATIC_RR(prrExist) );
  1405. if ( !fstatic )
  1406. {
  1407. DNSDBG( ANY, (
  1408. "ERROR: attempted caching at static (hosts file) record data!\n"
  1409. "\tpRecord = %p\n"
  1410. "\tName = %S\n"
  1411. "\tType = %d\n"
  1412. "\t-- Dumping new cache record list.\n",
  1413. pRecordSet,
  1414. pRecordSet->pName,
  1415. pRecordSet->wType ));
  1416. goto Failed;
  1417. }
  1418. //
  1419. // append host file records
  1420. // - start at "record" which is addr of record ptr entry
  1421. // making pNext field the actual pointer
  1422. // - delete duplicates
  1423. // - tack new RR on end
  1424. // - blow away new RR name if existing record
  1425. //
  1426. // DCR: should have simple "make cache RR set" function that
  1427. // handles name and TTL issues
  1428. //
  1429. // DCR: broken if non-flush load hits wire data; wire data
  1430. // may have multiple RR sets
  1431. //
  1432. else
  1433. {
  1434. PDNS_RECORD prr;
  1435. PDNS_RECORD prrPrev = (PDNS_RECORD) &pentry->Records[iter];
  1436. while ( prr = prrPrev->pNext )
  1437. {
  1438. // matches existing record?
  1439. // - cut existing record from list and free
  1440. if ( Dns_RecordCompare( prr, pRecordSet ) )
  1441. {
  1442. prrPrev->pNext = prr->pNext;
  1443. Dns_RecordFree( prr );
  1444. }
  1445. else
  1446. {
  1447. prrPrev = prr;
  1448. }
  1449. }
  1450. //
  1451. // tack entry on to end
  1452. // - if existing records of type delete name
  1453. //
  1454. if ( prrPrev != (PDNS_RECORD)&pentry->Records[iter] )
  1455. {
  1456. if ( IS_FREE_OWNER(pRecordSet) )
  1457. {
  1458. RECORD_HEAP_FREE( pRecordSet->pName );
  1459. pRecordSet->pName = NULL;
  1460. }
  1461. }
  1462. prrPrev->pNext = pRecordSet;
  1463. goto Done;
  1464. }
  1465. }
  1466. //
  1467. // put record into cache entry
  1468. //
  1469. // if no slot is available, switch to a harder scrub
  1470. //
  1471. // DCR: realloc if out of slots
  1472. //
  1473. fretry = FALSE;
  1474. while ( 1 )
  1475. {
  1476. for ( iter = 0;
  1477. iter < (INT)pentry->MaxCount;
  1478. iter++ )
  1479. {
  1480. if ( pentry->Records[iter] == NULL )
  1481. {
  1482. pentry->Records[iter] = pRecordSet;
  1483. g_RecordSetCount++;
  1484. g_RecordSetCache++;
  1485. goto Done;
  1486. }
  1487. }
  1488. if ( !fretry )
  1489. {
  1490. DNSDBG( QUERY, (
  1491. "No slots caching RR set %p at entry %p\n"
  1492. "\tdoing strong flush to free slot.\n",
  1493. pRecordSet,
  1494. pentry ));
  1495. Cache_FlushEntryRecords(
  1496. pentry,
  1497. FLUSH_LEVEL_WIRE,
  1498. 0 );
  1499. fretry = TRUE;
  1500. continue;
  1501. }
  1502. DNSDBG( ANY, (
  1503. "ERROR: Failed to cache set %p at entry %p\n",
  1504. pRecordSet,
  1505. pentry ));
  1506. goto Failed;
  1507. }
  1508. Failed:
  1509. DNSDBG( TRACE, ( "Cache_RecordSetAtomic() => failed\n" ));
  1510. Dns_RecordListFree( pRecordSet );
  1511. Done:
  1512. UNLOCK_CACHE();
  1513. DNSDBG( TRACE, ( "Leave Cache_RecordSetAtomic()\n" ));
  1514. return;
  1515. }
  1516. VOID
  1517. Cache_RecordList(
  1518. IN OUT PDNS_RECORD pRecordList
  1519. )
  1520. /*++
  1521. Routine Description:
  1522. Cache record list.
  1523. This is cache routine for "oddball" records -- not caching under
  1524. queried name.
  1525. - hostfile
  1526. - answer records at CNAME
  1527. - additional data at additional name
  1528. Arguments:
  1529. pRecordList -- record list to cache
  1530. Return Value:
  1531. None
  1532. --*/
  1533. {
  1534. BOOL fcnameAnswer = FALSE;
  1535. PDNS_RECORD pnextRR = pRecordList;
  1536. PDNS_RECORD prr;
  1537. BOOL fstatic;
  1538. DNSDBG( TRACE, (
  1539. "Cache_RecordList( rr=%p )\n",
  1540. pRecordList ));
  1541. if ( !pRecordList )
  1542. {
  1543. return;
  1544. }
  1545. fstatic = IS_STATIC_RR(pRecordList);
  1546. //
  1547. // cache records:
  1548. // - cache additional records in query
  1549. // - cache CNAME data from query
  1550. // - cache host file data
  1551. //
  1552. // background: Glenn's caching paradigm was to cache all answer
  1553. // data at the queried name in the API call (name might be short).
  1554. // However, not caching the CNAME data can cause problems, so this
  1555. // was tacked on.
  1556. //
  1557. // For CNAME caching we throw away the CNAMEs themselves and just
  1558. // cache the actually data (address) records at the CNAME node.
  1559. //
  1560. //
  1561. // cache additional records
  1562. //
  1563. while ( prr = pnextRR )
  1564. {
  1565. BOOL fcacheSet = FALSE;
  1566. pnextRR = Dns_RecordSetDetach( prr );
  1567. //
  1568. // host file data -- always cache
  1569. //
  1570. // for CNAME want CNAME AND associated answer data
  1571. // - detach to get new next set
  1572. // - append answer data back on to CNAME for caching
  1573. // - next RR set (if exists) will be another CNAME
  1574. // to the same address data
  1575. //
  1576. // DCR: follow CNAMEs in cache
  1577. // then could pull this hack
  1578. // and avoid double building of answer data in dnsapi
  1579. //
  1580. if ( fstatic )
  1581. {
  1582. fcacheSet = TRUE;
  1583. if ( prr->wType == DNS_TYPE_CNAME &&
  1584. pnextRR &&
  1585. pnextRR->wType != DNS_TYPE_CNAME )
  1586. {
  1587. PDNS_RECORD panswer = pnextRR;
  1588. pnextRR = Dns_RecordSetDetach( panswer );
  1589. Dns_RecordListAppend( prr, panswer );
  1590. }
  1591. }
  1592. //
  1593. // wire data -- do NOT cache:
  1594. // - answer records for queried name (not CNAME)
  1595. // - CNAME records when doing caching of answer data under CNAME
  1596. // - authority section records (NS, SOA, etc)
  1597. // - OPT records
  1598. //
  1599. else if ( prr->Flags.S.Section == DNSREC_ANSWER )
  1600. {
  1601. if ( prr->wType == DNS_TYPE_CNAME )
  1602. {
  1603. fcnameAnswer = TRUE;
  1604. }
  1605. else if ( fcnameAnswer )
  1606. {
  1607. fcacheSet = TRUE;
  1608. }
  1609. }
  1610. else if ( prr->Flags.S.Section == DNSREC_ADDITIONAL )
  1611. {
  1612. if ( prr->wType != DNS_TYPE_OPT )
  1613. {
  1614. fcacheSet = TRUE;
  1615. }
  1616. }
  1617. if ( !fcacheSet )
  1618. {
  1619. Dns_RecordListFree( prr );
  1620. continue;
  1621. }
  1622. //
  1623. // cache the set
  1624. //
  1625. // flip the section field to "Answer" section
  1626. //
  1627. // DCR: section caching?
  1628. //
  1629. // note: section fields in cache indicate whether
  1630. // answer data (or additional) once out of
  1631. // cache;
  1632. // this is necessary since we cache everything
  1633. // at node and return it in one RR list; we'd
  1634. // to change must
  1635. // - return in different lists with some indication
  1636. // in cache of what's what
  1637. // OR
  1638. // - another indication of what's what
  1639. //
  1640. //if ( !fstatic )
  1641. // currently HostFile entries get answer too
  1642. {
  1643. PDNS_RECORD ptemp = prr;
  1644. while ( ptemp )
  1645. {
  1646. ptemp->Flags.S.Section = DNSREC_ANSWER;
  1647. ptemp = ptemp->pNext;
  1648. }
  1649. }
  1650. Cache_RecordSetAtomic(
  1651. NULL,
  1652. 0,
  1653. prr );
  1654. }
  1655. DNSDBG( TRACE, ( "Leave Cache_RecordList()\n" ));
  1656. }
  1657. VOID
  1658. Cache_FlushRecords(
  1659. IN PWSTR pName,
  1660. IN DWORD Level,
  1661. IN WORD Type
  1662. )
  1663. /*++
  1664. Routine Description:
  1665. Flush cached records corresponding to a name and type.
  1666. Arguments:
  1667. pName -- name of records to delete
  1668. Level -- flush level
  1669. Type -- type of records to delete;
  1670. 0 to flush all records at name
  1671. Return Value:
  1672. ERROR_SUCCESS if successful.
  1673. ErrorCode on failure.
  1674. --*/
  1675. {
  1676. WORD iter;
  1677. PCACHE_ENTRY pentry = NULL;
  1678. PCACHE_ENTRY pprevEntry = NULL;
  1679. DNSDBG( TRACE, (
  1680. "Cache_FlushRecords( %S, %d )\n",
  1681. pName,
  1682. Type ));
  1683. //
  1684. // lock with no-start
  1685. // - bail if no cache
  1686. //
  1687. // need this as PnP release notifications will attempt to
  1688. // flush local cache entries; this avoids rebuilding when
  1689. // already down
  1690. //
  1691. LOCK_CACHE_NO_START();
  1692. if ( !g_HashTable )
  1693. {
  1694. goto Done;
  1695. }
  1696. //
  1697. // find entry in cache
  1698. //
  1699. pentry = Cache_FindEntry(
  1700. pName,
  1701. FALSE // no create
  1702. );
  1703. if ( !pentry )
  1704. {
  1705. goto Done;
  1706. }
  1707. //
  1708. // flush records of type
  1709. // - zero type will flush all
  1710. //
  1711. // note: Cache_FindEntry() always moves the found entry
  1712. // to the front of the hash bucket list; this allows
  1713. // us to directly whack the entry
  1714. //
  1715. if ( Cache_FlushEntryRecords(
  1716. pentry,
  1717. Level,
  1718. Type ) )
  1719. {
  1720. DWORD index = getHashIndex(
  1721. pentry->pName,
  1722. 0 );
  1723. DNS_ASSERT( pentry == g_HashTable[index] );
  1724. if ( pentry == g_HashTable[index] )
  1725. {
  1726. g_HashTable[ index ] = pentry->pNext;
  1727. Cache_FreeEntry( pentry );
  1728. }
  1729. }
  1730. Done:
  1731. UNLOCK_CACHE();
  1732. }
  1733. #if 0
  1734. BOOL
  1735. ReadCachedResults(
  1736. OUT PDNS_RESULTS pResults,
  1737. IN PWSTR pwsName,
  1738. IN WORD wType
  1739. )
  1740. /*++
  1741. Routine Description:
  1742. Find records of given name and type in cache.
  1743. Arguments:
  1744. pResults -- addr to receive results
  1745. pwsName -- name
  1746. wType -- record type to find
  1747. Return Value:
  1748. TRUE if results found.
  1749. FALSE if no cached data for name and type.
  1750. --*/
  1751. {
  1752. PDNS_RECORD prr;
  1753. DNS_STATUS status;
  1754. BOOL found = FALSE;
  1755. //
  1756. // clear results
  1757. //
  1758. RtlZeroMemory( pResults, sizeof(*pResults) );
  1759. // get cache results
  1760. // break out into results buffer
  1761. if ( found )
  1762. {
  1763. BreakRecordsIntoBlob(
  1764. pResults,
  1765. prr,
  1766. wType );
  1767. pResults->Status = status;
  1768. }
  1769. return( found );
  1770. }
  1771. #endif
  1772. //
  1773. // Cache utilities for remote routines
  1774. //
  1775. PDNS_RECORD
  1776. Cache_FindRecordsPrivate(
  1777. IN PWSTR pwsName,
  1778. IN WORD wType
  1779. )
  1780. /*++
  1781. Routine Description:
  1782. Find records of given name and type in cache.
  1783. Arguments:
  1784. pwsName -- name
  1785. Type -- record type to find
  1786. Return Value:
  1787. Ptr to record set of desired type -- if found.
  1788. NULL if not found.
  1789. --*/
  1790. {
  1791. PCACHE_ENTRY pentry;
  1792. PDNS_RECORD prr = NULL;
  1793. DNSDBG( TRACE, (
  1794. "Cache_FindRecordsPrivate( %S, type=%d )\n",
  1795. pwsName,
  1796. wType ));
  1797. if ( LOCK_CACHE() != NO_ERROR )
  1798. {
  1799. goto Done;
  1800. }
  1801. pentry = Cache_FindEntry(
  1802. pwsName,
  1803. FALSE );
  1804. if ( pentry )
  1805. {
  1806. prr = Cache_FindEntryRecords(
  1807. NULL, // don't need RR list ptr
  1808. pentry,
  1809. wType );
  1810. }
  1811. UNLOCK_CACHE();
  1812. Done:
  1813. DNSDBG( TRACE, (
  1814. "Leave Cache_FindRecordsPrivate( %S, type=%d ) => %p\n",
  1815. pwsName,
  1816. wType,
  1817. prr ));
  1818. return prr;
  1819. }
  1820. BOOL
  1821. Cache_GetRecordsForRpc(
  1822. OUT PDNS_RECORD * ppRecordList,
  1823. OUT PDNS_STATUS pStatus,
  1824. IN PWSTR pwsName,
  1825. IN WORD wType,
  1826. IN DWORD Flags
  1827. )
  1828. /*++
  1829. Routine Description:
  1830. Find records of given name and type in cache.
  1831. Arguments:
  1832. ppRecordList -- addr to receive pointer to record list
  1833. pStatus -- addr to get status return
  1834. pwsName -- name
  1835. Type -- record type to find
  1836. Flags -- query flags
  1837. Return Value:
  1838. TRUE if cache hit. OUT params are valid.
  1839. FALSE if cache miss. OUT params are unset.
  1840. --*/
  1841. {
  1842. PDNS_RECORD prr;
  1843. PDNS_RECORD prrResult = NULL;
  1844. DNS_STATUS status = NO_ERROR;
  1845. DNSDBG( RPC, (
  1846. "Cache_GetRecordsForRpc( %S, t=%d )\n",
  1847. pwsName,
  1848. wType ));
  1849. if ( (Flags & DNS_QUERY_BYPASS_CACHE) &&
  1850. (Flags & DNS_QUERY_NO_HOSTS_FILE) )
  1851. {
  1852. return FALSE;
  1853. }
  1854. if ( LOCK_CACHE() != NO_ERROR )
  1855. {
  1856. return FALSE;
  1857. }
  1858. //
  1859. // check cache for name and type
  1860. // - if name or type missing, jump to wire lookup
  1861. //
  1862. prr = Cache_FindRecordsPrivate(
  1863. pwsName,
  1864. wType );
  1865. if ( !prr )
  1866. {
  1867. goto Failed;
  1868. }
  1869. //
  1870. // cache hit
  1871. //
  1872. // if only interested in host file data ignore
  1873. //
  1874. if ( IS_HOSTS_FILE_RR(prr) )
  1875. {
  1876. if ( Flags & DNS_QUERY_NO_HOSTS_FILE )
  1877. {
  1878. goto Failed;
  1879. }
  1880. }
  1881. else // cache data
  1882. {
  1883. if ( Flags & DNS_QUERY_BYPASS_CACHE )
  1884. {
  1885. goto Failed;
  1886. }
  1887. }
  1888. //
  1889. // build response from cache data
  1890. // - cached NAME_ERROR or empty
  1891. // - cached records
  1892. //
  1893. if ( prr->wDataLength == 0 )
  1894. {
  1895. status = (prr->wType == DNS_TYPE_ANY)
  1896. ? DNS_ERROR_RCODE_NAME_ERROR
  1897. : DNS_INFO_NO_RECORDS;
  1898. }
  1899. else
  1900. {
  1901. // for CNAME query, get only the CNAME record itself
  1902. // not the data at the CNAME
  1903. //
  1904. // DCR: CNAME handling should be optional -- not given
  1905. // for cache display purposes
  1906. //
  1907. if ( wType == DNS_TYPE_CNAME &&
  1908. prr->wType == DNS_TYPE_CNAME &&
  1909. prr->Flags.S.Section == DNSREC_ANSWER )
  1910. {
  1911. prrResult = Dns_RecordCopyEx(
  1912. prr,
  1913. DnsCharSetUnicode,
  1914. DnsCharSetUnicode );
  1915. }
  1916. else
  1917. {
  1918. prrResult = Dns_RecordSetCopyEx(
  1919. prr,
  1920. DnsCharSetUnicode,
  1921. DnsCharSetUnicode );
  1922. }
  1923. if ( prrResult )
  1924. {
  1925. Cache_RestoreRecordListForRpc( prrResult );
  1926. status = ERROR_SUCCESS;
  1927. }
  1928. else
  1929. {
  1930. status = ERROR_NOT_ENOUGH_MEMORY;
  1931. }
  1932. }
  1933. UNLOCK_CACHE();
  1934. // set return values
  1935. *ppRecordList = prrResult;
  1936. *pStatus = status;
  1937. return TRUE;
  1938. Failed:
  1939. UNLOCK_CACHE();
  1940. return FALSE;
  1941. }
  1942. VOID
  1943. Cache_DeleteMatchingRecords(
  1944. IN PDNS_RECORD pRecords
  1945. )
  1946. /*++
  1947. Routine Description:
  1948. Delete particular records from the cache.
  1949. This is used to delete cluster records.
  1950. Arguments:
  1951. pRecords -- records to remove from cache
  1952. Return Value:
  1953. None
  1954. --*/
  1955. {
  1956. PCACHE_ENTRY pentry = NULL;
  1957. PDNS_RECORD * prrListAddr;
  1958. PDNS_RECORD prr;
  1959. PDNS_RECORD pnextRR;
  1960. DNSDBG( TRACE, (
  1961. "Cache_DeleteMatchingRecords( %p )\n",
  1962. pRecords ));
  1963. //
  1964. // lock with no-start
  1965. // - bail if no cache
  1966. //
  1967. // need this as PnP release notifications will attempt to
  1968. // flush local cache entries; this avoids rebuilding when
  1969. // already down
  1970. //
  1971. LOCK_CACHE_NO_START();
  1972. if ( !g_HashTable )
  1973. {
  1974. goto Done;
  1975. }
  1976. //
  1977. // check all records
  1978. //
  1979. pnextRR = pRecords;
  1980. while ( prr = pnextRR )
  1981. {
  1982. pnextRR = prr->pNext;
  1983. //
  1984. // find entry in cache
  1985. //
  1986. pentry = Cache_FindEntry(
  1987. prr->pName,
  1988. FALSE // no create
  1989. );
  1990. if ( !pentry )
  1991. {
  1992. DNSDBG( TRACE, (
  1993. "No cache entry for record %p (n=%S)\n",
  1994. prr,
  1995. prr->pName ));
  1996. continue;
  1997. }
  1998. //
  1999. // find matching records for type
  2000. //
  2001. prrListAddr = NULL;
  2002. Cache_FindEntryRecords(
  2003. &prrListAddr,
  2004. pentry,
  2005. prr->wType );
  2006. if ( !prrListAddr )
  2007. {
  2008. DNSDBG( TRACE, (
  2009. "No cache record matching type for record %p (n=%S)\n",
  2010. prr,
  2011. prr->pName ));
  2012. continue;
  2013. }
  2014. //
  2015. // delete matching record from list
  2016. //
  2017. Dns_DeleteRecordFromList(
  2018. prrListAddr, // addr of list
  2019. prr // record to delete
  2020. );
  2021. }
  2022. Done:
  2023. UNLOCK_CACHE();
  2024. }
  2025. //
  2026. // Garbage collection
  2027. //
  2028. VOID
  2029. Cache_SizeCheck(
  2030. VOID
  2031. )
  2032. /*++
  2033. Routine Description:
  2034. Check cache size.
  2035. Arguments:
  2036. Flag -- flag, currently unused
  2037. Return Value:
  2038. None
  2039. --*/
  2040. {
  2041. //
  2042. // ok -- don't signal for garbage collect
  2043. //
  2044. // - below threshold
  2045. // - already in garbage collection
  2046. // - collected recently
  2047. //
  2048. if ( g_RecordSetCount < g_RecordSetCountThreshold ||
  2049. g_GarbageCollectFlag ||
  2050. g_NextGarbageTime > GetCurrentTimeInSeconds() )
  2051. {
  2052. return;
  2053. }
  2054. DNSDBG( CACHE, (
  2055. "Cache_SizeCheck() over threshold!\n"
  2056. "\tRecordSetCount = %d\n"
  2057. "\tRecordSetCountLimit = %d\n"
  2058. "\tStarting garbage collection ...\n",
  2059. g_RecordSetCount,
  2060. g_RecordSetCountThreshold ));
  2061. //
  2062. // signal within lock, so that service thread
  2063. // can do signal within lock and avoid race on StopFlag check
  2064. // obviously better to simply not overload lock
  2065. //
  2066. LOCK_CACHE_NO_START();
  2067. if ( !g_StopFlag )
  2068. {
  2069. g_GarbageCollectFlag = TRUE;
  2070. SetEvent( g_hStopEvent );
  2071. }
  2072. UNLOCK_CACHE();
  2073. }
  2074. VOID
  2075. Cache_GarbageCollect(
  2076. IN DWORD Flag
  2077. )
  2078. /*++
  2079. Routine Description:
  2080. Garbage collect cache.
  2081. Arguments:
  2082. Flag -- flag, currently unused
  2083. Return Value:
  2084. None
  2085. --*/
  2086. {
  2087. DWORD iter;
  2088. DWORD index;
  2089. WORD flushLevel;
  2090. DWORD passCount;
  2091. DNSDBG( CACHE, (
  2092. "Cache_GarbageCollect()\n"
  2093. "\tNextIndex = %d\n"
  2094. "\tRecordSetCount = %d\n"
  2095. "\tRecordSetLimit = %d\n"
  2096. "\tRecordSetThreshold = %d\n",
  2097. g_NextGarbageIndex,
  2098. g_RecordSetCount,
  2099. g_RecordSetCountLimit,
  2100. g_RecordSetCountThreshold
  2101. ));
  2102. if ( !g_HashTable )
  2103. {
  2104. return;
  2105. }
  2106. //
  2107. // collect timed out data in cache
  2108. //
  2109. // DCR: smart garbage detect
  2110. // - cleans until below limit
  2111. // - first pass invalid
  2112. // - then the hard stuff
  2113. // use restartable index so get through the cach
  2114. //
  2115. passCount = 0;
  2116. while ( 1 )
  2117. {
  2118. if ( passCount == 0 )
  2119. {
  2120. flushLevel = FLUSH_LEVEL_INVALID;
  2121. }
  2122. else if ( passCount == 1 )
  2123. {
  2124. flushLevel = FLUSH_LEVEL_GARBAGE;
  2125. }
  2126. else
  2127. {
  2128. break;
  2129. }
  2130. passCount++;
  2131. //
  2132. // flush all hash bins at current flush level
  2133. // until
  2134. // - service stop
  2135. // - push cache size below limit
  2136. //
  2137. for ( iter = 0;
  2138. iter < g_HashTableSize;
  2139. iter++ )
  2140. {
  2141. index = (iter + g_NextGarbageIndex) % g_HashTableSize;
  2142. if ( g_StopFlag ||
  2143. g_WakeFlag ||
  2144. g_RecordSetCount < g_RecordSetCountLimit )
  2145. {
  2146. passCount = MAXDWORD;
  2147. break;
  2148. }
  2149. Cache_FlushBucket(
  2150. index,
  2151. flushLevel );
  2152. }
  2153. index++;
  2154. if ( index >= g_HashTableSize )
  2155. {
  2156. index = 0;
  2157. }
  2158. g_NextGarbageIndex = index;
  2159. }
  2160. //
  2161. // reset garbage globals
  2162. // - lockout for interval
  2163. // - clear signal flag
  2164. // - reset event (if not shuttting down)
  2165. //
  2166. // note: reset signal within lock, so that service thread
  2167. // can do signal within lock and avoid race on StopFlag check
  2168. // obviously better to simply not overload lock
  2169. //
  2170. g_NextGarbageTime = GetCurrentTimeInSeconds() + GARBAGE_LOCKOUT_INTERVAL;
  2171. LOCK_CACHE_NO_START();
  2172. if ( !g_StopFlag )
  2173. {
  2174. g_GarbageCollectFlag = FALSE;
  2175. ResetEvent( g_hStopEvent );
  2176. }
  2177. UNLOCK_CACHE();
  2178. DNSDBG( CACHE, (
  2179. "Leave Cache_GarbageCollect()\n"
  2180. "\tNextIndex = %d\n"
  2181. "\tNextTime = %d\n"
  2182. "\tRecordSetCount = %d\n"
  2183. "\tRecordSetLimit = %d\n"
  2184. "\tRecordSetThreshold = %d\n",
  2185. g_NextGarbageIndex,
  2186. g_NextGarbageTime,
  2187. g_RecordSetCount,
  2188. g_RecordSetCountLimit,
  2189. g_RecordSetCountThreshold
  2190. ));
  2191. }
  2192. //
  2193. // Hostfile load stuff
  2194. //
  2195. VOID
  2196. LoadHostFileIntoCache(
  2197. IN PSTR pszFileName
  2198. )
  2199. /*++
  2200. Routine Description:
  2201. Read hosts file into cache.
  2202. Arguments:
  2203. pFileName -- file name to load
  2204. Return Value:
  2205. None.
  2206. --*/
  2207. {
  2208. HOST_FILE_INFO hostInfo;
  2209. DNSDBG( INIT, ( "Enter LoadHostFileIntoCache\n" ));
  2210. //
  2211. // read entries from host file until exhausted
  2212. // - cache A record for each name and alias
  2213. // - cache PTR to name
  2214. //
  2215. RtlZeroMemory(
  2216. &hostInfo,
  2217. sizeof(hostInfo) );
  2218. hostInfo.pszFileName = pszFileName;
  2219. if ( !HostsFile_Open( &hostInfo ) )
  2220. {
  2221. return;
  2222. }
  2223. hostInfo.fBuildRecords = TRUE;
  2224. while ( HostsFile_ReadLine( &hostInfo ) )
  2225. {
  2226. // cache all the records we sucked out
  2227. Cache_RecordList( hostInfo.pForwardRR );
  2228. Cache_RecordList( hostInfo.pReverseRR );
  2229. Cache_RecordList( hostInfo.pAliasRR );
  2230. }
  2231. HostsFile_Close( &hostInfo );
  2232. DNSDBG( INIT, ( "Leave LoadHostFileIntoCache\n" ));
  2233. }
  2234. VOID
  2235. InitCacheWithHostFile(
  2236. VOID
  2237. )
  2238. /*++
  2239. Routine Description:
  2240. Initialize cache with host(s) file.
  2241. This handles regular cache file and ICS file if it
  2242. exists.
  2243. Arguments:
  2244. None
  2245. Return Value:
  2246. None.
  2247. --*/
  2248. {
  2249. DNSDBG( INIT, ( "Enter InitCacheWithHostFile\n" ));
  2250. //
  2251. // load host file into cache
  2252. //
  2253. LoadHostFileIntoCache( NULL );
  2254. //
  2255. // if running ICS, load it's file also
  2256. //
  2257. LoadHostFileIntoCache( "hosts.ics" );
  2258. DNSDBG( INIT, ( "Leave InitCacheWithHostFile\n\n\n" ));
  2259. }
  2260. DNS_STATUS
  2261. Cache_QueryResponse(
  2262. IN OUT PQUERY_BLOB pBlob
  2263. )
  2264. /*++
  2265. Routine Description:
  2266. Find records of given name and type in cache.
  2267. Arguments:
  2268. pBlob -- query blob
  2269. Uses:
  2270. pwsName
  2271. wType
  2272. Status
  2273. pRecords
  2274. fCacheNegativeResponse
  2275. Sets:
  2276. pRecords - may be reset to exclude non-RPCable records
  2277. Return Value:
  2278. ErrorStatus -- same as query status, unless processing error during caching
  2279. --*/
  2280. {
  2281. DNS_STATUS status = pBlob->Status;
  2282. PWSTR pname = pBlob->pNameOrig;
  2283. WORD wtype = pBlob->wType;
  2284. PDNS_RECORD presultRR = pBlob->pRecords;
  2285. DNSDBG( RPC, (
  2286. "\nCache_QueryResponse( %S, type %d )\n",
  2287. pname,
  2288. wtype ));
  2289. //
  2290. // successful response
  2291. // - make copy of records to return to caller
  2292. // - cache actual query record set
  2293. // - make copy to cache any additional data
  2294. //
  2295. if ( status == ERROR_SUCCESS && presultRR )
  2296. {
  2297. DWORD copyFlag;
  2298. PDNS_RECORD prrCache;
  2299. // cleanup for RPC and caching
  2300. prrCache = Dns_RecordListScreen(
  2301. presultRR,
  2302. SCREEN_OUT_AUTHORITY | SCREEN_OUT_NON_RPC );
  2303. //
  2304. // make copy for return
  2305. // - don't include authority records
  2306. //
  2307. // NOTE: IMPORTANT
  2308. // we return (RPC) a COPY of the wire set and cache the
  2309. // wire set; this is because the wire set has imbedded data
  2310. // (the data pointers are not actual heap allocations) and
  2311. // and hence can not be RPC'd (without changing the RPC
  2312. // definition to flat data)
  2313. //
  2314. // if we later want to return authority data on first query,
  2315. // then
  2316. // - clean non-RPC only
  2317. // - including owner name fixups
  2318. // - copy for result set
  2319. // - clean original for authority -- cache
  2320. // - clean any additional -- cache
  2321. //
  2322. // note: do name pointer fixup by making round trip into cache format
  2323. //
  2324. // DCR: shouldn't have external name pointers anywhere
  2325. // DCR: do RPC-able cleanup on original set before copy
  2326. // OR
  2327. // DCR: have "cache state" on record
  2328. // then could move original results to cache state and caching
  2329. // routines could detect state and avoid double TTLing
  2330. presultRR = Dns_RecordListCopyEx(
  2331. prrCache,
  2332. 0,
  2333. // SCREEN_OUT_AUTHORITY
  2334. DnsCharSetUnicode,
  2335. DnsCharSetUnicode );
  2336. pBlob->pRecords = presultRR;
  2337. if ( !presultRR )
  2338. {
  2339. Dns_RecordListFree( prrCache );
  2340. status = DNS_ERROR_NO_MEMORY;
  2341. goto Done;
  2342. }
  2343. // name pointer fixup
  2344. Cache_PrepareRecordList( presultRR );
  2345. Cache_RestoreRecordListForRpc( presultRR );
  2346. //
  2347. // do NOT cache local records
  2348. //
  2349. // note: we went through this function only to get
  2350. // PTR records and CNAME records in RPC format
  2351. // (no imbedded pointers)
  2352. //
  2353. if ( pBlob->pLocalRecords )
  2354. {
  2355. Dns_RecordListFree( prrCache );
  2356. goto Done;
  2357. }
  2358. //
  2359. // cache original data
  2360. //
  2361. if ( prrCache )
  2362. {
  2363. Cache_RecordSetAtomic(
  2364. pname,
  2365. wtype,
  2366. prrCache );
  2367. }
  2368. //
  2369. // extra records
  2370. // - additional data
  2371. // - CNAME answer data to cache at CNAME itself
  2372. // in CNAME case must include ANSWER data, but
  2373. // skip the CNAME itself
  2374. //
  2375. // Cache_RecordList() breaks records into RR sets before caching
  2376. //
  2377. prrCache = presultRR;
  2378. copyFlag = SCREEN_OUT_ANSWER | SCREEN_OUT_AUTHORITY;
  2379. if ( prrCache->wType == DNS_TYPE_CNAME )
  2380. {
  2381. prrCache = prrCache->pNext;
  2382. copyFlag = SCREEN_OUT_AUTHORITY;
  2383. }
  2384. prrCache = Dns_RecordListCopyEx(
  2385. prrCache,
  2386. copyFlag,
  2387. DnsCharSetUnicode,
  2388. DnsCharSetUnicode );
  2389. if ( prrCache )
  2390. {
  2391. Cache_RecordList( prrCache );
  2392. }
  2393. }
  2394. //
  2395. // negative response
  2396. //
  2397. else if ( status == DNS_ERROR_RCODE_NAME_ERROR ||
  2398. status == DNS_INFO_NO_RECORDS )
  2399. {
  2400. DWORD ttl;
  2401. PDNS_RECORD prr;
  2402. if ( !pBlob->fCacheNegative )
  2403. {
  2404. DNSDBG( QUERY, (
  2405. "No negative caching for %S, type=%d\n",
  2406. pname, wtype ));
  2407. goto Done;
  2408. }
  2409. //
  2410. // create negative cache entry
  2411. //
  2412. // DCR: should use TTL returned in SOA
  2413. //
  2414. prr = Dns_AllocateRecord( 0 );
  2415. if ( !prr )
  2416. {
  2417. status = ERROR_NOT_ENOUGH_MEMORY;
  2418. goto Done;
  2419. }
  2420. prr->pName = (PWSTR) Dns_StringCopyAllocate(
  2421. (PCHAR) pname,
  2422. 0, // NULL terminated
  2423. DnsCharSetUnicode,
  2424. DnsCharSetUnicode );
  2425. if ( prr->pName )
  2426. {
  2427. SET_FREE_OWNER( prr );
  2428. }
  2429. prr->wDataLength = 0;
  2430. ttl = g_MaxNegativeCacheTtl;
  2431. if ( wtype == DNS_TYPE_SOA
  2432. &&
  2433. ttl > g_NegativeSOACacheTime )
  2434. {
  2435. ttl = g_NegativeSOACacheTime;
  2436. }
  2437. prr->dwTtl = ttl;
  2438. prr->Flags.S.CharSet = DnsCharSetUnicode;
  2439. prr->Flags.S.Section = DNSREC_ANSWER;
  2440. prr->Flags.DW |= DNSREC_NOEXIST;
  2441. if ( status == DNS_ERROR_RCODE_NAME_ERROR )
  2442. {
  2443. prr->wType = DNS_TYPE_ANY;
  2444. }
  2445. else
  2446. {
  2447. prr->wType = wtype;
  2448. }
  2449. Cache_RecordSetAtomic(
  2450. NULL, // default name
  2451. 0, // default type
  2452. prr );
  2453. }
  2454. // failure return from query
  2455. // - nothing to cache
  2456. else
  2457. {
  2458. DNSDBG( QUERY, (
  2459. "Uncacheable error code %d -- no caching for %S, type=%d\n",
  2460. status,
  2461. pname,
  2462. wtype ));
  2463. }
  2464. Done:
  2465. //
  2466. // check cache size to see if garbage collect necessary
  2467. //
  2468. // note we do this only on query caching; this avoids
  2469. // - jamming ourselves in hosts file load
  2470. // - wakeup and grabbing lock between separate sets of query response
  2471. //
  2472. Cache_SizeCheck();
  2473. return status;
  2474. }
  2475. //
  2476. // End ncache.c
  2477. //