|
|
/*++
Copyright (c) 1997-2001 Microsoft Corporation
Module Name:
remote.c
Abstract:
DNS Resolver Service.
Remote APIs to resolver service.
Author:
Glenn Curtis (glennc) Feb 1997
Revision History:
Jim Gilroy (jamesg) March 2000 cleanup
--*/
#include "local.h"
//
// Local Definitions
//
typedef struct _POPUP_MSG_PARMS { LPWSTR Message; LPWSTR Title; } POPUP_MSG_PARMS, *PPOPUP_MSG_PARMS;
//
// Private protos
//
DNS_STATUS RslvrQueryToDnsServer( OUT PDNS_RECORD * ppRecord, IN PWSTR pwsName, IN WORD wType, IN DWORD Flags, OUT PBOOL pfCacheNegativeResponse );
BOOL IsKnownTimedOutAdapter( VOID );
VOID SetKnownTimedOutAdapter( VOID );
BOOL IsTimeToResetServerPriorities( VOID );
DWORD PopupMessageThread( IN PPOPUP_MSG_PARMS );
PDNS_RPC_CACHE_TABLE CreateCacheTableEntry( IN LPWSTR Name );
VOID FreeCacheTableEntryList( IN PDNS_RPC_CACHE_TABLE pCacheTableList );
BOOL IsEmptyDnsResponse( IN PDNS_RECORD );
//
// Operations
//
DNS_STATUS CRrReadCache( IN DNS_RPC_HANDLE Reserved, OUT PDNS_RPC_CACHE_TABLE * ppCacheTable ) /*++
Routine Description:
Arguments:
Return Value:
--*/ // CRrReadCache
{ DNS_STATUS status = ERROR_SUCCESS; PDNS_RPC_CACHE_TABLE pprevRpcEntry = NULL; DWORD iter; DWORD countEntries = 0;
#define MAX_RPC_CACHE_ENTRY_COUNT (300)
UNREFERENCED_PARAMETER(Reserved);
DNSDBG( RPC, ( "CRrReadCache\n" ));
if ( !ppCacheTable ) { return ERROR_INVALID_PARAMETER; }
*ppCacheTable = NULL;
DNSLOG_F1( "DNS Caching Resolver Service - CRrReadCache" );
if ( ClientThreadNotAllowedAccess() ) { DNSLOG_F1( "CRrReadCache - ERROR_ACCESS_DENIED" ); return ERROR_ACCESS_DENIED; }
LOCK_CACHE();
DNSLOG_F2( " Current number of entries in cache : %d", g_EntryCount ); DNSLOG_F2( " Current number of RR sets in cache : %d", g_RecordSetCount );
//
// Loop through all hash table slots looking for cache entries
// to return.
//
for ( iter = 0; iter < g_HashTableSize; iter++ ) { PCACHE_ENTRY pentry = g_HashTable[iter]; DWORD iter2;
while ( pentry && countEntries < MAX_RPC_CACHE_ENTRY_COUNT ) { PDNS_RPC_CACHE_TABLE prpcEntry;
prpcEntry = CreateCacheTableEntry( pentry->pName ); if ( ! prpcEntry ) { // only failure is memory alloc
FreeCacheTableEntryList( *ppCacheTable ); *ppCacheTable = NULL; status = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; }
//
// insert new entry at end of current list
//
if ( pprevRpcEntry ) pprevRpcEntry->pNext = prpcEntry; else *ppCacheTable = prpcEntry;
pprevRpcEntry = prpcEntry;
countEntries++;
//
// fill in entry with current cached types
//
for ( iter2 = 0; iter2 < pentry->MaxCount; iter2++ ) { PDNS_RECORD prr = pentry->Records[iter2]; WORD type;
if ( !prr ) { continue; }
// DCR -- goofy, just make sure the same and index (or limit?)
type = prr->wType;
if ( ! prpcEntry->Type1 ) prpcEntry->Type1 = type; else if ( ! prpcEntry->Type2 ) prpcEntry->Type2 = type; else prpcEntry->Type3 = type; } pentry = pentry->pNext; }
if ( countEntries > MAX_RPC_CACHE_ENTRY_COUNT ) { break; } }
ErrorExit:
UNLOCK_CACHE();
DNSLOG_F3( " CRrReadCache - Returning status : 0x%.8X\n\t%s", status, Dns_StatusString( status ) ); DNSLOG_F1( "" );
return status; }
DNS_STATUS CRrReadCacheEntry( IN DNS_RPC_HANDLE Reserved, IN LPWSTR pwsName, IN WORD wType, OUT PDNS_RECORD * ppRRSet ) /*++
Routine Description:
Arguments:
Return Value:
--*/ // CRrReadCacheEntry
{ DNS_STATUS status; PCACHE_ENTRY pentry; PDNS_RECORD prr;
UNREFERENCED_PARAMETER(Reserved);
DNSLOG_F1( "DNS Caching Resolver Service - CRrReadCacheEntry" ); DNSLOG_F1( " Arguments:" ); DNSLOG_F2( " Name : %S", pwsName ); DNSLOG_F2( " Type : %d", wType ); DNSLOG_F1( "" );
DNSDBG( RPC, ( "\nCRrReadCacheEntry( %S, %d )\n", pwsName, wType ));
if ( !ppRRSet ) return ERROR_INVALID_PARAMETER;
if ( ClientThreadNotAllowedAccess() ) { DNSLOG_F1( "CRrReadCacheEntry - ERROR_ACCESS_DENIED" ); return ERROR_ACCESS_DENIED; }
//
// find record in cache
// - copy if not NAME_ERROR or EMPTY
// - default to not-found error
// (DOES_NOT_EXIST error)
//
*ppRRSet = NULL; status = DNS_ERROR_RECORD_DOES_NOT_EXIST;
Cache_GetRecordsForRpc( ppRRSet, & status, pwsName, wType, 0 // no screening flags
);
DNSLOG_F3( " CRrReadCacheEntry - Returning status : 0x%.8X\n\t%s", status, Dns_StatusString( status ) ); DNSLOG_F1( "" );
DNSDBG( RPC, ( "Leave CRrReadCacheEntry( %S, %d ) => %d\n\n", pwsName, wType, status ));
return status; }
DNS_STATUS CRrGetHashTableStats( IN DNS_RPC_HANDLE Reserved, OUT LPDWORD pdwCacheHashTableSize, OUT LPDWORD pdwCacheHashTableBucketSize, OUT LPDWORD pdwNumberOfCacheEntries, OUT LPDWORD pdwNumberOfRecords, OUT LPDWORD pdwNumberOfExpiredRecords, OUT PDNS_STATS_TABLE * ppStatsTable ) /*++
Routine Description:
Arguments:
Return Value:
--*/ { PDNS_STATS_TABLE pprevRow = NULL; PDWORD_LIST_ITEM pprevItem = NULL; DWORD rowIter; DWORD itemIter; DWORD countExpiredRecords = 0; DWORD status = ERROR_SUCCESS;
UNREFERENCED_PARAMETER(Reserved);
if ( !pdwCacheHashTableSize || !pdwCacheHashTableBucketSize || !pdwNumberOfCacheEntries || !pdwNumberOfRecords || !pdwNumberOfExpiredRecords || !ppStatsTable ) { return ERROR_INVALID_PARAMETER; }
DNSLOG_F1( "CRrGetHashTableStats" ); DNSDBG( RPC, ( "CRrGetHashTableStats\n" ));
if ( ClientThreadNotAllowedAccess() ) { DNSLOG_F1( "CRrGetHashTableStats - ERROR_ACCESS_DENIED" ); return ERROR_ACCESS_DENIED; }
LOCK_CACHE();
*pdwCacheHashTableSize = g_HashTableSize; //*pdwCacheHashTableBucketSize = g_CacheHashTableBucketSize;
*pdwCacheHashTableBucketSize = 0; *pdwNumberOfCacheEntries = g_EntryCount; *pdwNumberOfRecords = g_RecordSetCount; *pdwNumberOfExpiredRecords = 0;
//
// read entire hash table
//
for ( rowIter = 0; rowIter < g_HashTableSize; rowIter++ ) { PCACHE_ENTRY pentry = g_HashTable[rowIter]; PDNS_STATS_TABLE pnewRow;
//
// create table for each new row
//
pnewRow = RPC_HEAP_ALLOC( sizeof(DNS_STATS_TABLE) ); if ( !pnewRow ) { status = ERROR_NOT_ENOUGH_MEMORY; goto Done; }
if ( rowIter == 0 ) *ppStatsTable = pnewRow; else pprevRow->pNext = pnewRow;
//
// fill in row data (if any)
//
while ( pentry ) { PDWORD_LIST_ITEM pnewItem;
pnewItem = RPC_HEAP_ALLOC( sizeof( DWORD_LIST_ITEM ) ); if ( !pnewItem ) { status = ERROR_NOT_ENOUGH_MEMORY; goto Done; }
for ( itemIter = 0; itemIter < pentry->MaxCount; itemIter++ ) { PDNS_RECORD prr = pentry->Records[itemIter]; if ( prr ) { pnewItem->Value1++;
if ( !Cache_IsRecordTtlValid( prr ) ) { pnewItem->Value2++; countExpiredRecords++; } } }
if ( !pnewRow->pListItem ) pnewRow->pListItem = pnewItem; else pprevItem->pNext = pnewItem;
pprevItem = pnewItem; pentry = pentry->pNext; }
pprevRow = pnewRow; }
Done:
UNLOCK_CACHE(); *pdwNumberOfExpiredRecords = countExpiredRecords; return status; }
BOOL IsKnownNetFailure( VOID ) /*++
Routine Description:
Determine if we are in known net failure window.
Arguments:
None
Return Value:
TRUE if in known net failure FALSE otherwise
--*/ { BOOL flag = FALSE;
DNSDBG( TRACE, ( "IsKnownNetFailure()\n" ));
LOCK_NET_FAILURE();
if ( g_NetFailureStatus ) { if ( g_NetFailureTime < Dns_GetCurrentTimeInSeconds() ) { g_NetFailureTime = 0; g_NetFailureStatus = ERROR_SUCCESS; flag = FALSE; } else { SetLastError( g_NetFailureStatus ); flag = TRUE; } }
UNLOCK_NET_FAILURE();
return flag; }
VOID SetKnownNetFailure( IN DNS_STATUS Status ) /*++
Routine Description:
Set cause of net failure.
Arguments:
Status -- status code for cause of net failure
Return Value:
None
--*/ { LPSTR DnsString = NULL; LPWSTR InsertStrings[3]; WCHAR String1[25]; WCHAR String2[256]; WCHAR String3[25];
DNSDBG( TRACE, ( "SetKnownNetFailure()\n" ));
//
// don't indicate failure during boot
//
if ( Dns_GetCurrentTimeInSeconds() < THREE_MINUTES_FROM_SYSTEM_BOOT ) { return; }
if ( !g_LocalAddrArray || g_NetFailureCacheTime == 0 ) { //
// We are in a no-net configuration, there is no need
// to display the pop-up message. No point warning
// of DNS configuration problems when the system is
// off the net.
// - or -
// We are on a NT server, and therefore don't do poor network
// performance caching.
//
return; }
LOCK_NET_FAILURE();
g_NetFailureTime = Dns_GetCurrentTimeInSeconds() + g_NetFailureCacheTime; g_NetFailureStatus = Status;
wsprintfW( String1, L"0x%.8X", Status );
DnsString = DnsStatusString( Status );
if ( DnsString ) { Dns_StringCopy( (PBYTE) String2, NULL, (PCHAR) DnsString, (WORD) strlen( DnsString ), DnsCharSetAnsi, DnsCharSetUnicode ); //
// No need to free this since the string is just a pointer
// to a global table entry.
//
// FREE_HEAP( DnsString );
} else { wsprintfW( String2, L"<?>" ); }
wsprintfW( String3, L"%d", g_NetFailureCacheTime );
if ( g_MessagePopupStrikes < 3 ) { g_MessagePopupStrikes++; } else { if ( Status != g_PreviousNetFailureStatus ) { //
// DCR_PERF: should remove logging from inside lock
//
InsertStrings[0] = String1; InsertStrings[1] = String2; InsertStrings[2] = String3; ResolverLogEvent( EVENT_DNS_CACHE_NETWORK_PERF_WARNING, EVENTLOG_WARNING_TYPE, 3, InsertStrings, Status ); g_PreviousNetFailureStatus = Status; }
g_MessagePopupStrikes = 0; }
UNLOCK_NET_FAILURE(); }
BOOL IsKnownTimedOutAdapter( VOID ) /*++
Routine Description:
Determine if timed out adapter exists.
Arguments:
None
Return Value:
TRUE if timed out adapter FALSE otherwise
--*/ { BOOL flag = FALSE;
DNSDBG( TRACE, ( "IsKnownTimedOutAdapter()\n" ));
//
// DCR: don't really need lock for this?
// - could check if lock taken?
// but if beat it -- so what
//
LOCK_NET_FAILURE();
if ( g_fTimedOutAdapter ) { if ( g_TimedOutAdapterTime < Dns_GetCurrentTimeInSeconds() ) { DNSLOG_F1( " Timed out adapter cache expired, resseting adapter!" ); g_TimedOutAdapterTime = 0; g_fTimedOutAdapter = FALSE; flag = FALSE; } else { flag = TRUE; } }
UNLOCK_NET_FAILURE();
return flag; }
VOID SetKnownTimedOutAdapter( VOID ) { DNSDBG( TRACE, ( "SetKnownTimedOutAdapter()\n" ));
if ( Dns_GetCurrentTimeInSeconds() < THREE_MINUTES_FROM_SYSTEM_BOOT ) { return; }
if ( !g_LocalAddrArray ) { //
// We are in a no-net configuration, there is no need
// to display the pop-up message. No point warning
// of DNS configuration problems when the system is
// off the net.
//
return; }
DNSLOG_F1( " Detected a timed out adapter, disabling it for a little while" );
LOCK_NET_FAILURE();
g_TimedOutAdapterTime = Dns_GetCurrentTimeInSeconds() + g_AdapterTimeoutLimit;
g_fTimedOutAdapter = TRUE;
UNLOCK_NET_FAILURE(); }
DWORD PopupMessageThread( IN OUT PPOPUP_MSG_PARMS MsgParms ) /*++
Routine Description:
Popup a message box with error.
Arguments:
MsgParms -- popup message parameters
Return Value:
ERROR_SUCCESS
--*/ { MessageBoxW( NULL, MsgParms->Message, MsgParms->Title, MB_SERVICE_NOTIFICATION | MB_ICONWARNING | MB_OK );
GENERAL_HEAP_FREE( MsgParms->Message ); GENERAL_HEAP_FREE( MsgParms->Title ); GENERAL_HEAP_FREE( MsgParms );
return ERROR_SUCCESS; }
BOOL ClientThreadNotAllowedAccess( VOID ) { #if 0
//
// DCR: should probably access check only for flush cache or
// delete entry
//
//
// DCR: - This idea of adding a security check for
// DNS RPC API is really debatable. The data
// maintained by the DNS Caching Resolver is definately
// not private. Since the cache mimicks the DNS protocol
// as a non-handle based access to public information,
// it would require an access check for every interface
// and for every call. Doing this would required a context
// switch into kernel mode to perform the check. This is
// a lot of overhead just to protect an API that provides
// access to widely available information. After all, the
// cache is supposed to improve name resolution performance!
//
// Below is the start of some code to implement an access
// check, though it sounds like I should call the Win32
// security function AccessCheck() and create a DNS cache
// SID to compare the desired access of the client thread
// against. This is not finished and I don't intend to
// try finish it.
//
GENERIC_MAPPING DNSAccessMapping = { STANDARD_RIGHTS_READ, STANDARD_RIGHTS_WRITE, STANDARD_RIGHTS_EXECUTE };
HANDLE hThread = GetCurrentThread(); DWORD dwGrantedAccess; BOOL Result;
if ( RpcImpersonateClient(NULL) ) return TRUE;
hThread = GetCurrentThread();
if ( !hThread ) { RpcRevertToSelf(); return TRUE; }
if ( OpenThreadToken( hThread, TOKEN_QUERY, FALSE, &hToken ) ) { if ( AccessCheck( pSD, hToken, STANDARD_RIGHTS_WRITE, &DNSAccessMapping, pPS, sizeof( *pPS ), &dwGrantedAccess, &Result ); }
CloseHandle( hThread );
RpcRevertToSelf();
if ( Result ) return FALSE; else return TRUE; #endif
return FALSE; }
PDNS_RPC_CACHE_TABLE CreateCacheTableEntry( IN LPWSTR pwsName ) { PDNS_RPC_CACHE_TABLE prpcEntry = NULL;
if ( ! pwsName ) return NULL;
prpcEntry = (PDNS_RPC_CACHE_TABLE) RPC_HEAP_ALLOC_ZERO( sizeof(DNS_RPC_CACHE_TABLE) );
if ( prpcEntry == NULL ) return NULL;
prpcEntry->Name = RPC_HEAP_ALLOC( sizeof(WCHAR) * (wcslen(pwsName) + 1) ); if ( ! prpcEntry->Name ) { RPC_HEAP_FREE( prpcEntry ); return NULL; }
wcscpy( prpcEntry->Name, pwsName );
return prpcEntry; }
VOID FreeCacheTableEntryList( IN PDNS_RPC_CACHE_TABLE pCacheTableList ) { while ( pCacheTableList ) { PDNS_RPC_CACHE_TABLE pNext = pCacheTableList->pNext;
if ( pCacheTableList->Name ) { RPC_HEAP_FREE( pCacheTableList->Name ); pCacheTableList->Name = NULL; }
RPC_HEAP_FREE( pCacheTableList );
pCacheTableList = pNext; } }
BOOL IsEmptyDnsResponse( IN PDNS_RECORD pRecord ) { //
// DCR_FIX: should be dnslib utility
//
// DCR_FIX: should distinguish referral and no-records
//
PDNS_RECORD pTempRecord = pRecord; BOOL fEmpty = TRUE;
while ( pTempRecord ) { if ( pTempRecord->Flags.S.Section == DNSREC_ANSWER ) { fEmpty = FALSE; break; } pTempRecord = pTempRecord->pNext; }
return fEmpty; }
DNS_STATUS CRrUpdateTest( IN DNS_RPC_HANDLE Reserved, IN PWSTR pwsName, IN DWORD fOptions, IN IP_ADDRESS ServerIp ) /*++
Routine Description:
Do update test for existing record.
DCR: need UpdateTest() IPv6 capable
Arguments:
Return Value:
ErrorCode from update attempt.
--*/ { DNS_STATUS status = ERROR_SUCCESS; DNS_RECORD record; DWORD flags = fOptions; PSTR pnameTemp = NULL; PDNS_NETINFO pnetInfo = NULL; IP_ARRAY serverIpArray; PIP_ARRAY pserverIpArray = NULL;
DNSLOG_F1( "DNS Caching Resolver Service - CRrUpdateTest" ); DNSDBG( RPC, ( "\nCRrUpdateTest()\n" ));
//
// Validate arguments
//
if ( !pwsName || !ServerIp ) { return ERROR_INVALID_PARAMETER; }
//
// make UTF8 name and FAZ
//
// DCR: not clear why all this work isn't just done in update API
//
pnameTemp = Dns_NameCopyAllocate( (PCHAR) pwsName, 0, DnsCharSetUnicode, DnsCharSetUtf8 ); if ( ! pnameTemp ) { return ERROR_NOT_ENOUGH_MEMORY; }
serverIpArray.AddrCount = 1; serverIpArray.AddrArray[0] = ServerIp; pserverIpArray = &serverIpArray;
status = Dns_FindAuthoritativeZoneLib( (PDNS_NAME) pnameTemp, 0, pserverIpArray, &pnetInfo );
if ( status != NO_ERROR ) { goto Cleanup; }
//
// build update prereq "nothing exists" record
//
RtlZeroMemory( &record, sizeof(DNS_RECORD) ); record.pName = (PDNS_NAME) pnameTemp; record.wType = DNS_TYPE_ANY; record.wDataLength = 0; record.Flags.DW = DNSREC_PREREQ | DNSREC_NOEXIST;
//
// update
//
status = Dns_UpdateLib( &record, 0, pnetInfo, NULL, NULL );
Cleanup:
Dns_Free( pnameTemp );
NetInfo_Free( pnetInfo );
DNSDBG( RPC, ( "Leave CRrUpdateTest() => %d\n\n", status ));
return status; }
//
// End remote.c
//
|