windows-nt/Source/XPSP1/NT/ds/dns/resolver/server/remote.c
2020-09-26 16:20:57 +08:00

1020 lines
20 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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
//