1436 lines
30 KiB
C
1436 lines
30 KiB
C
/*++
|
||
|
||
Copyright (c) 1997-2000 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
cache.c
|
||
|
||
Abstract:
|
||
|
||
DNS Resolver Service
|
||
|
||
Cache routines
|
||
|
||
Author:
|
||
|
||
Glenn Curtis (glennc) March 1997
|
||
|
||
Revision History:
|
||
|
||
Jim Gilroy (jamesg) February 2000 cleanup
|
||
|
||
--*/
|
||
|
||
|
||
#include "local.h"
|
||
|
||
|
||
//
|
||
// Global Declarations
|
||
//
|
||
|
||
#define IS_STATIC_TTL_RECORD(prr) IS_HOSTS_FILE_RR(prr)
|
||
|
||
|
||
//
|
||
// Cache hash table
|
||
//
|
||
|
||
extern PCACHE_ENTRY * g_HashTable;
|
||
extern DWORD g_CacheEntryCount;
|
||
extern DWORD g_CacheRecordSetCount;
|
||
extern DWORD g_CurrentCacheTime;
|
||
|
||
#define INITIAL_CACHE_HEAP_SIZE (16*1024)
|
||
|
||
|
||
//
|
||
// Compute a hash table index value for a string
|
||
//
|
||
|
||
#define EOS (L'\0')
|
||
|
||
#define COMPUTE_STRING_HASH_1( _String, _ulHashTableSize, _lpulHash ) \
|
||
{ \
|
||
PWCHAR p; \
|
||
ULOND h = 0, g; \
|
||
\
|
||
for ( p = _String; *p != EOS; p = p + 1 ) \
|
||
{ \
|
||
h = ( h << 4 ) + (DWORD) (*p); \
|
||
if ( g = h&0xf0000000 ) \
|
||
{ \
|
||
h = h ^ ( g >> 24 ); \
|
||
h = h ^ g; \
|
||
} \
|
||
} \
|
||
*_lpulHash = h % _ulHashTableSize; \
|
||
}
|
||
|
||
|
||
//
|
||
// Compute a hash table index value for a string
|
||
// which is invairant to case
|
||
//
|
||
#define COMPUTE_STRING_HASH_2( _String, _ulHashTableSize, _lpulHash ) \
|
||
{ \
|
||
PWCHAR _p = _String; \
|
||
PWCHAR _ep = _p + wcslen( _String ); \
|
||
ULONG h = 0; \
|
||
\
|
||
while( _p < _ep ) \
|
||
{ \
|
||
h <<= 1; \
|
||
h ^= *_p++; \
|
||
} \
|
||
\
|
||
*_lpulHash = h % _ulHashTableSize; \
|
||
}
|
||
|
||
|
||
//
|
||
// Private prototypes
|
||
//
|
||
|
||
BOOL
|
||
IsInvalidNegativeCacheEntry(
|
||
IN PDNS_RECORD
|
||
);
|
||
|
||
PDNS_RECORD
|
||
BuildDNSServerRecord(
|
||
IN IP_ADDRESS Address
|
||
);
|
||
|
||
PDNS_RECORD
|
||
BuildLocalAddressRecords(
|
||
IN PSTR Name
|
||
);
|
||
|
||
BOOL
|
||
IsLocalAddress(
|
||
IN IP_ADDRESS Ip
|
||
);
|
||
|
||
//
|
||
// From ncache.c
|
||
//
|
||
|
||
BOOL
|
||
makeCannonicalCacheName(
|
||
OUT PWCHAR pNameBuffer,
|
||
IN DWORD BufferLength,
|
||
IN PWSTR pName,
|
||
IN DWORD NameLength OPTIONAL
|
||
);
|
||
|
||
DWORD
|
||
getHashIndex(
|
||
IN PWSTR pName,
|
||
IN DWORD NameLength OPTIONAL
|
||
);
|
||
|
||
//
|
||
// Define to new routine
|
||
//
|
||
|
||
#define PrepareRecordSetForRpc(prr) Cache_RestoreRecordListForRpc(prr)
|
||
|
||
#define IsCacheTtlStillValid(prr) Cache_IsRecordTtlValid(prr)
|
||
|
||
#define GetCacheLock() LOCK_CACHE()
|
||
#define ReleaseCacheLock() UNLOCK_CACHE()
|
||
|
||
|
||
|
||
//
|
||
// Cache entry routines
|
||
//
|
||
|
||
PCACHE_ENTRY
|
||
CreateCacheEntry(
|
||
IN PWSTR pName,
|
||
IN BOOL fLoadingHostsFile
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Create cache entry, including allocation.
|
||
|
||
Arguments:
|
||
|
||
pName -- name to create entry for
|
||
|
||
fLoadingHostsFile -- TRUE if loading from host file; FALSE otherwise
|
||
|
||
Return Value:
|
||
|
||
Ptr to newly allocated cache entry.
|
||
NULL on error.
|
||
|
||
--*/
|
||
{
|
||
ULONG index = 0;
|
||
PCACHE_ENTRY pentry = NULL;
|
||
DWORD nameLength;
|
||
PWCHAR pnameCache = NULL;
|
||
|
||
DNSDBG( TRACE, (
|
||
"CreateCacheEntry( %S, hostfile=%d )\n",
|
||
pName,
|
||
fLoadingHostsFile ));
|
||
|
||
if ( !pName || !g_HashTable )
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// build cache entry
|
||
//
|
||
// DCR_PERF: alloc for name within entry?
|
||
//
|
||
|
||
pentry = (PCACHE_ENTRY) CACHE_HEAP_ALLOC_ZERO( sizeof(CACHE_ENTRY) );
|
||
if ( !pentry )
|
||
{
|
||
goto Fail;
|
||
}
|
||
|
||
//
|
||
// build the name
|
||
//
|
||
|
||
nameLength = wcslen( pName );
|
||
|
||
pnameCache = CACHE_HEAP_ALLOC_ZERO( sizeof(WCHAR) * (nameLength + 1) );
|
||
if ( !pnameCache )
|
||
{
|
||
goto Fail;
|
||
}
|
||
|
||
if ( !makeCannonicalCacheName(
|
||
pnameCache,
|
||
nameLength+1,
|
||
pName,
|
||
nameLength ) )
|
||
{
|
||
goto Fail;
|
||
}
|
||
|
||
pentry->pName = pnameCache;
|
||
|
||
pentry->fHostsFileEntry = fLoadingHostsFile;
|
||
|
||
//
|
||
// insert cache entry into cache -- first entry in bucket
|
||
//
|
||
|
||
index = getHashIndex( pnameCache, 0 );
|
||
pentry->pNext = g_HashTable[ index ];
|
||
g_HashTable[ index ] = pentry;
|
||
g_CacheEntryCount++;
|
||
|
||
|
||
if ( fLoadingHostsFile )
|
||
{
|
||
ResizeCacheBucket( index, &g_CacheHashTableBucketSize );
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Trim the hash bucket to keep the number of entries below
|
||
// g_CacheHashTableBucketSize.
|
||
//
|
||
// DCR: goofy cache limit
|
||
// DCR: cache limit should be total size above hosts file
|
||
// not individual buckets
|
||
// monitor thread should just wake up and and clear dead
|
||
// wood from cache
|
||
//
|
||
|
||
TrimCacheBucket( index, g_CacheHashTableBucketSize, TRUE );
|
||
}
|
||
|
||
return pentry;
|
||
|
||
|
||
Fail:
|
||
|
||
// dump entry
|
||
|
||
if ( pentry )
|
||
{
|
||
CACHE_HEAP_FREE( pentry );
|
||
}
|
||
if ( pnameCache )
|
||
{
|
||
CACHE_HEAP_FREE( pnameCache );
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
|
||
//raw
|
||
VOID
|
||
FreeCacheEntry(
|
||
IN OUT PCACHE_ENTRY pEntry
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Free cache entry.
|
||
|
||
Arguments:
|
||
|
||
pEntry -- cache entry to free
|
||
|
||
Globals:
|
||
|
||
g_CacheEntryCount -- decremented appropriately
|
||
|
||
g_NumberOfRecordsInCache -- decremented appropriately
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
register INT iter;
|
||
|
||
DNSDBG( TRACE, (
|
||
"FreeCacheEntry( %p )\n",
|
||
pEntry ));
|
||
|
||
//
|
||
// free entry
|
||
// - name
|
||
// - records
|
||
// - entry itself
|
||
//
|
||
// QUESTION: are global counters safe? should lock?
|
||
//
|
||
|
||
if ( pEntry )
|
||
{
|
||
for ( iter = 0;
|
||
iter < BASE_NUMBER_OF_RECORDS;
|
||
iter++ )
|
||
{
|
||
if ( pEntry->Records[iter] )
|
||
{
|
||
Dns_RecordListFree( pEntry->Records[iter] );
|
||
g_CacheRecordSetCount--;
|
||
}
|
||
}
|
||
|
||
if ( pEntry->pName )
|
||
{
|
||
CACHE_HEAP_FREE( pEntry->pName );
|
||
}
|
||
|
||
#if 0
|
||
if ( pEntry->pNext )
|
||
{
|
||
DNSLOG_F1( "FreeCacheEntry is deleting an entry that still points to other entries!" );
|
||
}
|
||
#endif
|
||
|
||
CACHE_HEAP_FREE( pEntry );
|
||
g_CacheEntryCount--;
|
||
}
|
||
}
|
||
|
||
|
||
//raw
|
||
VOID
|
||
AddRecordToCacheEntry(
|
||
IN OUT PCACHE_ENTRY pEntry,
|
||
IN PDNS_RECORD pRecord,
|
||
IN BOOL fFlushExisting,
|
||
IN BOOL fLoadingHostsFile
|
||
)
|
||
{
|
||
WORD iter;
|
||
WORD type = pRecord->wType;
|
||
|
||
|
||
DNSDBG( TRACE, (
|
||
"AddRecordToCacheEntry( e=%p, rr=%p, type=%d )\n",
|
||
pEntry,
|
||
pRecord,
|
||
type ));
|
||
|
||
//
|
||
// don't overwrite host file entries
|
||
//
|
||
|
||
if ( !fLoadingHostsFile &&
|
||
pEntry->fHostsFileEntry &&
|
||
( type == DNS_TYPE_A ||
|
||
type == DNS_TYPE_PTR ) )
|
||
{
|
||
return;
|
||
}
|
||
|
||
//
|
||
// clean up existing records at node
|
||
// - remove stale records
|
||
// - remove records of same type UNLESS not flushing existing
|
||
//
|
||
|
||
for ( iter = 0;
|
||
iter < BASE_NUMBER_OF_RECORDS;
|
||
iter++ )
|
||
{
|
||
PDNS_RECORD prrExistSet = pEntry->Records[iter];
|
||
|
||
//
|
||
// skip sets of different type that are still valid
|
||
//
|
||
|
||
if ( !prrExistSet ||
|
||
( prrExistSet->wType != type &&
|
||
IsCacheTtlStillValid( prrExistSet ) ) )
|
||
{
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// dump
|
||
// - stale records
|
||
// - records of same type (when flush existing)
|
||
//
|
||
// note, that previous test means we're here with same
|
||
// type record OR record is invalid
|
||
//
|
||
|
||
if ( fFlushExisting ||
|
||
pEntry->Records[iter]->wType != type )
|
||
{
|
||
Dns_RecordListFree( prrExistSet );
|
||
pEntry->Records[iter] = NULL;
|
||
g_CacheRecordSetCount--;
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// NOT flushing AND matching type -- host file load case
|
||
// => add new record to list
|
||
//
|
||
// the way this works
|
||
// - start at "record" which is addr of record ptr entry
|
||
// making pNext field the actual pointer
|
||
// - delete duplicates
|
||
// - tack new RR on end
|
||
// - blow away new RR name if existing record
|
||
//
|
||
//
|
||
// DCR: should have simple "make cache RR set" function that
|
||
// handles name and TTL issues
|
||
//
|
||
// DCR: broken if non-flush load hits wire data; wire data
|
||
// may have multiple RR sets
|
||
//
|
||
|
||
else
|
||
{
|
||
PDNS_RECORD prr;
|
||
PDNS_RECORD prrPrev = (PDNS_RECORD) &pEntry->Records[iter];
|
||
|
||
while ( prr = prrPrev->pNext )
|
||
{
|
||
// matches existing record?
|
||
// - cut existing record from list and free
|
||
|
||
if ( Dns_RecordCompare( prr, pRecord ) )
|
||
{
|
||
prrPrev->pNext = prr->pNext;
|
||
prr->pNext = NULL;
|
||
Dns_RecordListFree( prr );
|
||
}
|
||
else
|
||
{
|
||
prrPrev = prr;
|
||
}
|
||
}
|
||
|
||
//
|
||
// tack entry on to end
|
||
// - if existing records of type delete name
|
||
//
|
||
|
||
if ( prrPrev != (PDNS_RECORD)&pEntry->Records[iter] )
|
||
{
|
||
if ( IS_FREE_OWNER(pRecord) )
|
||
{
|
||
RECORD_HEAP_FREE( pRecord->pName );
|
||
pRecord->pName = NULL;
|
||
}
|
||
}
|
||
prrPrev->pNext = pRecord;
|
||
return;
|
||
}
|
||
}
|
||
|
||
//
|
||
// put record into cache entry
|
||
// - note record list may now contain pre-existing records
|
||
// from above
|
||
//
|
||
|
||
for ( iter = 0;
|
||
iter < BASE_NUMBER_OF_RECORDS;
|
||
iter++ )
|
||
{
|
||
if ( pEntry->Records[iter] == NULL )
|
||
{
|
||
pEntry->Records[iter] = pRecord;
|
||
g_CacheRecordSetCount++;
|
||
return;
|
||
}
|
||
}
|
||
|
||
//
|
||
// must find free spot in list -- get rid of last one
|
||
//
|
||
// DCR: realloc and push out if need more space
|
||
//
|
||
|
||
Dns_RecordListFree( pEntry->Records[BASE_NUMBER_OF_RECORDS-1] );
|
||
pEntry->Records[BASE_NUMBER_OF_RECORDS-1] = pRecord;
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
FlushCacheEntry(
|
||
IN PWSTR pName
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Flush cache entry corresponding to a name.
|
||
|
||
Arguments:
|
||
|
||
pName -- name to delete from the cache
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful.
|
||
ErrorCode on failure.
|
||
|
||
--*/
|
||
{
|
||
ULONG index = 0;
|
||
PCACHE_ENTRY pentry = NULL;
|
||
PCACHE_ENTRY pprevEntry = NULL;
|
||
WCHAR hashName[ DNS_MAX_NAME_BUFFER_LENGTH+4 ];
|
||
|
||
|
||
DNSDBG( TRACE, (
|
||
"FlushCacheEntry( %S )\n",
|
||
pName ));
|
||
|
||
if ( !g_HashTable )
|
||
{
|
||
return;
|
||
}
|
||
|
||
//
|
||
// build cache name
|
||
//
|
||
|
||
if ( !makeCannonicalCacheName(
|
||
hashName,
|
||
DNS_MAX_NAME_BUFFER_LENGTH,
|
||
pName,
|
||
0 ) )
|
||
{
|
||
return;
|
||
}
|
||
|
||
|
||
//
|
||
// find entry in cache
|
||
//
|
||
// DCR: consider a find routine that lets us cut
|
||
// - return prev pointer
|
||
// - or know we're at front of list and have index
|
||
//
|
||
|
||
index = getHashIndex( hashName, 0 );
|
||
|
||
GetCacheLock();
|
||
|
||
pentry = g_HashTable[ index ];
|
||
|
||
while( pentry )
|
||
{
|
||
if ( DnsNameCompare_W( hashName, pentry->pName ) )
|
||
{
|
||
//
|
||
// Can't purge cache entries that hold records that were
|
||
// read in from the hosts file . . .
|
||
//
|
||
if ( pentry->fHostsFileEntry )
|
||
{
|
||
goto Done;
|
||
}
|
||
|
||
//
|
||
// Found it!
|
||
//
|
||
if ( pprevEntry )
|
||
{
|
||
//
|
||
// There is an entry in front of the one we found
|
||
// in the list.
|
||
//
|
||
pprevEntry->pNext = pentry->pNext;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// There isn't an entry in front of the one we found.
|
||
//
|
||
g_HashTable[ index ] = pentry->pNext;
|
||
}
|
||
|
||
pentry->pNext = NULL;
|
||
FreeCacheEntry( pentry );
|
||
goto Done;
|
||
}
|
||
|
||
pprevEntry = pentry;
|
||
pentry = pentry->pNext;
|
||
}
|
||
|
||
Done:
|
||
|
||
ReleaseCacheLock();
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
FlushCacheEntryRecord(
|
||
IN PWSTR pName,
|
||
IN WORD Type
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Flush cached records corresponding to a name and type.
|
||
|
||
Arguments:
|
||
|
||
pName -- name of records to delete
|
||
|
||
Type -- type of records to delete
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful.
|
||
ErrorCode on failure.
|
||
|
||
--*/
|
||
{
|
||
WORD iter;
|
||
PCACHE_ENTRY pentry;
|
||
|
||
DNSDBG( TRACE, (
|
||
"FlushCacheEntryRecord( %S, %d )\n",
|
||
pName,
|
||
Type ));
|
||
|
||
//
|
||
// find entry in cache
|
||
//
|
||
|
||
GetCacheLock();
|
||
|
||
pentry = FindCacheEntry( pName );
|
||
if ( !pentry )
|
||
{
|
||
goto Done;
|
||
}
|
||
|
||
//
|
||
// free records for entry
|
||
// - not from hosts file
|
||
// - matches type
|
||
// - or type zero (for deleting ALL records)
|
||
// - or name error
|
||
//
|
||
|
||
for ( iter = 0;
|
||
iter < BASE_NUMBER_OF_RECORDS;
|
||
iter++ )
|
||
{
|
||
PDNS_RECORD prr = pentry->Records[iter];
|
||
|
||
if ( prr &&
|
||
! IS_HOSTS_FILE_RR(prr) &&
|
||
( prr->wType == Type ||
|
||
Type == 0 ||
|
||
( prr->wType == DNS_TYPE_ANY &&
|
||
prr->wDataLength == 0 ) ) )
|
||
{
|
||
Dns_RecordListFree( prr );
|
||
pentry->Records[iter] = NULL;
|
||
g_CacheRecordSetCount--;
|
||
}
|
||
}
|
||
|
||
Done:
|
||
|
||
ReleaseCacheLock();
|
||
}
|
||
|
||
|
||
//raw
|
||
VOID
|
||
TrimCache(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Trim back cache. Trim every bucket in cache.
|
||
|
||
Arguments:
|
||
|
||
None
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
WORD hashIter;
|
||
|
||
if ( !g_HashTable )
|
||
return;
|
||
|
||
for ( hashIter = 0;
|
||
hashIter < g_CacheHashTableSize;
|
||
hashIter++ )
|
||
{
|
||
TrimCacheBucket( hashIter,
|
||
g_CacheHashTableBucketSize,
|
||
FALSE );
|
||
}
|
||
}
|
||
|
||
|
||
//raw
|
||
VOID
|
||
TrimCacheBucket(
|
||
IN ULONG Index,
|
||
IN DWORD dwBucketSize,
|
||
IN BOOL fSkipFirst
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Trim back cache bucket.
|
||
|
||
Arguments:
|
||
|
||
Index -- Index of hash bucket to trim.
|
||
|
||
dwBucketSize -- size to trim bucket to
|
||
|
||
fSkipFirst -- skip first entry while triming
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
PCACHE_ENTRY pentry = g_HashTable[ Index ];
|
||
PCACHE_ENTRY pprevEntry = NULL;
|
||
DWORD EntryCount = 0;
|
||
|
||
|
||
//
|
||
// skip first entry in bucket
|
||
//
|
||
|
||
if ( pentry && fSkipFirst )
|
||
{
|
||
pprevEntry = pentry;
|
||
pentry = pentry->pNext;
|
||
EntryCount++;
|
||
}
|
||
|
||
while ( pentry )
|
||
{
|
||
PCACHE_ENTRY pnextEntry = pentry->pNext;
|
||
WORD recordCount = 0;
|
||
WORD iter;
|
||
|
||
for ( iter = 0;
|
||
iter < BASE_NUMBER_OF_RECORDS;
|
||
iter++ )
|
||
{
|
||
//
|
||
// delete stale entries
|
||
// - not from host file
|
||
// - TTL expired
|
||
//
|
||
|
||
if ( ! pentry->fHostsFileEntry &&
|
||
pentry->Records[iter] &&
|
||
!IsCacheTtlStillValid( pentry->Records[iter] ) )
|
||
{
|
||
Dns_RecordListFree( pentry->Records[iter] );
|
||
pentry->Records[iter] = NULL;
|
||
g_CacheRecordSetCount--;
|
||
recordCount++;
|
||
}
|
||
else if ( !pentry->Records[iter] )
|
||
{
|
||
recordCount++;
|
||
}
|
||
}
|
||
|
||
//
|
||
// if entry is empty -- delete
|
||
//
|
||
|
||
if ( recordCount == BASE_NUMBER_OF_RECORDS )
|
||
{
|
||
if ( pprevEntry )
|
||
pprevEntry->pNext = pentry->pNext;
|
||
else
|
||
g_HashTable[ Index ] = pentry->pNext;
|
||
|
||
pentry->pNext = NULL;
|
||
|
||
FreeCacheEntry( pentry );
|
||
}
|
||
else
|
||
{
|
||
pprevEntry = pentry;
|
||
EntryCount++;
|
||
}
|
||
|
||
pentry = pnextEntry;
|
||
}
|
||
|
||
//
|
||
// if still too many entries we need to delete
|
||
//
|
||
// DCR: cache size limitation is weak
|
||
// - ideally time out based on query, for instances pick an interval
|
||
// say ten minutes and time out all older stuff
|
||
// - or better yet do LRU timeout
|
||
//
|
||
|
||
if ( EntryCount >= dwBucketSize )
|
||
{
|
||
PCACHE_ENTRY pPrevPurgeEntry = NULL;
|
||
PCACHE_ENTRY pPurgeEntry = NULL;
|
||
|
||
|
||
pentry = g_HashTable[ Index ];
|
||
pprevEntry = NULL;
|
||
|
||
//
|
||
// skip first when required
|
||
//
|
||
|
||
if ( pentry && fSkipFirst )
|
||
{
|
||
pprevEntry = pentry;
|
||
pentry = pentry->pNext;
|
||
}
|
||
|
||
//
|
||
// Loop through all cache entries looking for potential ones
|
||
// to get rid of, the last one in the list will be the one that
|
||
// is purged (Least Recently Used).
|
||
//
|
||
while ( pentry )
|
||
{
|
||
if ( ! pentry->fHostsFileEntry )
|
||
{
|
||
//
|
||
// Found a potential entry to purge
|
||
//
|
||
pPrevPurgeEntry = pprevEntry;
|
||
pPurgeEntry = pentry;
|
||
}
|
||
|
||
pprevEntry = pentry;
|
||
pentry = pentry->pNext;
|
||
}
|
||
|
||
//
|
||
// Now get rid of the entry that was found
|
||
//
|
||
// FIXME: how many times are we going to clone this code???
|
||
//
|
||
|
||
if ( pPurgeEntry )
|
||
{
|
||
WORD iter;
|
||
|
||
if ( pPrevPurgeEntry )
|
||
{
|
||
pPrevPurgeEntry->pNext = pPurgeEntry->pNext;
|
||
}
|
||
else
|
||
{
|
||
g_HashTable[ Index ] = pPurgeEntry->pNext;
|
||
}
|
||
|
||
pPurgeEntry->pNext = NULL;
|
||
|
||
FreeCacheEntry( pPurgeEntry );
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
//raw
|
||
VOID
|
||
ResizeCacheBucket(
|
||
IN ULONG Index,
|
||
IN PDWORD pdwBucketSize
|
||
)
|
||
{
|
||
PCACHE_ENTRY pentry = g_HashTable[ Index ];
|
||
DWORD Count = 0;
|
||
|
||
while ( pentry )
|
||
{
|
||
Count++;
|
||
pentry = pentry->pNext;
|
||
}
|
||
|
||
if ( (*pdwBucketSize - Count) < 10 )
|
||
{
|
||
*pdwBucketSize += 10;
|
||
}
|
||
}
|
||
|
||
|
||
//raw
|
||
PCACHE_ENTRY
|
||
FindCacheEntry(
|
||
IN PWSTR pName
|
||
)
|
||
{
|
||
ULONG index;
|
||
PCACHE_ENTRY pentry;
|
||
PCACHE_ENTRY pprevEntry = NULL;
|
||
WCHAR hashName[ DNS_MAX_NAME_BUFFER_LENGTH+4 ];
|
||
|
||
if ( !g_HashTable )
|
||
{
|
||
return NULL;
|
||
}
|
||
if ( !pName )
|
||
{
|
||
DNS_ASSERT( FALSE );
|
||
return NULL;
|
||
}
|
||
|
||
DNSDBG( TRACE, (
|
||
"FindCacheEntry( %S )\n",
|
||
pName ));
|
||
|
||
//
|
||
// build cache name
|
||
// - if invalid (too long) bail
|
||
//
|
||
|
||
if ( !makeCannonicalCacheName(
|
||
hashName,
|
||
DNS_MAX_NAME_BUFFER_LENGTH,
|
||
pName,
|
||
0 ) )
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// find entry in cache
|
||
//
|
||
|
||
index = getHashIndex( hashName, 0 );
|
||
|
||
pentry = g_HashTable[ index ];
|
||
|
||
|
||
DNSDBG( TRACE, (
|
||
"in FindCacheEntry\n"
|
||
"\tname = %S\n"
|
||
"\tindex = %d\n"
|
||
"\tpentry = %p\n",
|
||
hashName,
|
||
index,
|
||
pentry ));
|
||
|
||
while( pentry )
|
||
{
|
||
if ( DnsNameCompare_W( hashName, pentry->pName ) )
|
||
{
|
||
//
|
||
// found entry
|
||
// - move to front, if not already there
|
||
|
||
if ( pprevEntry )
|
||
{
|
||
pprevEntry->pNext = pentry->pNext;
|
||
pentry->pNext = g_HashTable[ index ];
|
||
g_HashTable[ index ] = pentry;
|
||
}
|
||
break;
|
||
}
|
||
ELSE
|
||
{
|
||
DNSDBG( OFF, (
|
||
"in FindCacheEntry -- failed name compare\n"
|
||
"\tout name = %S\n"
|
||
"\tpentry = %p\n"
|
||
"\tname = %S\n",
|
||
hashName,
|
||
pentry,
|
||
pentry->pName ));
|
||
}
|
||
|
||
pprevEntry = pentry;
|
||
pentry = pentry->pNext;
|
||
}
|
||
|
||
DNSDBG( TRACE, (
|
||
"Leave FindCacheEntry\n"
|
||
"\tname = %S\n"
|
||
"\tindex = %d\n"
|
||
"\tpentry = %p\n",
|
||
hashName,
|
||
index,
|
||
pentry ));
|
||
|
||
return pentry;
|
||
}
|
||
|
||
|
||
|
||
|
||
PCACHE_ENTRY
|
||
FindOrCreateCacheEntry(
|
||
IN PWSTR pName,
|
||
IN BOOL fHostsFile
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Find or create entry for name in cache.
|
||
|
||
Arguments:
|
||
|
||
pName -- name to find
|
||
|
||
Return Value:
|
||
|
||
Ptr to cache entry -- if successful.
|
||
NULL on failure.
|
||
|
||
--*/
|
||
{
|
||
PCACHE_ENTRY pentry;
|
||
|
||
DNSDBG( TRACE, (
|
||
"FindOrCreateCacheEntry( %S, hosts=%d )\n",
|
||
pName,
|
||
fHostsFile ));
|
||
|
||
//
|
||
// find entry?
|
||
//
|
||
|
||
pentry = FindCacheEntry( pName );
|
||
if ( !pentry )
|
||
{
|
||
pentry = CreateCacheEntry(
|
||
pName,
|
||
fHostsFile
|
||
);
|
||
}
|
||
|
||
DNSDBG( TRACE, (
|
||
"Leave FindOrCreateCacheEntry( %S, hosts=%d ) => %p\n",
|
||
pName,
|
||
fHostsFile,
|
||
pentry ));
|
||
|
||
return pentry;
|
||
}
|
||
|
||
|
||
|
||
|
||
PDNS_RECORD
|
||
FindCacheEntryRecord(
|
||
IN PCACHE_ENTRY pEntry,
|
||
IN WORD Type
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Find entry in cache.
|
||
|
||
Arguments:
|
||
|
||
pEntry -- cache entry to check
|
||
|
||
Type -- record type to find
|
||
|
||
Return Value:
|
||
|
||
Ptr to record set of desired type -- if found.
|
||
NULL if not found.
|
||
|
||
--*/
|
||
{
|
||
WORD iter;
|
||
PDNS_RECORD prr;
|
||
|
||
DNSDBG( TRACE, (
|
||
"FindCacheEntryRecord( %p, type=%d )\n",
|
||
pEntry,
|
||
Type ));
|
||
|
||
//
|
||
// check all the records at the cache entry
|
||
//
|
||
|
||
for ( iter = 0;
|
||
iter < BASE_NUMBER_OF_RECORDS;
|
||
iter++ )
|
||
{
|
||
prr = pEntry->Records[iter];
|
||
|
||
if ( prr &&
|
||
( prr->wType == Type ||
|
||
prr->wType == DNS_TYPE_CNAME ||
|
||
( prr->wType == DNS_TYPE_ANY &&
|
||
prr->wDataLength == 0 ) ) )
|
||
{
|
||
//
|
||
// if expired dump RR list
|
||
//
|
||
// DCR: if different RR sets in list, then TTL check here not sufficient
|
||
//
|
||
// DCR: functionalize this
|
||
//
|
||
|
||
if ( !IsCacheTtlStillValid( prr ) ||
|
||
IsInvalidNegativeCacheEntry( prr ) )
|
||
{
|
||
DNSDBG( TRACE, (
|
||
"Whacking timed out record %p at cache entry %p\n",
|
||
prr,
|
||
pEntry ));
|
||
Dns_RecordListFree( prr );
|
||
pEntry->Records[iter] = NULL;
|
||
g_CacheRecordSetCount--;
|
||
prr = NULL;
|
||
goto Done;
|
||
}
|
||
|
||
//
|
||
// If the cached record is a CNAME, walk the record
|
||
// list to see if the CNAME chain points to a record
|
||
// that is the type we are looking for.
|
||
//
|
||
|
||
if ( prr->wType == DNS_TYPE_CNAME &&
|
||
Type != DNS_TYPE_CNAME )
|
||
{
|
||
PDNS_RECORD prrChain = prr->pNext;
|
||
|
||
while ( prrChain )
|
||
{
|
||
if ( prrChain->wType == Type )
|
||
{
|
||
// chain to desired type -- take RR set
|
||
goto Done;
|
||
}
|
||
prrChain = prrChain->pNext;
|
||
}
|
||
prr = NULL;
|
||
goto Done;
|
||
}
|
||
|
||
// take RR set
|
||
goto Done;
|
||
}
|
||
}
|
||
// type not found
|
||
prr = NULL;
|
||
|
||
Done:
|
||
|
||
DNSDBG( TRACE, (
|
||
"Leave FindCacheEntryRecord() => %p\n",
|
||
prr ));
|
||
|
||
return prr;
|
||
}
|
||
|
||
|
||
|
||
PDNS_RECORD
|
||
FindCacheRecords(
|
||
IN PWSTR pwsName,
|
||
IN WORD wType
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Find records of given name and type in cache.
|
||
|
||
Arguments:
|
||
|
||
pwsName -- name
|
||
|
||
Type -- record type to find
|
||
|
||
Return Value:
|
||
|
||
Ptr to record set of desired type -- if found.
|
||
NULL if not found.
|
||
|
||
--*/
|
||
{
|
||
PCACHE_ENTRY pentry;
|
||
PDNS_RECORD prr = NULL;
|
||
|
||
DNSDBG( TRACE, (
|
||
"FindCacheRecords( %S, type=%d )\n",
|
||
pwsName,
|
||
wType ));
|
||
|
||
pentry = FindCacheEntry( pwsName );
|
||
if ( pentry )
|
||
{
|
||
prr = FindCacheEntryRecord(
|
||
pentry,
|
||
wType );
|
||
}
|
||
|
||
DNSDBG( TRACE, (
|
||
"Leave FindCacheRecords( %S, type=%d ) => %p\n",
|
||
pwsName,
|
||
wType,
|
||
prr ));
|
||
|
||
return prr;
|
||
}
|
||
|
||
|
||
//raw
|
||
VOID
|
||
CacheAnyAdditionalRecords(
|
||
IN OUT PDNS_RECORD pRecord,
|
||
IN BOOL fHostFile
|
||
)
|
||
{
|
||
BOOL fcnameAnswer = FALSE;
|
||
PCACHE_ENTRY pentry = NULL;
|
||
PDNS_RECORD pnextRR = pRecord;
|
||
PDNS_RECORD prr;
|
||
|
||
|
||
DNSDBG( TRACE, (
|
||
"CacheAnyAdditionalRecords( rr=%p, hosts=%d )\n",
|
||
pRecord,
|
||
fHostFile ));
|
||
|
||
//
|
||
// cache "additional" records
|
||
//
|
||
// this is really cache additional OR
|
||
// cache the answer records for a CNAME at that CNAME
|
||
//
|
||
// background: Glenn's caching paradigm was to cache all answer
|
||
// data at the queried name in the API call (name might be short).
|
||
// However, not caching the CNAME data can cause problems, so this
|
||
// was tacked on.
|
||
//
|
||
// For CNAME caching we throw away the CNAMEs themselves and just
|
||
// cache the actually data (address) records at the CNAME node.
|
||
//
|
||
|
||
//
|
||
// cache additional records
|
||
//
|
||
|
||
while ( prr = pnextRR )
|
||
{
|
||
BOOL fcacheSet = FALSE;
|
||
|
||
pnextRR = Dns_RecordSetDetach( prr );
|
||
|
||
//
|
||
// do NOT cache
|
||
// - answer records for queried name (not CNAME)
|
||
// - CNAME records when doing caching of answer data under CNAME
|
||
// - authority section records (NS, SOA, etc)
|
||
// - OPT records
|
||
//
|
||
// DCR: have some sort of "cacheable type" test
|
||
// which would screen out any transactional records
|
||
//
|
||
|
||
if ( fHostFile )
|
||
{
|
||
fcacheSet = TRUE;
|
||
}
|
||
else if ( prr->Flags.S.Section == DNSREC_ANSWER )
|
||
{
|
||
if ( prr->wType == DNS_TYPE_CNAME )
|
||
{
|
||
fcnameAnswer = TRUE;
|
||
}
|
||
else if ( fcnameAnswer )
|
||
{
|
||
fcacheSet = TRUE;
|
||
}
|
||
}
|
||
else if ( prr->Flags.S.Section == DNSREC_ADDITIONAL )
|
||
{
|
||
if ( prr->wType != DNS_TYPE_OPT )
|
||
{
|
||
fcacheSet = TRUE;
|
||
}
|
||
}
|
||
|
||
if ( !fcacheSet )
|
||
{
|
||
Dns_RecordListFree( prr );
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// cache the set
|
||
//
|
||
// flip the section field to "Answer" section
|
||
//
|
||
// DCR: section caching?
|
||
//
|
||
// note: section fields in cache indicate whether
|
||
// answer data (or additional) once out of
|
||
// cache;
|
||
// this is necessary since we cache everything
|
||
// at node and return it in one RR list; we'd
|
||
// to change must
|
||
// - return in different lists with some indication
|
||
// in cache of what's what
|
||
// OR
|
||
// - another indication of what's what
|
||
//
|
||
|
||
pentry = FindOrCreateCacheEntry(
|
||
prr->pName,
|
||
fHostFile
|
||
);
|
||
if ( !pentry )
|
||
{
|
||
Dns_RecordListFree( prr );
|
||
}
|
||
else
|
||
{
|
||
//if ( !fHostFile )
|
||
// currently HostFile entries get answer too
|
||
{
|
||
PDNS_RECORD ptemp = prr;
|
||
while ( ptemp )
|
||
{
|
||
ptemp->Flags.S.Section = DNSREC_ANSWER;
|
||
ptemp = ptemp->pNext;
|
||
}
|
||
}
|
||
|
||
PrepareRecordSetForCache( prr );
|
||
|
||
AddRecordToCacheEntry(
|
||
pentry,
|
||
prr,
|
||
! fHostFile, // flush if NOT hostfile load
|
||
fHostFile
|
||
);
|
||
}
|
||
}
|
||
|
||
DNSDBG( TRACE, ( "Leave CacheAnyAdditionalRecords()\n" ));
|
||
}
|
||
|
||
|
||
//raw
|
||
BOOL
|
||
IsInvalidNegativeCacheEntry(
|
||
IN PDNS_RECORD pRecord
|
||
)
|
||
{
|
||
DWORD cacheTime;
|
||
|
||
if ( pRecord->wDataLength != 0 )
|
||
{
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// recover record cache time
|
||
//
|
||
|
||
cacheTime = g_MaxNegativeCacheTtl;
|
||
if ( pRecord->wType == DNS_TYPE_SOA )
|
||
{
|
||
cacheTime = g_NegativeSOACacheTime;
|
||
}
|
||
|
||
// should NEVER have absolute time less than time we cached for
|
||
|
||
ASSERT( cacheTime <= pRecord->dwTtl );
|
||
|
||
//
|
||
// check if last PnP AFTER this record was cached
|
||
//
|
||
|
||
DNSDBG( TRACE, (
|
||
"IsInvalidNegativeCacheEntry( rr=%p ) => %d\n",
|
||
pRecord,
|
||
( g_TimeOfLastPnPUpdate > (pRecord->dwTtl - cacheTime))
|
||
));
|
||
|
||
return( g_TimeOfLastPnPUpdate > (pRecord->dwTtl - cacheTime) );
|
||
}
|
||
|
||
//
|
||
// End cache.c
|
||
//
|
||
|