mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1435 lines
30 KiB
1435 lines
30 KiB
/*++
|
|
|
|
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
|
|
//
|
|
|