|
|
/*++
Copyright (c) 2000-2001 Microsoft Corporation
Module Name:
ops.c
Abstract:
DNS Resolver Service.
Remote APIs to resolver service.
Author:
Jim Gilroy (jamesg) November 2000
Revision History:
--*/
#include "local.h"
//
// Max number to enum at a time
//
#define MAX_CACHE_ENUM_COUNT (500)
//
// Enum operations
//
// Tag is DWORD with
// - high word the hash bucket index
// - low word the entry count
//
#define MakeEnumTag(h,e) MAKEDWORD( (WORD)e, (WORD)h )
#define HashBucketFromEnumTag(t) HIWORD(t)
#define EntryIndexFromEnumTag(t) LOWORD(t)
#define GetRpcRecords(p,t,pi) (NULL)
#define AllocRpcName( s ) Dns_StringCopyAllocate_W( (s), 0 )
DNS_STATUS R_ResolverEnumCache( IN DNS_RPC_HANDLE Handle, IN PDNS_CACHE_ENUM_REQUEST pRequest, OUT PDNS_CACHE_ENUM * ppEnum ) /*++
Routine Description:
Enumerate entries in cache.
Arguments:
RpcHandle -- RPC handle
pRequest -- ptr to Enum request
ppEnum -- addr to recv pointer to enumeration
Return Value:
ERROR_SUCCESS if successful. ErrorCode on failure to enum.
--*/ { DNS_STATUS status = ERROR_SUCCESS; PDNS_CACHE_ENUM penum = NULL; BOOL flocked = FALSE; DWORD count = 0; DWORD maxCount = 0; DWORD entryCount = 0; DWORD entryStart; WORD typeRequest = 0; DWORD hashStart; DWORD ihash; PDNS_RECORD prr; PDNS_CACHE_ENTRY prpcEntry;
DNSDBG( RPC, ( "\nR_ResolverEnumCache\n" )); DNSLOG_F1( "Resolver - R_ResolverEnumCache" );
if ( !ppEnum || !pRequest ) { return ERROR_INVALID_PARAMETER; } *ppEnum = NULL;
if ( ClientThreadNotAllowedAccess() ) { status = ERROR_ACCESS_DENIED; goto Done; }
//
// allocate desired space
//
maxCount = pRequest->MaxCount; if ( maxCount > MAX_CACHE_ENUM_COUNT ) { maxCount = MAX_CACHE_ENUM_COUNT; }
penum = (PDNS_CACHE_ENUM) RPC_HEAP_ALLOC_ZERO( sizeof(DNS_CACHE_ENUM) + (maxCount * sizeof(DNS_CACHE_ENTRY)) ); if ( !penum ) { status = DNS_ERROR_NO_MEMORY; goto Done; }
//
// read entries starting from EnumTag
//
LOCK_CACHE(); flocked = TRUE; count = 0; typeRequest = pRequest->Type;
hashStart = HashBucketFromEnumTag( pRequest->EnumTag ); entryStart = EntryIndexFromEnumTag( pRequest->EnumTag );
//
// enum next DCR: issue of CNAME here?
//
for ( ihash = hashStart; ihash < g_HashTableSize; ihash++ ) { PCACHE_ENTRY pentry = g_HashTable[ihash]; entryCount = 0;
while ( pentry ) { DWORD index = 0;
// skip any entries in previous enum
if ( ihash == hashStart && entryCount < entryStart ) { pentry = pentry->pNext; entryCount++; continue; }
// write enum entries matching criteria
while( count < maxCount ) { prr = GetRpcRecords( pentry, typeRequest, & index ); if ( !prr ) { break; }
prpcEntry = &penum->EntryArray[count];
prpcEntry->pName = AllocRpcName( pentry->pName ); prpcEntry->wType = typeRequest; count++; }
pentry = pentry->pNext; entryCount++; } }
//
// set return params
// if exhaust cache -- success
// if more data, set termination tag to restart
//
penum->TotalCount = g_EntryCount; penum->EnumCount = count; penum->EnumTagStart = pRequest->EnumTag;
if ( ihash == g_HashTableSize ) { status = ERROR_SUCCESS; } else { status = ERROR_MORE_DATA; penum->EnumTagStop = (DWORD) MAKELONG( entryCount, ihash ); }
*ppEnum = penum;
Done:
UNLOCK_CACHE();
DNSDBG( RPC, ( "Leave R_ResolverEnumCache()\n" "\tstatus = %d\n" "\ttotal count = %d\n" "\ttag start = %p\n" "\ttag end = %p\n" "\tcount = %d\n\n", status, penum->TotalCount, penum->EnumTagStart, penum->EnumTagStop, penum->EnumCount ));
return( status ); }
//
// Cache operations
//
DNS_STATUS R_ResolverFlushCache( IN DNS_RPC_HANDLE Handle ) /*++
Routine Description:
Flush resolver cache.
Arguments:
Handle -- RPC handle
Return Value:
ERROR_SUCCESS if successful. ERROR_ACCESS_DENIED if unable to flush.
--*/ { DNSDBG( RPC, ( "\nR_ResolverFlushCache\n" ));
//
// DCR: flush should have security
//
if ( ClientThreadNotAllowedAccess() ) { DNSLOG_F1( "R_ResolverFlushCache - ERROR_ACCESS_DENIED" ); return ERROR_ACCESS_DENIED; }
//
// flush cache
//
Cache_Flush();
DNSDBG( RPC, ( "Leave R_ResolverFlushCache\n\n" )); return ERROR_SUCCESS; }
DNS_STATUS R_ResolverFlushCacheEntry( IN DNS_RPC_HANDLE Handle, IN PWSTR pwsName, IN WORD wType ) /*++
Routine Description:
Flush data from resolver cache.
Arguments:
Handle -- RPC handle
pwsName -- name to flush (if NULL flush entire cache)
wType -- type to flush; if zero, flush entire entry for name
Return Value:
ERROR_SUCCESS if successful. ERROR_ACCESS_DENIED if unable to flush.
--*/ { DNSLOG_F1( "R_ResolverFlushCacheEntry" ); DNSLOG_F2( " Name : %S", pwsName ); DNSLOG_F2( " Type : %d", wType );
DNSDBG( RPC, ( "R_ResolverFlushCacheEntry\n" "\tName = %p %S\n" "\tType = %d\n", pwsName, pwsName, wType ));
if ( !pwsName ) { return ERROR_INVALID_PARAMETER; }
//
// two levels
// 1) - no type => flush the whole name entry
// 2) - name and type => flush on particular RR set
//
Cache_FlushRecords( pwsName, wType ? FLUSH_LEVEL_NORMAL : FLUSH_LEVEL_WIRE, wType );
DNSDBG( RPC, ( "Leave R_ResolverFlushCacheEntry\n\n" ));
return ERROR_SUCCESS; }
//
// Query API utilities
//
DNS_STATUS ResolverQuery( IN OUT PQUERY_BLOB pBlob ) /*++
Routine Description:
Make the query to DNS server.
Arguments:
pBlob -- query blob
Return Value:
ERROR_SUCCESS if successful response. DNS_INFO_NO_RECORDS on no records for type response. DNS_ERROR_RCODE_NAME_ERROR on name error. DNS_ERROR_INVALID_NAME on bad name. None
--*/ { DNS_STATUS status = ERROR_SUCCESS; PDNS_NETINFO pnetInfo = NULL; BOOL fadapterTimedOut = FALSE; DNS_STATUS statusNetFailure = ERROR_SUCCESS;
DNSDBG( TRACE, ( "ResolverQuery( %S, type=%d, f=%08x )\n", pBlob->pNameOrig, pBlob->wType, pBlob->Flags ));
//
// skip query -- timeouts -- entirely if net down
//
if ( IsKnownNetFailure() ) { status = GetLastError(); DNSLOG_F2( "Not going query since there is a known net failure: 0x%.8X", status ); DNSDBG( ANY, ( "WARNING: known net failure %d, suppressing queries!\n", status )); return status; }
//
// get valid network info
//
pnetInfo = GrabNetworkInfo(); if ( ! pnetInfo ) { DNSDBG( ANY, ( "ERROR: GrabNetworkInfo() failed!\n" )); return DNS_ERROR_NO_DNS_SERVERS; } pBlob->pNetworkInfo = pnetInfo;
//
// cluster filtering setup
//
if ( g_IsServer ) { pBlob->pfnIsClusterIp = IsClusterAddress; }
//
// query
// includes
// - local name check
// - wire query
//
status = Query_Main( pBlob );
statusNetFailure = pBlob->NetFailureStatus;
#if 0
//
// DCR: missing catching intermediate failures
//
//
// reset server priorities on failures
// do here to avoid washing out info in retry with new name
//
if ( status != ERROR_SUCCESS && pnetInfo->ReturnFlags & DNS_FLAG_RESET_SERVER_PRIORITY ) { if ( g_AdapterTimeoutCacheTime && Dns_DisableTimedOutAdapters( pnetInfo ) ) { fadapterTimedOut = TRUE; SetKnownTimedOutAdapter(); } } #endif
//
// success
// - drop message popup count
//
if ( status == ERROR_SUCCESS ) { #if 0
// don't see any point in locking for this crap
// as long as don't decrement anywhere, and even
// then success reset should make it ok
LOCK_NET_FAILURE(); if ( g_MessagePopupStrikes > 0 ) g_MessagePopupStrikes--; UNLOCK_NET_FAILURE(); #endif
g_MessagePopupStrikes = 0; }
//
// network failure condition
// - anything but ERROR_TIMEOUT is net failure
//
// timeout error indicates possible net down condition
// - ping DNS servers
// if down shutdown queries for short interval; this
// eliminates long timeouts in boot up during netdown
// condition
//
// DCR: this is stupid -- ping especially
//
// should just keep a count, if count rises back off;
// why we should do useless query (ping) is beyond me
// rather than just doing another query; only advantage
// of ping is that it should succeed immediately
//
// furthermore any tracking for this that we do do should
// be in single routine saving the network info
//
else if ( statusNetFailure ) { if ( statusNetFailure == ERROR_TIMEOUT ) { #if 0
DWORD iter; BOOL fping = FALSE; PDNS_ADAPTER padapter; for ( iter = 0; iter < pnetInfo->cAdapterCount; iter++ ) { padapter = pnetInfo->AdapterArray[iter]; if ( padapter && g_NetFailureCacheTime && Dns_PingAdapterServers( padapter ) ) { fping = TRUE; break; } } if ( !fping ) { SetKnownNetFailure( status ); } #endif
} else { SetKnownNetFailure( status ); } }
//
// save change in adapter priority
//
if ( pnetInfo->ReturnFlags & RUN_FLAG_RESET_SERVER_PRIORITY ) { UpdateNetworkInfo( pnetInfo ); } else { NetInfo_Free( pnetInfo ); } pBlob->pNetworkInfo = NULL;
DNSDBG( QUERY, ( "Leave ResolverQuery() => %d\n", status ));
IF_DNSDBG( QUERY ) { DnsDbg_QueryBlob( "Blob leaving ResolverQuery()", pBlob ); } return status; }
//
// Query API
//
#ifdef DNS_TRY_ASYNC
VOID R_ResolverQueryAsync( IN PRPC_ASYNC_STATE AsyncHandle, IN DNS_RPC_HANDLE Handle, IN OUT PRPC_QUERY_BLOB pBlob ) /*++
Routine Description:
Query the resolver.
Arguments:
pBlob -- ptr to query info and results buffer
Return Value:
ERROR_SUCCESS if successful. ErrorCode (including DNS RCODE) on failure.
--*/ { DNS_STATUS status = ERROR_SUCCESS; PDNS_RECORD prr = NULL; PDNS_RECORD prrQuery = NULL; PDNS_RECORD presultRR = NULL; PCACHE_ENTRY pentry = NULL; BOOL locked = FALSE; BOOL fcacheNegativeResponse = FALSE; CHAR nameUtf8[ DNS_MAX_NAME_BUFFER_LENGTH+1 ]; DWORD nameBufLength = DNS_MAX_NAME_BUFFER_LENGTH;
// DCR_CLEANUP: make local
// quickie define to old args
PWSTR pwsName = pBlob->pName; WORD Type = pBlob->wType; DWORD Flags = pBlob->Flags;
DNSLOG_F1( "R_ResolverQuery" ); DNSLOG_F1( " Arguments:" ); DNSLOG_F2( " Name : %S", pwsName ); DNSLOG_F2( " Type : %d", Type ); DNSLOG_F2( " Flags : 0x%x", Flags );
DNSDBG( RPC, ( "\nR_ResolverQuery( %S, t=%d, f=%08x )\n", pwsName, Type, Flags ));
//
// cacheable response
//
Done:
//
// put results in blob
//
pBlob->pRecords = presultRR; pBlob->Status = status;
DNSLOG_F3( "R_ResolverQuery - status : 0x%.8X\n\t%s", status, Dns_StatusString( status ) ); DNSLOG_F1( "" );
DNSDBG( RPC, ( "Leave R_ResolverQuery( %S, t=%d, f=%08x )\n\n", pwsName, Type, Flags )); } #endif
BOOL ResolverCacheQueryCallback( IN OUT PQUERY_BLOB pBlob ) /*++
Routine Description:
Check cache for name.
This is callback to check appended names.
Arguments:
pBlob -- query blob
Return Value:
TRUE if name and type found. FALSE otherwise.
--*/ { //
// check cache for name and type
//
if ( SKIP_CACHE_LOOKUP(pBlob->Flags) ) { return FALSE; }
//
// copy name back to unicode
//
if ( ! Dns_NameCopyWireToUnicode( pBlob->NameBufferWide, pBlob->pNameWire ) ) { DNSDBG( ANY, ( "Invalid name %s.\n", pBlob->pNameWire )); DNS_ASSERT( FALSE ); return FALSE; }
//
// lookup in cache
//
return Cache_GetRecordsForRpc( & pBlob->pRecords, & pBlob->Status, pBlob->NameBufferWide, pBlob->wType, pBlob->Flags ); }
DNS_STATUS R_ResolverQuery( IN DNS_RPC_HANDLE Handle, IN PWSTR pwsName, IN WORD wType, IN DWORD Flags, OUT PDNS_RECORD * ppResultRecords ) /*++
Routine Description:
Simple query to resolver.
Arguments:
Return Value:
ERROR_SUCCESS if query successful. ErrorCode on failure.
--*/ { DNS_STATUS status = ERROR_SUCCESS; PDNS_RECORD prrReturn = NULL; QUERY_BLOB blob;
DNSLOG_F1( "DNS Caching Resolver Service - R_ResolverQuery" ); DNSLOG_F1( " Arguments:" ); DNSLOG_F2( " Name : %S", pwsName ); DNSLOG_F2( " Type : %d", wType ); DNSLOG_F2( " Flags : 0x%x", Flags );
DNSDBG( RPC, ( "\nR_ResolverQuery( %S, t=%d, f=%08x )\n", pwsName, wType, Flags ));
if ( !ppResultRecords ) { return ERROR_INVALID_PARAMETER; }
if ( ClientThreadNotAllowedAccess() ) { DNSLOG_F1( "R_ResolverQuery - ERROR_ACCESS_DENIED" ); status = ERROR_ACCESS_DENIED; goto Done; }
//
// check cache for name and type
//
// DCR: functionalize to take QUERY_BLOB
//
if ( !(Flags & DNS_QUERY_BYPASS_CACHE) ) { if ( Cache_GetRecordsForRpc( & prrReturn, & status, pwsName, wType, Flags ) ) { goto Done; } }
//
// setup query blob
//
RtlZeroMemory( & blob, sizeof(blob) );
blob.pNameOrig = pwsName; blob.wType = wType; blob.Flags = Flags | DNSQUERY_UNICODE_OUT;
// callbacks
// - address info func for prioritize
// - cache query for intermediate names
blob.pfnGetAddrArray = GetLocalAddrArray; blob.pfnQueryCache = ResolverCacheQueryCallback;
//
// do query
// - local lookup
// - then wire query
//
status = ResolverQuery( &blob );
if ( status != ERROR_SUCCESS && status != DNS_ERROR_RCODE_NAME_ERROR && status != DNS_INFO_NO_RECORDS ) { goto Done; } prrReturn = blob.pRecords;
//
// local results
// - not cached
// but note that still going through Cache_QueryResponse()
// to get proper RPC preparation
#if 0
if ( blob.pLocalRecords ) { } #endif
//
// cache results
// - don't cache local lookup records
//
// DCR: should have simple "CacheResults" flag
//
// note: even local records are going through here
// now to clean them up for RPC; they are not
// cached
//
status = Cache_QueryResponse( &blob ); prrReturn = blob.pRecords;
Done:
// dump any unused query records
if ( prrReturn && status != ERROR_SUCCESS ) { Dns_RecordListFree( prrReturn ); prrReturn = NULL; }
// set out pointer
*ppResultRecords = prrReturn;
DNSLOG_F3( " R_ResolverQuery - Returning status : 0x%.8X\n\t%s", status, Dns_StatusString(status) ); DNSLOG_F1( "" );
IF_DNSDBG( RPC ) { DnsDbg_RecordSet( "R_ResolverQuery Result List:", prrReturn ); } DNSDBG( RPC, ( "Leave R_ResolverQuery( %S, t=%d, f=%08x )\n\n" "\tstatus = %d\n\n", pwsName, wType, Flags, status ));
return status; }
//
// End ops.c
//
|