mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3765 lines
81 KiB
3765 lines
81 KiB
/*++
|
|
|
|
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
|
|
//
|
|
|
|
|