|
|
/*++
Copyright (c) 1997-2001 Microsoft Corporation
Module Name:
query.c
Abstract:
Domain Name System (DNS) API
Query routines.
Author:
Jim Gilroy (jamesg) January, 1997
Revision History:
--*/
#include "local.h"
//
// TTL for answering IP string queries
// (use a week)
//
#define IPSTRING_RECORD_TTL (604800)
//
// Max number of server's we'll ever bother to extract from packet
// (much more and you're out of UDP packet space anyway)
//
#define MAX_NAME_SERVER_COUNT (20)
//
// Query utilities
//
// DCR: move to library packet stuff
//
BOOL IsEmptyDnsResponse( IN PDNS_RECORD pRecordList ) /*++
Routine Description:
Check for no-answer response.
Arguments:
pRecordList -- record list to check
Return Value:
TRUE if no-answers FALSE if answers
--*/ { PDNS_RECORD prr = pRecordList; BOOL fempty = TRUE;
while ( prr ) { if ( prr->Flags.S.Section == DNSREC_ANSWER ) { fempty = FALSE; break; } prr = prr->pNext; }
return fempty; }
BOOL IsEmptyDnsResponseFromResolver( IN PDNS_RECORD pRecordList ) /*++
Routine Description:
Check for no-answer response.
Arguments:
pRecordList -- record list to check
Return Value:
TRUE if no-answers FALSE if answers
--*/ { PDNS_RECORD prr = pRecordList; BOOL fempty = TRUE;
//
// resolver sends every thing back as ANSWER section
// or section==0 for host file
//
//
// DCR: this is lame because the query interface to the
// resolver is lame
//
while ( prr ) { if ( prr->Flags.S.Section == DNSREC_ANSWER || prr->Flags.S.Section == 0 ) { fempty = FALSE; break; } prr = prr->pNext; }
return fempty; }
VOID FixupNameOwnerPointers( IN OUT PDNS_RECORD pRecord ) /*++
Routine Description:
None.
Arguments:
None.
Return Value:
None.
--*/ { PDNS_RECORD prr = pRecord; PTSTR pname = pRecord->pName;
DNSDBG( TRACE, ( "FixupNameOwnerPointers()\n" ));
while ( prr ) { if ( prr->pName == NULL ) { prr->pName = pname; } else { pname = prr->pName; }
prr = prr->pNext; } }
BOOL IsCacheableNameError( IN PDNS_NETINFO pNetInfo ) /*++
Routine Description:
Determine if name error is cacheable.
To this is essentially a check that DNS received results on all networks.
Arguments:
pNetInfo -- pointer to network info used in query
Return Value:
TRUE if name error cacheable. FALSE otherwise (some network did not respond)
--*/ { DWORD iter; PDNS_ADAPTER padapter;
DNSDBG( TRACE, ( "IsCacheableNameError()\n" ));
if ( !pNetInfo ) { ASSERT( FALSE ); return TRUE; }
//
// check each adapter
// - any that are capable of responding (have DNS servers)
// MUST have responded in order for response to be
// cacheable
//
// DCR: return flags DCR
// - adapter queried flag
// - got response flag (valid response flag?)
// - explict negative answer flag
//
// DCR: cachable negative should come back directly from query
// perhaps in netinfo as flag -- "negative on all adapters"
//
for ( iter = 0; iter < pNetInfo->AdapterCount; iter++ ) { padapter = pNetInfo->AdapterArray[iter];
if ( ( padapter->InfoFlags & DNS_FLAG_IGNORE_ADAPTER ) || ( padapter->RunFlags & RUN_FLAG_STOP_QUERY_ON_ADAPTER ) ) { continue; }
// if negative answer on adapter -- fine
if ( padapter->Status == DNS_ERROR_RCODE_NAME_ERROR || padapter->Status == DNS_INFO_NO_RECORDS ) { ASSERT( padapter->RunFlags & RUN_FLAG_STOP_QUERY_ON_ADAPTER ); continue; }
// note, the above should map one-to-one with query stop
ASSERT( !(padapter->RunFlags & RUN_FLAG_STOP_QUERY_ON_ADAPTER) );
// if adapter has no DNS server -- fine
// in this case PnP before useful, and the PnP event
// will flush the cache
if ( padapter->ServerCount == 0 ) { continue; }
// otherwise, this adapter was queried but could not produce a response
DNSDBG( TRACE, ( "IsCacheableNameError() -- FALSE\n" "\tadapter %d (%S) did not receive response\n" "\treturn status = %d\n" "\treturn flags = %08x\n", iter, padapter->pszAdapterGuidName, padapter->Status, padapter->RunFlags ));
return FALSE; } return TRUE; }
//
// Query name building utils
//
BOOL ValidateQueryTld( IN PSTR pTld ) /*++
Routine Description:
Validate query TLD
Arguments:
pTld -- TLD to validate
Return Value:
TRUE if valid FALSE otherwise
--*/ { //
// numeric
//
if ( g_ScreenBadTlds & DNS_TLD_SCREEN_NUMERIC ) { if ( Dns_IsNameNumeric_A( pTld ) ) { return FALSE; } }
//
// bogus TLDs
//
if ( g_ScreenBadTlds & DNS_TLD_SCREEN_WORKGROUP ) { if ( Dns_NameCompare_UTF8( "workgroup", pTld )) { return FALSE; } }
// not sure about these
// probably won't turn on screening by default
if ( g_ScreenBadTlds & DNS_TLD_SCREEN_DOMAIN ) { if ( Dns_NameCompare_UTF8( "domain", pTld )) { return FALSE; } } if ( g_ScreenBadTlds & DNS_TLD_SCREEN_OFFICE ) { if ( Dns_NameCompare_UTF8( "office", pTld )) { return FALSE; } } if ( g_ScreenBadTlds & DNS_TLD_SCREEN_HOME ) { if ( Dns_NameCompare_UTF8( "home", pTld )) { return FALSE; } }
return TRUE; }
BOOL ValidateQueryName( IN PQUERY_BLOB pBlob, IN PSTR pName, IN PSTR pDomain ) /*++
Routine Description:
Validate name for wire query.
Arguments:
pBlob -- query blob
pName -- name; may be any sort of name
pDomain -- domain name to append
Return Value:
TRUE if name query will be valid. FALSE otherwise.
--*/ { WORD wtype; PSTR pnameTld; PSTR pdomainTld;
// no screening -- bail
if ( g_ScreenBadTlds == 0 ) { return TRUE; }
// only screening for standard types
// - A, AAAA, SRV
wtype = pBlob->wType; if ( wtype != DNS_TYPE_A && wtype != DNS_TYPE_AAAA && wtype != DNS_TYPE_SRV ) { return TRUE; }
// get name TLD
pnameTld = Dns_GetTldForName( pName );
//
// if no domain appended
// - exclude single label
// - exclude bad TLD (numeric, bogus domain)
// - but allow root queries
//
// DCR: MS DCS screening
// screen
// _msdcs.<name>
// will probably be unappended query
//
if ( !pDomain ) { if ( !pnameTld || !ValidateQueryTld( pnameTld ) ) { goto Failed; } return TRUE; }
//
// domain appended
// - exclude bad TLD (numeric, bogus domain)
// - exclude matching TLD
//
pdomainTld = Dns_GetTldForName( pDomain ); if ( !pdomainTld ) { pdomainTld = pDomain; }
if ( !ValidateQueryTld( pdomainTld ) ) { goto Failed; }
// screen repeated TLD
if ( g_ScreenBadTlds & DNS_TLD_SCREEN_REPEATED ) { if ( Dns_NameCompare_UTF8( pnameTld, pdomainTld ) ) { goto Failed; } }
return TRUE;
Failed:
DNSDBG( QUERY, ( "Failed invalid query name:\n" "\tname %s\n" "\tdomain %s\n", pName, pDomain ));
return FALSE; }
PSTR GetNextAdapterDomainName( IN OUT PDNS_NETINFO pNetInfo ) /*++
Routine Description:
Get next adapter domain name to query.
Arguments:
pNetInfo -- DNS Network info for query; adapter data will be modified (InfoFlags field) to indicate which adapter to query and which to skip query on
Return Value:
Ptr to domain name (UTF8) to query. NULL if no more domain names to query.
--*/ { DWORD iter; PSTR pqueryDomain = NULL;
DNSDBG( TRACE, ( "GetNextAdapterDomainName()\n" ));
if ( ! pNetInfo ) { ASSERT( FALSE ); return NULL; }
IF_DNSDBG( OFF ) { DnsDbg_NetworkInfo( "Net info to get adapter domain name from: ", pNetInfo ); }
//
// check each adapter
// - first unqueried adapter with name is chosen
// - other adapters with
// - matching name => included in query
// - non-matching => turned OFF for query
//
// DCR: query on\off should use adapter dynamic flags
//
for ( iter = 0; iter < pNetInfo->AdapterCount; iter++ ) { PDNS_ADAPTER padapter; PSTR pdomain;
padapter = pNetInfo->AdapterArray[iter];
// netinfo should come in with name specific flags clean
DNS_ASSERT( !(padapter->RunFlags & RUN_FLAG_SINGLE_NAME_MASK) );
//
// ignore
// - ignored adapter OR
// - previously queried adapter domain
// note: it can't match any "fresh" domain we come up with
// as we always collect all matches
if ( (padapter->InfoFlags & DNS_FLAG_IGNORE_ADAPTER) || (padapter->RunFlags & RUN_FLAG_QUERIED_ADAPTER_DOMAIN) ) { padapter->RunFlags |= RUN_FLAG_STOP_QUERY_ON_ADAPTER; continue; }
// no domain name -- always off
pdomain = padapter->pszAdapterDomain; if ( !pdomain ) { padapter->RunFlags |= (RUN_FLAG_QUERIED_ADAPTER_DOMAIN | RUN_FLAG_STOP_QUERY_ON_ADAPTER); continue; }
// first "fresh" domain name -- save, turn on and flag as used
if ( !pqueryDomain ) { pqueryDomain = pdomain; padapter->RunFlags |= RUN_FLAG_QUERIED_ADAPTER_DOMAIN; continue; }
// other "fresh" domain names
// - if matches query domain => on for query
// - no match => off
if ( Dns_NameCompare_UTF8( pqueryDomain, pdomain ) ) { padapter->RunFlags |= RUN_FLAG_QUERIED_ADAPTER_DOMAIN; continue; } else { padapter->RunFlags |= RUN_FLAG_STOP_QUERY_ON_ADAPTER; continue; } }
//
// if no adapter domain name -- clear STOP flag
// - all adapters participate in other names (name devolution)
//
if ( !pqueryDomain ) { for ( iter = 0; iter < pNetInfo->AdapterCount; iter++ ) { PDNS_ADAPTER padapter = pNetInfo->AdapterArray[iter]; padapter->RunFlags &= (~RUN_FLAG_SINGLE_NAME_MASK ); }
DNSDBG( INIT2, ( "GetNextAdapterDomainName out of adapter names.\n" ));
pNetInfo->ReturnFlags |= RUN_FLAG_QUERIED_ADAPTER_DOMAIN; }
IF_DNSDBG( INIT2 ) { if ( pqueryDomain ) { DnsDbg_NetworkInfo( "Net info after adapter name select: ", pNetInfo ); } }
DNSDBG( INIT2, ( "Leaving GetNextAdapterDomainName() => %s\n", pqueryDomain ));
return pqueryDomain; }
PSTR GetNextDomainNameToAppend( IN OUT PDNS_NETINFO pNetInfo, OUT PDWORD pSuffixFlags ) /*++
Routine Description:
Get next adapter domain name to query.
Arguments:
pNetInfo -- DNS Network info for query; adapter data will be modified (RunFlags field) to indicate which adapter to query and which to skip query on
pSuffixFlags -- flags associated with the use of this suffix
Return Value:
Ptr to domain name (UTF8) to query. NULL if no more domain names to query.
--*/ { PSTR psearchName; PSTR pdomain;
//
// search list if real search list
//
// if suffix flags zero, then this is REAL search list
// or is PDN name
//
psearchName = SearchList_GetNextName( pNetInfo->pSearchList, FALSE, // not reset
pSuffixFlags );
if ( psearchName && (*pSuffixFlags == 0) ) { // found regular search name -- done
DNSDBG( INIT2, ( "getNextDomainName from search list => %s, %d\n", psearchName, *pSuffixFlags )); return( psearchName ); }
//
// try adapter domain names
//
// but ONLY if search list is dummy; if real we only
// use search list entries
//
// DCR_CLEANUP: eliminate bogus search list
//
if ( pNetInfo->InfoFlags & DNS_FLAG_DUMMY_SEARCH_LIST && ! (pNetInfo->ReturnFlags & RUN_FLAG_QUERIED_ADAPTER_DOMAIN) ) { pdomain = GetNextAdapterDomainName( pNetInfo ); if ( pdomain ) { *pSuffixFlags = DNS_QUERY_USE_QUICK_TIMEOUTS; DNSDBG( INIT2, ( "getNextDomainName from adapter domain name => %s, %d\n", pdomain, *pSuffixFlags ));
// back the search list up one tick
// we queried through it above, so if it was returing
// a name, we need to get that name again on next query
if ( psearchName ) { ASSERT( pNetInfo->pSearchList->CurrentNameIndex > 0 ); pNetInfo->pSearchList->CurrentNameIndex--; } return( pdomain ); } }
//
// DCR_CLEANUP: remove devolution from search list and do explicitly
// - its cheap (or do it once and save, but store separately)
//
//
// finally use and devolved search names (or other nonsense)
//
*pSuffixFlags = DNS_QUERY_USE_QUICK_TIMEOUTS;
DNSDBG( INIT2, ( "getNextDomainName from devolution\\other => %s, %d\n", psearchName, *pSuffixFlags ));
return( psearchName ); }
PSTR GetNextQueryName( IN OUT PQUERY_BLOB pBlob ) /*++
Routine Description:
Get next name to query.
Arguments:
pBlob - blob of query information
Uses: NameOriginalWire NameAttributes QueryCount pNetworkInfo
Sets: NameWire -- set with appended wire name pNetworkInfo -- runtime flags set to indicate which adapters are queried NameFlags -- set with properties of name fAppendedName -- set when name appended
Return Value:
Ptr to name to query with. - will be orginal name on first query if name is multilabel name - otherwise will be NameWire buffer which will contain appended name composed of pszName and some domain name NULL if no more names to append
--*/ { PSTR pnameOrig = pBlob->NameOriginalWire; PSTR pdomainName = NULL; PSTR pnameBuf; DWORD queryCount = pBlob->QueryCount; DWORD nameAttributes = pBlob->NameAttributes;
DNSDBG( TRACE, ( "GetNextQueryName( %p )\n", pBlob ));
// default suffix flags
pBlob->NameFlags = 0;
//
// FQDN
// - send FQDN only
//
if ( nameAttributes & DNS_NAME_IS_FQDN ) { if ( queryCount == 0 ) { #if 0
// currently won't even validate FQDN
if ( ValidateQueryName( pBlob, pnameOrig, NULL ) ) { return pnameOrig; } #endif
return pnameOrig; } DNSDBG( QUERY, ( "No append for FQDN name %s -- end query.\n", pnameOrig )); return NULL; }
//
// multilabel
// - first pass on name itself -- if valid
//
// DCR: intelligent choice on multi-label whether append first
// or go to wire first (example foo.ntdev) could append
// first
//
if ( nameAttributes & DNS_NAME_MULTI_LABEL ) { if ( queryCount == 0 ) { if ( ValidateQueryName( pBlob, pnameOrig, NULL ) ) { return pnameOrig; } }
if ( !g_AppendToMultiLabelName ) { DNSDBG( QUERY, ( "No append allowed on multi-label name %s -- end query.\n", pnameOrig )); return NULL; }
// falls through to appending on multi-label names
}
//
// not FQDN -- append a domain name
// - next search name (if available)
// - otherwise next adapter domain name
//
pnameBuf = pBlob->NameWire;
while ( 1 ) { pdomainName = GetNextDomainNameToAppend( pBlob->pNetworkInfo, & pBlob->NameFlags ); if ( !pdomainName ) { DNSDBG( QUERY, ( "No more domain names to append -- end query\n" )); return NULL; }
if ( !ValidateQueryName( pBlob, pnameOrig, pdomainName ) ) { continue; }
// append domain name to name
if ( Dns_NameAppend_A( pnameBuf, DNS_MAX_NAME_BUFFER_LENGTH, pnameOrig, pdomainName ) ) { pBlob->fAppendedName = TRUE; break; } }
DNSDBG( QUERY, ( "GetNextQueryName() result => %s\n", pnameBuf ));
return pnameBuf; }
DNS_STATUS QueryDirectEx( IN OUT PDNS_MSG_BUF * ppMsgResponse, OUT PDNS_RECORD * ppResponseRecords, IN PDNS_HEADER pHeader, IN BOOL fNoHeaderCounts, IN PDNS_NAME pszQuestionName, IN WORD wQuestionType, IN PDNS_RECORD pRecords, IN DWORD dwFlags, IN PIP_ARRAY aipDnsServers, IN OUT PDNS_NETINFO pNetInfo ) /*++
Routine Description:
Query.
Arguments:
ppMsgResponse -- addr to recv ptr to response buffer; caller MUST free buffer
ppResponseRecord -- address to receive ptr to record list returned from query
pHead -- DNS header to send
fNoHeaderCounts - do NOT include record counts in copying header
pszQuestionName -- DNS name to query; Unicode string if dwFlags has DNSQUERY_UNICODE_NAME set. ANSI string otherwise.
wType -- query type
pRecords -- address to receive ptr to record list returned from query
dwFlags -- query flags
aipDnsServers -- specific DNS servers to query; OPTIONAL, if specified overrides normal list associated with machine
pDnsNetAdapters -- DNS servers to query; if NULL get current list
Return Value:
ERROR_SUCCESS if successful. Error code on failure.
--*/ { PDNS_MSG_BUF psendMsg; DNS_STATUS status = DNS_ERROR_NO_MEMORY;
DNSDBG( QUERY, ( "QueryDirectEx()\n" "\tname %s\n" "\ttype %d\n" "\theader %p\n" "\t - counts %d\n" "\trecords %p\n" "\tflags %08x\n" "\trecv msg %p\n" "\trecv records %p\n" "\tserver IPs %p\n" "\tadapter list %p\n", pszQuestionName, wQuestionType, pHeader, fNoHeaderCounts, pRecords, dwFlags, ppMsgResponse, ppResponseRecords, aipDnsServers, pNetInfo ));
//
// build send packet
//
psendMsg = Dns_BuildPacket( pHeader, fNoHeaderCounts, pszQuestionName, wQuestionType, pRecords, dwFlags, FALSE // query, not an update
); if ( !psendMsg ) { status = ERROR_INVALID_NAME; goto Cleanup; }
#if MULTICAST_ENABLED
//
// QUESTION: mcast test is not complete here
// - should first test that we actually do it
// including whether we have DNS servers
// FIXME: then when we do do it -- encapsulate it
// ShouldMulicastQuery()
//
// Check to see if name is for something in the multicast local domain.
// If so, set flag to multicast this query only.
//
if ( Dns_NameCompareEx( pszQuestionName, ( dwFlags & DNSQUERY_UNICODE_NAME ) ? (LPSTR) MULTICAST_DNS_LOCAL_DOMAIN_W : MULTICAST_DNS_LOCAL_DOMAIN, 0, ( dwFlags & DNSQUERY_UNICODE_NAME ) ? DnsCharSetUnicode : DnsCharSetUtf8 ) == DnsNameCompareRightParent ) { dwFlags |= DNS_QUERY_MULTICAST_ONLY; } #endif
//
// send query and receive response
//
Trace_LogQueryEvent( psendMsg, wQuestionType );
status = Dns_SendAndRecv( psendMsg, ppMsgResponse, ppResponseRecords, dwFlags, aipDnsServers, pNetInfo );
Trace_LogResponseEvent( psendMsg, ( ppResponseRecords && *ppResponseRecords ) ? (*ppResponseRecords)->wType : 0, status );
Cleanup:
FREE_HEAP( psendMsg );
DNSDBG( QUERY, ( "Leaving QueryDirectEx(), status = %s (%d)\n", Dns_StatusString(status), status ));
return( status ); }
DNS_STATUS Query_SingleName( IN OUT PQUERY_BLOB pBlob ) /*++
Routine Description:
Query single name.
Arguments:
pBlob - query blob
Return Value:
ERROR_SUCCESS if successful. Error code on failure.
--*/ { PDNS_MSG_BUF psendMsg = NULL; DNS_STATUS status = DNS_ERROR_NO_MEMORY; DWORD flags = pBlob->Flags;
DNSDBG( QUERY, ( "Query_SingleName( %p )\n", pBlob ));
IF_DNSDBG( QUERY ) { DnsDbg_QueryBlob( "Enter Query_SingleName()", pBlob ); }
//
// cache\hostfile callback on appended name
// - note that queried name was already done
// (in resolver or in Query_Main())
//
if ( pBlob->pfnQueryCache && pBlob->fAppendedName ) { if ( (pBlob->pfnQueryCache)( pBlob ) ) { status = pBlob->Status; goto Cleanup; } }
//
// if wire disallowed -- stop here
//
if ( flags & DNS_QUERY_NO_WIRE_QUERY ) { status = DNS_ERROR_NAME_NOT_FOUND_LOCALLY; pBlob->Status = status; goto Cleanup; }
//
// build send packet
//
psendMsg = Dns_BuildPacket( NULL, // no header
0, // no header counts
pBlob->pNameWire, pBlob->wType, NULL, // no records
flags, FALSE // query, not an update
); if ( !psendMsg ) { status = DNS_ERROR_INVALID_NAME; goto Cleanup; }
#if MULTICAST_ENABLED
//
// QUESTION: mcast test is not complete here
// - should first test that we actually do it
// including whether we have DNS servers
// FIXME: then when we do do it -- encapsulate it
// ShouldMulicastQuery()
//
// Check to see if name is for something in the multicast local domain.
// If so, set flag to multicast this query only.
//
if ( Dns_NameCompareEx( pBlob->pName, ( flags & DNSQUERY_UNICODE_NAME ) ? (LPSTR) MULTICAST_DNS_LOCAL_DOMAIN_W : MULTICAST_DNS_LOCAL_DOMAIN, 0, ( flags & DNSQUERY_UNICODE_NAME ) ? DnsCharSetUnicode : DnsCharSetUtf8 ) == DnsNameCompareRightParent ) { flags |= DNS_QUERY_MULTICAST_ONLY; } #endif
//
// send query and receive response
//
Trace_LogQueryEvent( psendMsg, pBlob->wType );
status = Dns_SendAndRecv( psendMsg, (flags & DNS_QUERY_RETURN_MESSAGE) ? &pBlob->pRecvMsg : NULL, & pBlob->pRecords, flags, pBlob->pDnsServers, pBlob->pNetworkInfo );
Trace_LogResponseEvent( psendMsg, ( pBlob->pRecords ) ? (pBlob->pRecords)->wType : 0, status );
Cleanup:
FREE_HEAP( psendMsg );
DNSDBG( QUERY, ( "Leaving Query_SingleName(), status = %s (%d)\n", Dns_StatusString(status), status ));
IF_DNSDBG( QUERY ) { DnsDbg_QueryBlob( "Blob leaving Query_SingleName()", pBlob ); } return( status ); }
DNS_STATUS Query_Main( IN OUT PQUERY_BLOB pBlob ) /*++
Routine Description:
Main query routine.
Does all the query processing - local lookup - name appending - cache\hostfile lookup on appended name - query to server
Arguments:
pBlob -- query info 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 = DNS_ERROR_NAME_NOT_FOUND_LOCALLY; PSTR pdomainName = NULL; PDNS_RECORD precords; DWORD queryFlags; DWORD suffixFlags = 0; DWORD nameAttributes; DNS_STATUS bestQueryStatus = ERROR_SUCCESS; BOOL fcacheNegative = TRUE;
DWORD flagsIn = pBlob->Flags; PDNS_NETINFO pnetInfo = pBlob->pNetworkInfo; DWORD nameLength; DWORD bufLength; DWORD queryCount;
DNSDBG( TRACE, ( "Query_Main( %p )\n" "\t%S, f=%08x, type=%d, time = %d\n", pBlob, pBlob->pNameOrig, flagsIn, pBlob->wType, Dns_GetCurrentTimeInSeconds() ));
//
// clear out params
//
pBlob->pRecords = NULL; pBlob->pLocalRecords = NULL; pBlob->fCacheNegative = FALSE; pBlob->fNoIpLocal = FALSE; pBlob->NetFailureStatus = ERROR_SUCCESS;
//
// convert name to wire format
//
bufLength = DNS_MAX_NAME_BUFFER_LENGTH;
nameLength = Dns_NameCopy( pBlob->NameOriginalWire, & bufLength, (PSTR) pBlob->pNameOrig, 0, // name is NULL terminated
DnsCharSetUnicode, DnsCharSetWire );
if ( nameLength == 0 ) { return DNS_ERROR_INVALID_NAME; } nameLength--; pBlob->NameLength = nameLength; pBlob->pNameOrigWire = pBlob->NameOriginalWire;
//
// determine name properties
// - determines number and order of name queries
//
nameAttributes = Dns_GetNameAttributes( pBlob->NameOriginalWire );
if ( flagsIn & DNS_QUERY_TREAT_AS_FQDN ) { nameAttributes |= DNS_NAME_IS_FQDN; } pBlob->NameAttributes = nameAttributes;
//
// hostfile lookup
// - called in process
// - hosts file lookup allowed
// -> then must do hosts file lookup before appending\queries
//
// note: this matches the hostsfile\cache lookup in resolver
// before call; hosts file queries to appended names are
// handled together by callback in Query_SingleName()
//
// we MUST make this callback here, because it must PRECEDE
// the local name call, as some customers specifically direct
// some local mappings in the hosts file
//
if ( pBlob->pfnQueryCache == QueryHostFile && ! (flagsIn & DNS_QUERY_NO_HOSTS_FILE) ) { pBlob->pNameWire = pBlob->pNameOrigWire;
if ( QueryHostFile( pBlob ) ) { status = pBlob->Status; goto Done; } }
//
// check for local name
// - if successful, skip wire query
//
if ( ! (flagsIn & DNS_QUERY_NO_LOCAL_NAME) ) { status = GetRecordsForLocalName( pBlob );
if ( status == ERROR_SUCCESS && !pBlob->fNoIpLocal ) { DNS_ASSERT( pBlob->pRecords && pBlob->pRecords == pBlob->pLocalRecords ); goto Done; } }
//
// query until
// - successfull
// - exhaust names to query with
//
queryCount = 0;
while ( 1 ) { PSTR pqueryName;
// clean name specific info from list
if ( queryCount != 0 ) { NetInfo_Clean( pnetInfo, CLEAR_LEVEL_SINGLE_NAME ); }
//
// next query name
//
pqueryName = GetNextQueryName( pBlob ); if ( !pqueryName ) { if ( queryCount == 0 ) { status = DNS_ERROR_INVALID_NAME; } break; } pBlob->QueryCount = ++queryCount; pBlob->pNameWire = pqueryName;
DNSDBG( QUERY, ( "Query %d is for name %s\n", queryCount, pqueryName ));
//
// set flags
// - passed in flags
// - unicode results
// - flags for this particular suffix
pBlob->Flags = flagsIn | pBlob->NameFlags;
//
// clear any previously received records (shouldn't be any)
//
if ( pBlob->pRecords ) { DNS_ASSERT( FALSE ); Dns_RecordListFree( pBlob->pRecords ); pBlob->pRecords = NULL; }
//
// do the query for name
// includes
// - cache or hostfile lookup
// - wire query
//
status = Query_SingleName( pBlob );
//
// clean out records on "non-response"
//
// DCR: need to fix record return
// - should keep records on any response (best response)
// just make sure NO_RECORDS rcode is mapped
//
// the only time we keep them is FAZ
// - ALLOW_EMPTY_AUTH flag set
// - sending FQDN (or more precisely doing single query)
//
precords = pBlob->pRecords;
if ( precords ) { if ( IsEmptyDnsResponse( precords ) ) { if ( (flagsIn & DNS_QUERY_ALLOW_EMPTY_AUTH_RESP) && ( (nameAttributes & DNS_NAME_IS_FQDN) || ((nameAttributes & DNS_NAME_MULTI_LABEL) && !g_AppendToMultiLabelName ) ) ) { // stop here as caller (probably FAZ code)
// wants to get the authority records
DNSDBG( QUERY, ( "Returning empty query response with authority records.\n" )); break; } else { Dns_RecordListFree( precords ); pBlob->pRecords = NULL; if ( status == NO_ERROR ) { status = DNS_INFO_NO_RECORDS; } } } }
// successful query -- done
if ( status == ERROR_SUCCESS ) { RTL_ASSERT( precords ); break; }
#if 0
//
// DCR_FIX0: lost adapter timeout from early in multi-name query
// - callback here or some other approach
//
// this is resolver version
//
// 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(); } }
//
// DCR_CLEANUP: lost intermediate timed out adapter deal
//
if ( status != NO_ERROR && (pnetInfo->ReturnFlags & DNS_FLAG_RESET_SERVER_PRIORITY) ) { Dns_DisableTimedOutAdapters( pnetInfo ); } #endif
//
// save first query error (for some errors)
//
if ( queryCount == 1 && ( status == DNS_ERROR_RCODE_NAME_ERROR || status == DNS_INFO_NO_RECORDS || status == DNS_ERROR_INVALID_NAME || status == DNS_ERROR_RCODE_SERVER_FAILURE || status == DNS_ERROR_RCODE_FORMAT_ERROR ) ) { DNSDBG( QUERY, ( "Saving bestQueryStatus %d\n", status )); bestQueryStatus = status; }
//
// continue with other queries on some errors
//
// on NAME_ERROR or NO_RECORDS response
// - check if this negative result will be
// cacheable, if it holds up
//
// note: the reason we check every time is that when the
// query involves several names, one or more may fail
// with one network timing out, YET the final name
// queried indeed is a NAME_ERROR everywhere; hence
// we can not do the check just once on the final
// negative response;
// in short, every negative response must be determinative
// in order for us to cache
//
if ( status == DNS_ERROR_RCODE_NAME_ERROR || status == DNS_INFO_NO_RECORDS ) { if ( fcacheNegative ) { fcacheNegative = IsCacheableNameError( pnetInfo ); } if ( status == DNS_INFO_NO_RECORDS ) { DNSDBG( QUERY, ( "Saving bestQueryStatus %d\n", status )); bestQueryStatus = status; } continue; } // server failure may indicate intermediate or remote
// server timeout and hence also makes any final
// name error determination uncacheable
else if ( status == DNS_ERROR_RCODE_SERVER_FAILURE ) { fcacheNegative = FALSE; continue; } // busted name errors
// - just continue with next query
else if ( status == DNS_ERROR_INVALID_NAME || status == DNS_ERROR_RCODE_FORMAT_ERROR ) { continue; } //
// other errors -- ex. timeout and winsock -- are terminal
//
else { fcacheNegative = FALSE; break; } }
DNSDBG( QUERY, ( "Query_Main() -- name loop termination\n" "\tstatus = %d\n" "\tquery count = %d\n", status, queryCount ));
//
// if no queries then invalid name
// - either name itself is invalid
// OR
// - single part name and don't have anything to append
//
DNS_ASSERT( queryCount != 0 || status == DNS_ERROR_INVALID_NAME );
//
// success -- prioritize record data
//
// to prioritize
// - prioritize is set
// - have more than one A record
// - can get IP list
//
// note: need the callback because resolver uses directly
// local copy of IP address info, whereas direct query
// RPC's a copy over from the resolver
//
// alternative would be some sort of "set IP source"
// function that resolver would call when there's a
// new list; then could have common function that
// picks up source if available or does RPC
//
if ( status == ERROR_SUCCESS ) { if ( g_PrioritizeRecordData && Dns_RecordListCount( precords, DNS_TYPE_A ) > 1 && pBlob->pfnGetAddrArray ) { PDNS_ADDR_ARRAY paddrArray = (pBlob->pfnGetAddrArray)();
if ( paddrArray ) { pBlob->pRecords = Dns_PrioritizeRecordSetEx( precords, paddrArray ); FREE_HEAP( paddrArray ); } } }
//
// no-op common negative response
// doing this for perf to skip extensive status code check below
//
else if ( status == DNS_ERROR_RCODE_NAME_ERROR || status == DNS_INFO_NO_RECORDS ) { // no-op
}
//
// timeout indicates possible network problem
// winsock errors indicate definite network problem
//
else if ( status == ERROR_TIMEOUT || status == WSAEFAULT || status == WSAENOTSOCK || status == WSAENETDOWN || status == WSAENETUNREACH || status == WSAEPFNOSUPPORT || status == WSAEAFNOSUPPORT || status == WSAEHOSTDOWN || status == WSAEHOSTUNREACH ) { pBlob->NetFailureStatus = status; }
#if 0
//
// DCR: not sure when to free message buffer
//
// - it is reused in Dns_QueryLib call, so no leak
// - point is when to return it
// - old QuickQueryEx() would dump when going around again?
// not sure of the point of that
//
//
// going around again -- free up message buffer
//
if ( ppMsgResponse && *ppMsgResponse ) { FREE_HEAP( *ppMsgResponse ); *ppMsgResponse = NULL; } #endif
//
// use NO-IP local name?
//
// if matched local name but had no IPs (IP6 currently)
// then use default here if not successful wire query
//
if ( pBlob->fNoIpLocal ) { if ( status != ERROR_SUCCESS ) { Dns_RecordListFree( pBlob->pRecords ); pBlob->pRecords = pBlob->pLocalRecords; status = ERROR_SUCCESS; pBlob->Status = status; } else { Dns_RecordListFree( pBlob->pLocalRecords ); pBlob->pLocalRecords = NULL; } }
//
// if error, use "best" error
// this is either
// - original query response
// - or NO_RECORDS response found later
//
if ( status != ERROR_SUCCESS && bestQueryStatus ) { status = bestQueryStatus; pBlob->Status = status; }
//
// set negative response cacheability
//
pBlob->fCacheNegative = fcacheNegative;
Done:
DNS_ASSERT( !pBlob->pLocalRecords || pBlob->pLocalRecords == pBlob->pRecords );
DNSDBG( TRACE, ( "Leave Query_Main()\n" "\tstatus = %d\n" "\ttime = %d\n", status, Dns_GetCurrentTimeInSeconds() )); IF_DNSDBG( QUERY ) { DnsDbg_QueryBlob( "Blob leaving Query_Main()", pBlob ); }
//
// DCR_HACK: remove me
//
// must return some records on success query
//
// not sure this is true on referral -- if so it's because we flag
// as referral
//
ASSERT( status != ERROR_SUCCESS || pBlob->pRecords != NULL );
return status; }
DNS_STATUS Query_InProcess( IN OUT PQUERY_BLOB pBlob ) /*++
Routine Description:
Main direct in-process query routine.
Arguments:
pBlob -- query info blob
Return Value:
ERROR_SUCCESS if successful. DNS RCODE error for RCODE response. DNS_INFO_NO_RECORDS for no records response. ERROR_TIMEOUT on complete lookup failure. ErrorCode on local failure.
--*/ { DNS_STATUS status = NO_ERROR; PDNS_NETINFO pnetInfo; PDNS_NETINFO pnetInfoLocal = NULL; PDNS_NETINFO pnetInfoOriginal; DNS_STATUS statusNetFailure = NO_ERROR;
DNSDBG( TRACE, ( "Query_InProcess( %p )\n", pBlob ));
//
// skip queries in "net down" situation
//
if ( IsKnownNetFailure() ) { status = GetLastError(); goto Cleanup; }
//
// get network info
//
pnetInfo = pnetInfoOriginal = pBlob->pNetworkInfo;
//
// explicit DNS server list -- build into network info
// - requires info from current list for search list or PDN
// - then dump current list and use private version
//
if ( pBlob->pDnsServers ) { pnetInfo = NetInfo_CreateFromIpArray( pBlob->pDnsServers, 0, // no specific server
TRUE, // build search info
pnetInfo // use existing netinfo
); if ( !pnetInfo ) { status = DNS_ERROR_NO_MEMORY; goto Cleanup; } pnetInfoLocal = pnetInfo; }
//
// no network info -- get it
//
else if ( !pnetInfo ) { pnetInfoLocal = pnetInfo = GetNetworkInfo(); if ( ! pnetInfo ) { status = DNS_ERROR_NO_DNS_SERVERS; goto Cleanup; } }
pBlob->pNetworkInfo = pnetInfo;
//
// make actual query to DNS servers
//
pBlob->pfnQueryCache = QueryHostFile; pBlob->pfnGetAddrArray = DnsGetLocalAddrArray;
status = Query_Main( pBlob );
//
// save net failure
// - but not if passed in network info
// only meaningful if its standard info
//
// DCR: fix statusNetFailure mess
//
if ( statusNetFailure ) { if ( !pBlob->pDnsServers ) { SetKnownNetFailure( status ); } }
//
// cleanup
//
Cleanup:
NetInfo_Free( pnetInfoLocal ); pBlob->pNetworkInfo = pnetInfoOriginal;
GUI_MODE_SETUP_WS_CLEANUP( g_InNTSetupMode );
return status; }
//
// Query utilities
//
DNS_STATUS GetDnsServerRRSet( OUT PDNS_RECORD * ppRecord, IN BOOLEAN fUnicode ) /*++
Routine Description:
Create record list of None.
Arguments:
None.
Return Value:
None.
--*/ { PDNS_NETINFO pnetInfo; DWORD iter; PDNS_RECORD prr; DNS_RRSET rrSet; DNS_CHARSET charSet = fUnicode ? DnsCharSetUnicode : DnsCharSetUtf8;
DNSDBG( QUERY, ( "GetDnsServerRRSet()\n" ));
DNS_RRSET_INIT( rrSet );
pnetInfo = GetNetworkInfo(); if ( !pnetInfo ) { goto Done; }
//
// loop through all adapters build record for each DNS server
//
for ( iter = 0; iter < pnetInfo->AdapterCount; iter++ ) { PDNS_ADAPTER padapter = pnetInfo->AdapterArray[iter]; PSTR pname; DWORD jiter;
if ( !padapter ) { continue; }
// DCR: goofy way to expose aliases
//
// if register the adapter's domain name, make it record name
// this
pname = padapter->pszAdapterDomain; if ( !pname || !( padapter->InfoFlags & DNS_FLAG_REGISTER_DOMAIN_NAME ) ) { pname = "."; }
for ( jiter = 0; jiter < padapter->ServerCount; jiter++ ) { // DCR: IP6 DNS servers
IP_UNION ipUnion;
IPUNION_SET_IP4( &ipUnion, padapter->ServerArray[jiter].IpAddress );
prr = Dns_CreateForwardRecord( pname, & ipUnion, 0, // no TTL
DnsCharSetUtf8, // name is UTF8
charSet // result set
); if ( prr ) { prr->Flags.S.Section = DNSREC_ANSWER; DNS_RRSET_ADD( rrSet, prr ); } } }
Done:
NetInfo_Free( pnetInfo );
*ppRecord = prr = rrSet.pFirstRR;
DNSDBG( QUERY, ( "Leave GetDnsServerRRSet() => %d\n", (prr ? ERROR_SUCCESS : DNS_ERROR_NO_DNS_SERVERS) ));
return (prr ? ERROR_SUCCESS : DNS_ERROR_NO_DNS_SERVERS); }
//
// DNS Query API
//
DNS_STATUS WINAPI privateNarrowToWideQuery( IN PCSTR pszName, IN WORD wType, IN DWORD Options, IN PIP_ARRAY pDnsServers OPTIONAL, OUT PDNS_RECORD * ppResultSet OPTIONAL, IN OUT PDNS_MSG_BUF * ppMessageResponse OPTIONAL, IN DNS_CHARSET CharSet ) /*++
Routine Description:
Convert narrow to wide query.
This routine simple avoids duplicate code in ANSI and UTF8 query routines.
Arguments:
pszName -- name to query
wType -- type of query
Options -- flags to query
pDnsServers -- array of DNS servers to use in query
ppResultSet -- addr to receive result DNS records
ppMessageResponse -- addr to receive response message
CharSet -- char set of original query
Return Value:
ERROR_SUCCESS on success. DNS RCODE error on query with RCODE DNS_INFO_NO_RECORDS on no records response. ErrorCode on failure.
--*/ { DNS_STATUS status = NO_ERROR; PDNS_RECORD prrList = NULL; PWSTR pwideName = NULL; WORD nameLength;
if ( !pszName ) { return ERROR_INVALID_PARAMETER; }
nameLength = (WORD) strlen( pszName );
pwideName = ALLOCATE_HEAP( (nameLength + 1) * sizeof(WCHAR) ); if ( !pwideName ) { return DNS_ERROR_NO_MEMORY; }
if ( !Dns_NameCopy( (PSTR) pwideName, NULL, (PSTR) pszName, nameLength, CharSet, DnsCharSetUnicode ) ) { status = ERROR_INVALID_NAME; goto Done; }
status = DnsQuery_W( pwideName, wType, Options, pDnsServers, ppResultSet ? &prrList : NULL, ppMessageResponse );
//
// convert result records back to ANSI (or UTF8)
//
if ( ppResultSet && prrList ) { *ppResultSet = Dns_RecordSetCopyEx( prrList, DnsCharSetUnicode, CharSet ); if ( ! *ppResultSet ) { status = DNS_ERROR_NO_MEMORY; } Dns_RecordListFree( prrList ); }
//
// cleanup
//
Done:
FREE_HEAP( pwideName );
return status; }
DNS_STATUS WINAPI DnsQuery_UTF8( IN PCSTR pszName, IN WORD wType, IN DWORD Options, IN PIP_ARRAY pDnsServers OPTIONAL, OUT PDNS_RECORD * ppResultSet OPTIONAL, IN OUT PDNS_MSG_BUF * ppMessageResponse OPTIONAL ) /*++
Routine Description:
Public UTF8 query.
Arguments:
pszName -- name to query
wType -- type of query
Options -- flags to query
pDnsServers -- array of DNS servers to use in query
ppResultSet -- addr to receive result DNS records
ppMessageResponse -- addr to receive response message
Return Value:
ERROR_SUCCESS on success. DNS RCODE error on query with RCODE DNS_INFO_NO_RECORDS on no records response. ErrorCode on failure.
--*/ { return privateNarrowToWideQuery( pszName, wType, Options, pDnsServers, ppResultSet, ppMessageResponse, DnsCharSetUtf8 ); }
DNS_STATUS WINAPI DnsQuery_A( IN PCSTR pszName, IN WORD wType, IN DWORD Options, IN PIP_ARRAY pDnsServers OPTIONAL, OUT PDNS_RECORD * ppResultSet OPTIONAL, IN OUT PDNS_MSG_BUF * ppMessageResponse OPTIONAL ) /*++
Routine Description:
Public ANSI query.
Arguments:
pszName -- name to query
wType -- type of query
Options -- flags to query
pDnsServers -- array of DNS servers to use in query
ppResultSet -- addr to receive result DNS records
ppMessageResponse -- addr to receive resulting message
Return Value:
ERROR_SUCCESS on success. DNS RCODE error on query with RCODE DNS_INFO_NO_RECORDS on no records response. ErrorCode on failure.
--*/ { return privateNarrowToWideQuery( pszName, wType, Options, pDnsServers, ppResultSet, ppMessageResponse, DnsCharSetAnsi ); }
DNS_STATUS WINAPI DnsQuery_W( IN PCWSTR pwsName, IN WORD wType, IN DWORD Options, IN PIP_ARRAY pDnsServers OPTIONAL, IN OUT PDNS_RECORD * ppResultSet OPTIONAL, IN OUT PDNS_MSG_BUF * ppMessageResponse OPTIONAL ) /*++
Routine Description:
Public unicode query API
Note, this unicode version is the main routine. The other public API call back through it.
Arguments:
pszName -- name to query
wType -- type of query
Options -- flags to query
pDnsServers -- array of DNS servers to use in query
ppResultSet -- addr to receive result DNS records
ppMessageResponse -- addr to receive resulting message
Return Value:
ERROR_SUCCESS on success. DNS RCODE error on query with RCODE DNS_INFO_NO_RECORDS on no records response. ErrorCode on failure.
--*/ { DNS_STATUS status = NO_ERROR; PDNS_NETINFO pnetInfo = NULL; PDNS_RECORD prpcRecord = NULL; DWORD rpcStatus = NO_ERROR; PQUERY_BLOB pblob; PWSTR pnameLocal = NULL;
DNSDBG( TRACE, ( "\n\nDnsQuery_W()\n" "\tName = %S\n" "\twType = %d\n" "\tOptions = %08x\n" "\tpDnsServers = %p\n" "\tppMessage = %p\n", pwsName, wType, Options, pDnsServers, ppMessageResponse ));
//
// must ask for some kind of results
//
if ( !ppResultSet && !ppMessageResponse ) { return ERROR_INVALID_PARAMETER; }
//
// NULL name indicates localhost lookup
//
// DCR: NULL name lookup for localhost could be improved
// - support NULL all the way through to wire
// - have local IP routines just accept it
//
if ( !pwsName ) { pnameLocal = (PWSTR) Reg_GetHostName( DnsCharSetUnicode ); if ( !pnameLocal ) { return DNS_ERROR_NAME_NOT_FOUND_LOCALLY; } pwsName = (PCWSTR) pnameLocal; Options |= DNS_QUERY_CACHE_ONLY; }
// clear OUT params
if ( ppResultSet ) { *ppResultSet = NULL; }
if ( ppMessageResponse ) { *ppMessageResponse = NULL; }
//
// IP string queries
//
if ( ppResultSet ) { PDNS_RECORD prr;
prr = Dns_CreateRecordForIpString_W( pwsName, wType, IPSTRING_RECORD_TTL ); if ( prr ) { *ppResultSet = prr; status = ERROR_SUCCESS; goto Done; } }
//
// empty type A query get DNS servers
//
// DCR_CLEANUP: DnsQuery empty name query for DNS servers?
// need better\safer approach to this
// is this SDK doc'd
//
if ( ppResultSet && !ppMessageResponse && wType == DNS_TYPE_A && ( !wcscmp( pwsName, L"" ) || !wcscmp( pwsName, DNS_SERVER_QUERY_NAME ) ) ) { status = GetDnsServerRRSet( ppResultSet, TRUE // unicode
); goto Done; }
//
// BYPASS_CACHE
// - required if want message buffer or specify server
// list -- just set flag
// - incompatible with CACHE_ONLY
// - required to get EMPTY_AUTH_RESPONSE
//
if ( ppMessageResponse || pDnsServers || (Options & DNS_QUERY_ALLOW_EMPTY_AUTH_RESP) ) { Options |= DNS_QUERY_BYPASS_CACHE; //Options |= DNS_QUERY_NO_CACHE_DATA;
}
//
// do direct query?
// - not RPC-able type
// - want message buffer
// - specifying DNS servers
// - want EMPTY_AUTH response records
//
// DCR: currently by-passing for type==ALL
// this may be too common to do that; may want to
// go to cache then determine if security records
// or other stuff require us to query in process
//
// DCR: not clear what the EMPTY_AUTH benefit is
//
// DCR: currently BYPASSing whenever BYPASS is set
// because otherwise we miss the hosts file
// if fix so lookup in cache, but screen off non-hosts
// data, then could resume going to cache
//
if ( !Dns_IsRpcRecordType(wType) && !(Options & DNS_QUERY_CACHE_ONLY) ) { goto InProcessQuery; }
if ( Options & DNS_QUERY_BYPASS_CACHE ) #if 0
if ( (Options & DNS_QUERY_BYPASS_CACHE) && ( ppMessageResponse || pDnsServers || (Options & DNS_QUERY_ALLOW_EMPTY_AUTH_RESP) ) ) #endif
{ if ( Options & DNS_QUERY_CACHE_ONLY ) { status = ERROR_INVALID_PARAMETER; goto Done; } goto InProcessQuery; }
//
// querying through cache
// - get cluster-filtering info
//
if ( g_IsServer ) { ENVAR_DWORD_INFO filterInfo;
Reg_ReadDwordEnvar( RegIdFilterClusterIp, &filterInfo );
if ( filterInfo.fFound && filterInfo.Value ) { Options |= DNSP_QUERY_FILTER_CLUSTER; } }
rpcStatus = NO_ERROR;
RpcTryExcept { status = R_ResolverQuery( NULL, (PWSTR) pwsName, wType, Options, &prpcRecord ); } RpcExcept( DNS_RPC_EXCEPTION_FILTER ) { rpcStatus = RpcExceptionCode(); } RpcEndExcept
//
// cache unavailable
// - bail if just querying cache
// - otherwise query direct
if ( rpcStatus != NO_ERROR ) { DNSDBG( TRACE, ( "DnsQuery_W() RPC failed status = %d\n", rpcStatus )); goto InProcessQuery; } if ( status == DNS_ERROR_NO_TCPIP ) { DNSDBG( TRACE, ( "DnsQuery_W() NO_TCPIP error!\n" "\tassume resolver security problem -- query in process!\n" )); RTL_ASSERT( !prpcRecord ); goto InProcessQuery; }
//
// return records
// - screen out empty-auth responses
//
// DCR_FIX1: cache should convert and return NO_RECORDS response
// directly (no need to do this here)
//
// DCR: UNLESS we allow return of these records
//
if ( prpcRecord ) { FixupNameOwnerPointers( prpcRecord );
if ( IsEmptyDnsResponseFromResolver( prpcRecord ) ) { Dns_RecordListFree( prpcRecord ); prpcRecord = NULL; if ( status == NO_ERROR ) { status = DNS_INFO_NO_RECORDS; } } *ppResultSet = prpcRecord; } RTL_ASSERT( status!=NO_ERROR || prpcRecord ); goto Done;
//
// query directly -- either skipping cache or it's unavailable
//
InProcessQuery:
DNSDBG( TRACE, ( "DnsQuery_W() -- doing in process query\n" "\tpname = %S\n" "\ttype = %d\n", pwsName, wType ));
//
// load query blob
//
// DCR: set some sort of "want message buffer" flag if ppMessageResponse
// exists
//
pblob = ALLOCATE_HEAP_ZERO( sizeof(*pblob) ); if ( !pblob ) { status = DNS_ERROR_NO_MEMORY; goto Done; }
pblob->pNameOrig = (PWSTR) pwsName; pblob->wType = wType; pblob->Flags = Options | DNSQUERY_UNICODE_OUT; pblob->pDnsServers = pDnsServers;
//
// query
// - then set OUT params
status = Query_InProcess( pblob );
if ( ppResultSet ) { *ppResultSet = pblob->pRecords; RTL_ASSERT( status!=NO_ERROR || *ppResultSet ); } else { Dns_RecordListFree( pblob->pRecords ); }
if ( ppMessageResponse ) { *ppMessageResponse = pblob->pRecvMsg; }
FREE_HEAP( pblob );
Done:
// sanity check
if ( status==NO_ERROR && ppResultSet && !*ppResultSet ) { RTL_ASSERT( FALSE ); status = DNS_INFO_NO_RECORDS; }
if ( pnameLocal ) { FREE_HEAP( pnameLocal ); }
DNSDBG( TRACE, ( "Leave DnsQuery_W()\n" "\tstatus = %d\n" "\tresult set = %p\n\n\n", status, *ppResultSet ));
return( status ); }
//
// DnsQueryEx() routines
//
DNS_STATUS WINAPI ShimDnsQueryEx( IN OUT PDNS_QUERY_INFO pQueryInfo ) /*++
Routine Description:
Query DNS -- shim for main SDK query routine.
Arguments:
pQueryInfo -- blob describing query
Return Value:
ERROR_SUCCESS if successful query. Error code on failure.
--*/ { PDNS_RECORD prrResult; WORD type = pQueryInfo->Type; DNS_STATUS status; DNS_LIST listAnswer; DNS_LIST listAlias; DNS_LIST listAdditional; DNS_LIST listAuthority;
DNSDBG( TRACE, ( "ShimDnsQueryEx()\n" ));
//
// DCR: temp hack is to pass this to DNSQuery
//
status = DnsQuery_W( (PWSTR) pQueryInfo->pName, type, pQueryInfo->Flags, pQueryInfo->pDnsServers, & prrResult, NULL );
pQueryInfo->Status = status;
//
// cut result records appropriately
//
pQueryInfo->pAnswerRecords = NULL; pQueryInfo->pAliasRecords = NULL; pQueryInfo->pAdditionalRecords = NULL; pQueryInfo->pAuthorityRecords = NULL;
if ( prrResult ) { PDNS_RECORD prr; PDNS_RECORD pnextRR;
DNS_LIST_STRUCT_INIT( listAnswer ); DNS_LIST_STRUCT_INIT( listAlias ); DNS_LIST_STRUCT_INIT( listAdditional ); DNS_LIST_STRUCT_INIT( listAuthority );
//
// break list into section specific lists
// - section 0 for hostfile records
// - note, this does pull RR sets apart, but
// they, being in same section, should immediately
// be rejoined
//
pnextRR = prrResult; while ( prr = pnextRR ) { pnextRR = prr->pNext; prr->pNext = NULL; if ( prr->Flags.S.Section == 0 || prr->Flags.S.Section == DNSREC_ANSWER ) { if ( prr->wType == DNS_TYPE_CNAME && type != DNS_TYPE_CNAME ) { DNS_LIST_STRUCT_ADD( listAlias, prr ); continue; } else { DNS_LIST_STRUCT_ADD( listAnswer, prr ); continue; } } else if ( prr->Flags.S.Section == DNSREC_ADDITIONAL ) { DNS_LIST_STRUCT_ADD( listAdditional, prr ); continue; } else { DNS_LIST_STRUCT_ADD( listAuthority, prr ); continue; } }
// pack stuff back into blob
pQueryInfo->pAnswerRecords = listAnswer.pFirst; pQueryInfo->pAliasRecords = listAlias.pFirst; pQueryInfo->pAuthorityRecords = listAuthority.pFirst; pQueryInfo->pAdditionalRecords = listAdditional.pFirst; //pQueryInfo->pSigRecords = listSig.pFirst;
//
// convert result records back to ANSI (or UTF8)
// - convert each result set
// - then paste back into query blob
//
// DCR_FIX0: handle issue of failure on conversion
//
if ( pQueryInfo->CharSet != DnsCharSetUnicode ) { PDNS_RECORD prr; PDNS_RECORD * prrSetPtr;
prrSetPtr = & pQueryInfo->pAnswerRecords; for ( prrSetPtr = & pQueryInfo->pAnswerRecords; prrSetPtr <= & pQueryInfo->pAdditionalRecords; prrSetPtr++ ) { prr = *prrSetPtr; *prrSetPtr = Dns_RecordSetCopyEx( prr, DnsCharSetUnicode, pQueryInfo->CharSet ); Dns_RecordListFree( prr ); } } }
//
// replace name for originally narrow queries
//
if ( pQueryInfo->CharSet != DnsCharSetUnicode ) { ASSERT( pQueryInfo->CharSet != 0 ); ASSERT( pQueryInfo->pReservedName != NULL );
FREE_HEAP( pQueryInfo->pName ); pQueryInfo->pName = (LPTSTR) pQueryInfo->pReservedName; pQueryInfo->pReservedName = NULL; }
//
// indicate return if async
//
if ( pQueryInfo->hEvent ) { SetEvent( pQueryInfo->hEvent ); }
return( status ); }
DNS_STATUS WINAPI CombinedQueryEx( IN OUT PDNS_QUERY_INFO pQueryInfo, IN DNS_CHARSET CharSet ) /*++
Routine Description:
Convert narrow to wide query.
This routine simple avoids duplicate code in ANSI and UTF8 query routines.
Arguments:
pQueryInfo -- query info blob
CharSet -- char set of original query
Return Value:
ERROR_SUCCESS on success. DNS RCODE error on query with RCODE DNS_INFO_NO_RECORDS on no records response. ErrorCode on failure.
--*/ { DNS_STATUS status = NO_ERROR; PWSTR pwideName = NULL; HANDLE hthread; DWORD threadId;
DNSDBG( TRACE, ( "CombinedQueryEx( %S%s, type=%d, flag=%08x, event=%p )\n", PRINT_STRING_WIDE_CHARSET( pQueryInfo->pName, CharSet ), PRINT_STRING_ANSI_CHARSET( pQueryInfo->pName, CharSet ), pQueryInfo->Type, pQueryInfo->Flags, pQueryInfo->hEvent ));
//
// set CharSet
//
pQueryInfo->CharSet = CharSet;
if ( CharSet == DnsCharSetUnicode ) { pQueryInfo->pReservedName = 0; }
//
// if narrow name
// - allocate wide name copy
// - swap in wide name and make query wide
//
// DCR: allow NULL name? for local machine name?
//
else if ( CharSet == DnsCharSetAnsi || CharSet == DnsCharSetUtf8 ) { WORD nameLength; PSTR pnameNarrow;
pnameNarrow = pQueryInfo->pName; if ( !pnameNarrow ) { return ERROR_INVALID_PARAMETER; } nameLength = (WORD) strlen( pnameNarrow ); pwideName = ALLOCATE_HEAP( (nameLength + 1) * sizeof(WCHAR) ); if ( !pwideName ) { return DNS_ERROR_NO_MEMORY; } if ( !Dns_NameCopy( (PSTR) pwideName, NULL, pnameNarrow, nameLength, CharSet, DnsCharSetUnicode ) ) { status = ERROR_INVALID_NAME; goto Failed; }
pQueryInfo->pName = (LPTSTR) pwideName; pQueryInfo->pReservedName = pnameNarrow; }
//
// async?
// - if event exists we are async
// - spin up thread and call it
//
if ( pQueryInfo->hEvent ) { hthread = CreateThread( NULL, // no security
0, // default stack
ShimDnsQueryEx, pQueryInfo, // param
0, // run immediately
& threadId ); if ( !hthread ) { DNS_STATUS status = GetLastError();
DNSDBG( ANY, ( "Failed to create thread = %d\n", status ));
if ( status == ERROR_SUCCESS ) { status = DNS_ERROR_NO_MEMORY; } goto Failed; }
CloseHandle( hthread ); return( ERROR_IO_PENDING ); }
//
// otherwise make direct async call
//
return ShimDnsQueryEx( pQueryInfo );
Failed:
FREE_HEAP( pwideName ); return( status ); }
DNS_STATUS WINAPI DnsQueryExW( IN OUT PDNS_QUERY_INFO pQueryInfo ) /*++
Routine Description:
Query DNS -- main SDK query routine.
Arguments:
pQueryInfo -- blob describing query
Return Value:
ERROR_SUCCESS if successful query. ERROR_IO_PENDING if successful async start. Error code on failure.
--*/ { DNSDBG( TRACE, ( "DnsQueryExW( %S, type=%d, flag=%08x, event=%p )\n", pQueryInfo->pName, pQueryInfo->Type, pQueryInfo->Flags, pQueryInfo->hEvent ));
return CombinedQueryEx( pQueryInfo, DnsCharSetUnicode ); }
DNS_STATUS WINAPI DnsQueryExA( IN OUT PDNS_QUERY_INFO pQueryInfo ) /*++
Routine Description:
Query DNS -- main SDK query routine.
Arguments:
pQueryInfo -- blob describing query
Return Value:
ERROR_SUCCESS if successful query. ERROR_IO_PENDING if successful async start. Error code on failure.
--*/ { DNSDBG( TRACE, ( "DnsQueryExA( %s, type=%d, flag=%08x, event=%p )\n", pQueryInfo->pName, pQueryInfo->Type, pQueryInfo->Flags, pQueryInfo->hEvent ));
return CombinedQueryEx( pQueryInfo, DnsCharSetAnsi ); }
DNS_STATUS WINAPI DnsQueryExUTF8( IN OUT PDNS_QUERY_INFO pQueryInfo ) /*++
Routine Description:
Query DNS -- main SDK query routine.
Arguments:
pQueryInfo -- blob describing query
Return Value:
ERROR_SUCCESS if successful query. ERROR_IO_PENDING if successful async start. Error code on failure.
--*/ { DNSDBG( TRACE, ( "DnsQueryExUTF8( %s, type=%d, flag=%08x, event=%p )\n", pQueryInfo->pName, pQueryInfo->Type, pQueryInfo->Flags, pQueryInfo->hEvent ));
return CombinedQueryEx( pQueryInfo, DnsCharSetUtf8 ); }
//
// Name collision API
//
// DCR_QUESTION: name collision -- is there any point to this?
//
DNS_STATUS WINAPI DnsCheckNameCollision_UTF8( IN PCSTR pszName, IN DWORD Options ) /*++
Routine Description:
None.
Arguments:
None.
Return Value:
None.
--*/ { DNS_STATUS status = NO_ERROR; PDNS_RECORD prrList = NULL; PDNS_RECORD prr = NULL; DWORD iter; BOOL fmatch = FALSE; WORD wtype = DNS_TYPE_A;
if ( !pszName ) { return ERROR_INVALID_PARAMETER; }
if ( Options & DNS_CHECK_AGAINST_HOST_ANY ) { wtype = DNS_TYPE_ANY; }
status = DnsQuery_UTF8( pszName, wtype, DNS_QUERY_BYPASS_CACHE, NULL, &prrList, NULL );
if ( status == DNS_ERROR_RCODE_NAME_ERROR || status == DNS_INFO_NO_RECORDS ) { Dns_RecordListFree( prrList ); return NO_ERROR; }
if ( status == NO_ERROR && Options == DNS_CHECK_AGAINST_HOST_ANY ) { Dns_RecordListFree( prrList ); return DNS_ERROR_RCODE_YXRRSET; }
if ( status == NO_ERROR && Options == DNS_CHECK_AGAINST_HOST_DOMAIN_NAME ) { char TestName[DNS_MAX_NAME_LENGTH * 2]; PSTR pszHostName = Reg_GetHostName( DnsCharSetUtf8 ); PSTR pszPrimaryDomain = Reg_GetPrimaryDomainName( DnsCharSetUtf8 );
fmatch = TRUE;
strcpy( TestName, pszHostName );
if ( pszPrimaryDomain ) { strcat( TestName, "." ); strcat( TestName, pszPrimaryDomain ); }
if ( Dns_NameCompare_UTF8( pszHostName, (PSTR)pszName ) ) { fmatch = TRUE; }
if ( !fmatch && pszPrimaryDomain && Dns_NameCompare_UTF8( TestName, (PSTR)pszName ) ) { fmatch = TRUE; }
if ( !fmatch ) { PDNS_NETINFO pNetInfo = GetNetworkInfo();
if ( pNetInfo ) { for ( iter = 0; iter < pNetInfo->AdapterCount; iter++ ) { PSTR pszDomain = pNetInfo->AdapterArray[iter]-> pszAdapterDomain; if ( pszDomain ) { strcpy( TestName, pszHostName ); strcat( TestName, "." ); strcat( TestName, pszDomain );
if ( Dns_NameCompare_UTF8( TestName, (PSTR)pszName ) ) fmatch = TRUE; } } }
NetInfo_Free( pNetInfo ); }
FREE_HEAP( pszHostName ); FREE_HEAP( pszPrimaryDomain );
if ( fmatch ) { Dns_RecordListFree( prrList ); return DNS_ERROR_RCODE_YXRRSET; } }
if ( status == NO_ERROR ) { PDNS_ADDRESS_INFO pAddressInfo = NULL; DWORD Count = DnsGetIpAddressInfoList( &pAddressInfo );
if ( Count == 0 ) { Dns_RecordListFree( prrList ); return DNS_ERROR_RCODE_YXRRSET; }
prr = prrList;
while ( prr ) { fmatch = FALSE;
if ( prr->Flags.S.Section != DNSREC_ANSWER ) { prr = prr->pNext; continue; }
if ( prr->wType == DNS_TYPE_CNAME ) { FREE_HEAP( pAddressInfo ); Dns_RecordListFree( prrList ); return DNS_ERROR_RCODE_YXRRSET; }
for ( iter = 0; iter < Count; iter++ ) { if ( prr->Data.A.IpAddress == pAddressInfo[iter].ipAddress ) { fmatch = TRUE; } }
if ( !fmatch ) { FREE_HEAP( pAddressInfo ); Dns_RecordListFree( prrList ); return DNS_ERROR_RCODE_YXRRSET; }
prr = prr->pNext; }
FREE_HEAP( pAddressInfo ); Dns_RecordListFree( prrList ); return NO_ERROR; }
return status; }
DNS_STATUS WINAPI DnsCheckNameCollision_A( IN PCSTR pszName, IN DWORD Options ) /*++
Routine Description:
None.
Arguments:
None.
Return Value:
None.
--*/ { PSTR pUtf8Name = NULL; DNS_STATUS status = NO_ERROR;
//
// DCR_CLEANUP: fix unnecessary alloc
// DCR_PERF: eliminate alloc
//
if ( !pszName ) { return ERROR_INVALID_PARAMETER; }
pUtf8Name = Dns_NameCopyAllocate( (PSTR) pszName, 0, DnsCharSetAnsi, DnsCharSetUtf8 ); if ( !pUtf8Name ) { return DNS_ERROR_NO_MEMORY; }
status = DnsCheckNameCollision_UTF8( pUtf8Name, Options );
FREE_HEAP( pUtf8Name );
return status; }
DNS_STATUS WINAPI DnsCheckNameCollision_W( IN PCWSTR pszName, IN DWORD Options ) /*++
Routine Description:
None.
Arguments:
None.
Return Value:
None.
--*/ { //
// DCR_CLEANUP: fix unnecessary alloc
// DCR_PERF: eliminate alloc
//
DNS_STATUS status = NO_ERROR; PSTR lpTempName = NULL; WORD nameLength;
if ( !pszName ) { return ERROR_INVALID_PARAMETER; }
nameLength = (WORD)wcslen( pszName );
lpTempName = ALLOCATE_HEAP( (nameLength + 1) * sizeof(WCHAR) );
if ( lpTempName == NULL ) { return DNS_ERROR_NO_MEMORY; }
Dns_NameCopy( lpTempName, NULL, (PSTR) pszName, 0, DnsCharSetUnicode, DnsCharSetUtf8 );
status = DnsCheckNameCollision_UTF8( lpTempName, Options );
FREE_HEAP( lpTempName );
return status; }
//
// Roll your own query utilities
//
BOOL WINAPI DnsWriteQuestionToBuffer_W( IN OUT PDNS_MESSAGE_BUFFER pDnsBuffer, IN OUT LPDWORD pdwBufferSize, IN PWSTR pszName, IN WORD wType, IN WORD Xid, IN BOOL fRecursionDesired ) /*++
Routine Description:
None.
Arguments:
None.
Return Value:
None.
--*/ { //
// DCR_CLEANUP: duplicate code with routine below ... surprise!
// - eliminate duplicate
// - probably can just pick up library routine
//
PCHAR pch; PCHAR pbufferEnd = NULL;
if ( *pdwBufferSize >= DNS_MAX_UDP_PACKET_BUFFER_LENGTH ) { pbufferEnd = (PCHAR)pDnsBuffer + *pdwBufferSize;
// clear header
RtlZeroMemory( pDnsBuffer, sizeof(DNS_HEADER) );
// set for rewriting
pch = pDnsBuffer->MessageBody;
// write question name
pch = Dns_WriteDottedNameToPacket( pch, pbufferEnd, (PCHAR) pszName, NULL, 0, TRUE );
if ( !pch ) { return FALSE; }
// write question structure
*(UNALIGNED WORD *) pch = htons( wType ); pch += sizeof(WORD); *(UNALIGNED WORD *) pch = DNS_RCLASS_INTERNET; pch += sizeof(WORD);
// set question RR section count
pDnsBuffer->MessageHead.QuestionCount = htons( 1 ); pDnsBuffer->MessageHead.RecursionDesired = (BOOLEAN)fRecursionDesired; pDnsBuffer->MessageHead.Xid = htons( Xid );
*pdwBufferSize = (DWORD)(pch - (PCHAR)pDnsBuffer);
return TRUE; } else { *pdwBufferSize = DNS_MAX_UDP_PACKET_BUFFER_LENGTH; return FALSE; } }
BOOL WINAPI DnsWriteQuestionToBuffer_UTF8( IN OUT PDNS_MESSAGE_BUFFER pDnsBuffer, IN OUT PDWORD pdwBufferSize, IN PSTR pszName, IN WORD wType, IN WORD Xid, IN BOOL fRecursionDesired ) /*++
Routine Description:
None.
Arguments:
None.
Return Value:
None.
--*/ { PCHAR pch; PCHAR pbufferEnd = NULL;
if ( *pdwBufferSize >= DNS_MAX_UDP_PACKET_BUFFER_LENGTH ) { pbufferEnd = (PCHAR)pDnsBuffer + *pdwBufferSize;
// clear header
RtlZeroMemory( pDnsBuffer, sizeof(DNS_HEADER) );
// set for rewriting
pch = pDnsBuffer->MessageBody;
// write question name
pch = Dns_WriteDottedNameToPacket( pch, pbufferEnd, pszName, NULL, 0, FALSE );
if ( !pch ) { return FALSE; }
// write question structure
*(UNALIGNED WORD *) pch = htons( wType ); pch += sizeof(WORD); *(UNALIGNED WORD *) pch = DNS_RCLASS_INTERNET; pch += sizeof(WORD);
// set question RR section count
pDnsBuffer->MessageHead.QuestionCount = htons( 1 ); pDnsBuffer->MessageHead.RecursionDesired = (BOOLEAN)fRecursionDesired; pDnsBuffer->MessageHead.Xid = htons( Xid );
*pdwBufferSize = (DWORD)(pch - (PCHAR)pDnsBuffer);
return TRUE; } else { *pdwBufferSize = DNS_MAX_UDP_PACKET_BUFFER_LENGTH; return FALSE; } }
//
// Record list to\from results
//
VOID CombineRecordsInBlob( IN PDNS_RESULTS pResults, OUT PDNS_RECORD * ppRecords ) /*++
Routine Description:
Query DNS -- shim for main SDK query routine.
Arguments:
pQueryInfo -- blob describing query
Return Value:
ERROR_SUCCESS if successful query. Error code on failure.
--*/ { PDNS_RECORD prr;
DNSDBG( TRACE, ( "CombineRecordsInBlob()\n" ));
//
// combine records back into one list
//
// note, working backwards so only touch records once
//
prr = Dns_RecordListAppend( pResults->pAuthorityRecords, pResults->pAdditionalRecords );
prr = Dns_RecordListAppend( pResults->pAnswerRecords, prr );
prr = Dns_RecordListAppend( pResults->pAliasRecords, prr );
*ppRecords = prr; }
VOID BreakRecordsIntoBlob( OUT PDNS_RESULTS pResults, IN PDNS_RECORD pRecords, IN WORD wType ) /*++
Routine Description:
Break single record list into results pblob->
Arguments:
pResults -- results to fill in
pRecords -- record list
Return Value:
None
--*/ { PDNS_RECORD prr; PDNS_RECORD pnextRR; DNS_LIST listAnswer; DNS_LIST listAlias; DNS_LIST listAdditional; DNS_LIST listAuthority;
DNSDBG( TRACE, ( "BreakRecordsIntoBlob()\n" ));
//
// clear blob
//
RtlZeroMemory( pResults, sizeof(*pResults) );
//
// init building lists
//
DNS_LIST_STRUCT_INIT( listAnswer ); DNS_LIST_STRUCT_INIT( listAlias ); DNS_LIST_STRUCT_INIT( listAdditional ); DNS_LIST_STRUCT_INIT( listAuthority );
//
// break list into section specific lists
// - note, this does pull RR sets apart, but
// they, being in same section, should immediately
// be rejoined
//
// - note, hostfile records made have section=0
// this is no longer the case but preserve until
// know this is solid and determine what section==0
// means
//
pnextRR = pRecords; while ( prr = pnextRR ) { pnextRR = prr->pNext; prr->pNext = NULL; if ( prr->Flags.S.Section == 0 || prr->Flags.S.Section == DNSREC_ANSWER ) { if ( prr->wType == DNS_TYPE_CNAME && wType != DNS_TYPE_CNAME ) { DNS_LIST_STRUCT_ADD( listAlias, prr ); continue; } else { DNS_LIST_STRUCT_ADD( listAnswer, prr ); continue; } } else if ( prr->Flags.S.Section == DNSREC_ADDITIONAL ) { DNS_LIST_STRUCT_ADD( listAdditional, prr ); continue; } else { DNS_LIST_STRUCT_ADD( listAuthority, prr ); continue; } }
// pack stuff into blob
pResults->pAnswerRecords = listAnswer.pFirst; pResults->pAliasRecords = listAlias.pFirst; pResults->pAuthorityRecords = listAuthority.pFirst; pResults->pAdditionalRecords = listAdditional.pFirst; }
//
// Random discontinued
//
#if 0
IP_ADDRESS findHostIpInRecordList( IN PDNS_RECORD pRecord, IN PDNS_NAME pszHostName ) /*++
Routine Description:
Find IP for hostname, if its A record is in list.
Arguments:
pRecord - incoming RR set
pszHostName - hostname to find
Return Value:
IP address matching hostname, if A record for hostname found. Zero if not found.
--*/ { //
// DCR: find best A record for name
// currently only finds first; this is harmless but in
// disjoint net situation would need sorted to find best
//
while ( pRecord ) { if ( pRecord->wType == DNS_TYPE_A && Dns_NameCompare( pRecord->pName, pszHostName ) ) { return( pRecord->Data.A.IpAddress ); } pRecord = pRecord->pNext; } return( 0 ); } #endif
//
// End query.c
//
|