windows-nt/Source/XPSP1/NT/ds/dns/resolver/server/ops.c

885 lines
18 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
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
//