/*++ 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" // NetInfo_AdapterLoopStart( pNetInfo ); while( padapter = NetInfo_GetNextAdapter( pNetInfo ) ) { if ( ( padapter->InfoFlags & AINFO_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->pDnsAddrs ) { 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", padapter->InterfaceIndex, padapter->pszAdapterGuidName, padapter->Status, padapter->RunFlags )); return FALSE; } return TRUE; } VOID query_PrioritizeRecords( IN OUT PQUERY_BLOB pBlob ) /*++ Routine Description: Prioritize records in query result. Arguments: pBlob -- query info blob Return Value: None --*/ { PDNS_RECORD prr; DNSDBG( TRACE, ( "query_PrioritizeRecords( %p )\n", pBlob )); // // 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 // // DCR: FIX6: don't prioritize local results // DCR: FIX6: prioritize ONLY when SETS in list > 1 record // if ( !g_PrioritizeRecordData ) { return; } prr = pBlob->pRecords; if ( Dns_RecordListCount( prr, DNS_TYPE_A ) > 1 ) { PDNS_ADDR_ARRAY paddrArray; // create local addr array from netinfo blob paddrArray = NetInfo_CreateLocalAddrArray( pBlob->pNetInfo, NULL, // no specific adapter name NULL, // no specific adapter AF_INET, FALSE // no cluster addrs ); // prioritize against local addrs pBlob->pRecords = Dns_PrioritizeRecordList( prr, paddrArray ); FREE_HEAP( paddrArray ); } } // // Query name building utils // BOOL ValidateQueryTld( IN PWSTR 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_IsNameNumericW( pTld ) ) { return FALSE; } } // // bogus TLDs // if ( g_ScreenBadTlds & DNS_TLD_SCREEN_WORKGROUP ) { if ( Dns_NameCompare_W( L"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_W( L"domain", pTld )) { return FALSE; } } if ( g_ScreenBadTlds & DNS_TLD_SCREEN_OFFICE ) { if ( Dns_NameCompare_W( L"office", pTld )) { return FALSE; } } if ( g_ScreenBadTlds & DNS_TLD_SCREEN_HOME ) { if ( Dns_NameCompare_W( L"home", pTld )) { return FALSE; } } return TRUE; } BOOL ValidateQueryName( IN PQUERY_BLOB pBlob, IN PWSTR pName, IN PWSTR 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; PWSTR pnameTld; PWSTR 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_GetTldForNameW( pName ); // // if no domain appended // - exclude single label // - exclude bad TLD (numeric, bogus domain) // - but allow root queries // // DCR: MS DCS screening // screen // _msdcs. // 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_GetTldForNameW( pDomain ); if ( !pdomainTld ) { pdomainTld = pDomain; } if ( !ValidateQueryTld( pdomainTld ) ) { goto Failed; } // screen repeated TLD if ( g_ScreenBadTlds & DNS_TLD_SCREEN_REPEATED ) { if ( Dns_NameCompare_W( pnameTld, pdomainTld ) ) { goto Failed; } } return TRUE; Failed: DNSDBG( QUERY, ( "Failed invalid query name:\n" "\tname %S\n" "\tdomain %S\n", pName, pDomain )); return FALSE; } PWSTR 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; PWSTR pqueryDomain = NULL; PDNS_ADAPTER padapter; 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 // NetInfo_AdapterLoopStart( pNetInfo ); while( padapter = NetInfo_GetNextAdapter( pNetInfo ) ) { PWSTR pdomain; // // clear single-name-query-specific flags // these flags are set for each name, determining // whether adapter participates // 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 // // DCR: problem with adapter domain names on "ignored adapters" // - we'd like to keep adapter in query if other adapter has the name // - we'd like to query name on this adapter if we absolutely run // out of other names to query // if ( (padapter->InfoFlags & AINFO_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_W( 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 ) { NetInfo_AdapterLoopStart( pNetInfo ); while( padapter = NetInfo_GetNextAdapter( pNetInfo ) ) { 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; } PWSTR 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. --*/ { PWSTR psearchName; PWSTR 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 & NINFO_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 ); } PWSTR 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 --*/ { PWSTR pnameOrig = pBlob->pNameOrig; PWSTR pdomainName = NULL; PWSTR pnameBuf; DWORD queryCount = pBlob->QueryCount; DWORD nameAttributes = pBlob->NameAttributes; DNSDBG( TRACE, ( "GetNextQueryName( %p )\n", pBlob )); // default suffix flags pBlob->NameFlags = 0; // // DCR: cannonical name // probably should canonicalize original name first\once // // DCR: multiple checks on original name // the way this works we repeatedly get the TLD and do // check on orginal name // // DCR: if fail to validate\append ANY domain, then will // fail -- should make sure INVALID_NAME is the result // // // 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->NameBuffer; while ( 1 ) { pdomainName = GetNextDomainNameToAppend( pBlob->pNetInfo, & 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_W( 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 PCHAR pszQuestionName, IN WORD wQuestionType, IN PDNS_RECORD pRecords, IN DWORD dwFlags, IN PIP4_ARRAY aipServerList, IN OUT PDNS_NETINFO pNetInfo ) /*++ Routine Description: Query. DCR: remove EXPORTED: QueryDirectEx (dnsup.exe) 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 aipServerList -- 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, aipServerList, pNetInfo )); // // build send packet // psendMsg = Dns_BuildPacket( pHeader, fNoHeaderCounts, (PDNS_NAME) 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 ); { SEND_BLOB sendBlob; RtlZeroMemory( &sendBlob, sizeof(sendBlob) ); sendBlob.pSendMsg = psendMsg; sendBlob.pServ4List = aipServerList; sendBlob.Flags = dwFlags; sendBlob.fSaveResponse = (ppMsgResponse != NULL); sendBlob.fSaveRecords = (ppResponseRecords != NULL); sendBlob.Results.pMessage = (ppMsgResponse) ? *ppMsgResponse : NULL; status = Send_AndRecv( &sendBlob ); if ( ppMsgResponse ) { *ppMsgResponse = sendBlob.Results.pMessage; } if ( ppResponseRecords ) { *ppResponseRecords = sendBlob.Results.pRecords; } } 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 (PDNS_NAME) pBlob->pNameQuery, pBlob->wType, NULL, // no records flags | DNSQUERY_UNICODE_NAME, 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 ); { SEND_BLOB sendBlob; RtlZeroMemory( &sendBlob, sizeof(sendBlob) ); sendBlob.pSendMsg = psendMsg; sendBlob.pNetInfo = pBlob->pNetInfo; sendBlob.pServerList = pBlob->pServerList; sendBlob.pServ4List = pBlob->pServerList4; sendBlob.Flags = flags; sendBlob.fSaveResponse = (flags & DNS_QUERY_RETURN_MESSAGE); sendBlob.fSaveRecords = TRUE; status = Send_AndRecv( &sendBlob ); pBlob->pRecvMsg = sendBlob.Results.pMessage; pBlob->pRecords = sendBlob.Results.pRecords; } 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; PWSTR 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->pNetInfo; DWORD nameLength; DWORD bufLength; DWORD queryCount; DNSDBG( TRACE, ( "\n\nQuery_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; // // DCR: canonicalize original name? // #if 0 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; #endif // // determine name properties // - determines number and order of name queries // nameAttributes = Dns_GetNameAttributesW( pBlob->pNameOrig ); 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 == HostsFile_Query && ! (flagsIn & DNS_QUERY_NO_HOSTS_FILE) ) { pBlob->pNameQuery = pBlob->pNameOrig; if ( HostsFile_Query( pBlob ) ) { status = pBlob->Status; goto Done; } } // // check for local name // - if successful, skip wire query // if ( ! (flagsIn & DNS_QUERY_NO_LOCAL_NAME) ) { status = Local_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 ) { PWSTR 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->pNameQuery = 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 & RUN_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 & RUN_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 ) { query_PrioritizeRecords( pBlob ); } #if 0 // // 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; } #endif #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 ); // // check no-servers failure // if ( status != ERROR_SUCCESS && pnetInfo && (pnetInfo->InfoFlags & NINFO_FLAG_NO_DNS_SERVERS) ) { DNSDBG( TRACE, ( "Replacing query status %d with NO_DNS_SERVERS.\n", status )); status = DNS_ERROR_NO_DNS_SERVERS; pBlob->Status = status; pBlob->fCacheNegative = FALSE; } 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; PDNS_ADDR_ARRAY pservArray = NULL; PDNS_ADDR_ARRAY pallocServArray = NULL; DNSDBG( TRACE, ( "Query_InProcess( %p )\n", pBlob )); // // get network info // pnetInfo = pnetInfoOriginal = pBlob->pNetInfo; // // skip queries in "net down" situation // if ( IsKnownNetFailure() ) { status = GetLastError(); goto Cleanup; } // // 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 // // DCR: could functionalize -- netinfo, right from server lists // pservArray = pBlob->pServerList; if ( !pservArray ) { pallocServArray = Util_GetAddrArray( NULL, // no copy issue NULL, // no addr array pBlob->pServerList4, NULL // no extra info ); pservArray = pallocServArray; } if ( pservArray ) { pnetInfo = NetInfo_CreateFromAddrArray( pservArray, 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; } } // // make actual query to DNS servers // // note: at this point we MUST have network info // and resolved any server list issues // (including extracting imbedded extra info) // pBlob->pNetInfo = pnetInfo; pBlob->pServerList = NULL; pBlob->pServerList4 = NULL; pBlob->pfnQueryCache = HostsFile_Query; status = Query_Main( pBlob ); // // save net failure // - but not if passed in network info // only meaningful if its standard info // if ( pBlob->NetFailureStatus && !pBlob->pServerList ) { SetKnownNetFailure( status ); } // // cleanup // Cleanup: DnsAddrArray_Free( pallocServArray ); NetInfo_Free( pnetInfoLocal ); pBlob->pNetInfo = 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; PDNS_ADAPTER padapter; 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 // NetInfo_AdapterLoopStart( pnetInfo ); while( padapter = NetInfo_GetNextAdapter( pnetInfo ) ) { PDNS_ADDR_ARRAY pserverArray; PWSTR pname; DWORD jiter; pserverArray = padapter->pDnsAddrs; if ( !pserverArray ) { continue; } // DCR: goofy way to expose aliases // // if register the adapter's domain name, make it record name // this // // FIX6: do we need to expose IP6 DNS servers this way? // pname = padapter->pszAdapterDomain; if ( !pname || !( padapter->InfoFlags & AINFO_FLAG_REGISTER_DOMAIN_NAME ) ) { pname = L"."; } for ( jiter = 0; jiter < pserverArray->AddrCount; jiter++ ) { prr = Dns_CreateForwardRecord( (PDNS_NAME) pname, DNS_TYPE_A, & pserverArray->AddrArray[jiter], 0, // no TTL DnsCharSetUnicode, // name is unicode 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_STATUS Query_CheckIp6Literal( IN PCWSTR pwsName, IN WORD wType, OUT PDNS_RECORD * ppResultSet ) /*++ Routine Description: Check for\handle UPNP literal hack. Arguments: Return Value: NO_ERROR if not literal. DNS_ERROR_RCODE_NAME_ERROR if literal but bad type. DNS_INFO_NUMERIC_NAME if good data -- convert this to NO_ERROR for response. ErrorCode if have literal, but can't build record. --*/ { SOCKADDR_IN6 sockAddr; DNS_STATUS status; DNSDBG( QUERY, ( "Query_CheckIp6Literal( %S, %d )\n", pwsName, wType )); // // check for literal // if ( ! Dns_Ip6LiteralNameToAddress( & sockAddr, pwsName ) ) { return NO_ERROR; } // // if found literal, but not AAAA query -- done // if ( wType != DNS_TYPE_AAAA ) { status = DNS_ERROR_RCODE_NAME_ERROR; goto Done; } // // build AAAA record // status = DNS_ERROR_NUMERIC_NAME; if ( ppResultSet ) { PDNS_RECORD prr; prr = Dns_CreateAAAARecord( (PDNS_NAME) pwsName, * (PIP6_ADDRESS) &sockAddr.sin6_addr, IPSTRING_RECORD_TTL, DnsCharSetUnicode, DnsCharSetUnicode ); if ( !prr ) { status = DNS_ERROR_NO_MEMORY; } *ppResultSet = prr; } Done: DNSDBG( QUERY, ( "Leave Query_CheckIp6Literal( %S, %d ) => %d\n", pwsName, wType, status )); return status; } // // DNS Query API // DNS_STATUS WINAPI Query_PrivateExW( IN PCWSTR pwsName, IN WORD wType, IN DWORD Options, IN PADDR_ARRAY pServerList OPTIONAL, IN PIP4_ARRAY pServerList4 OPTIONAL, OUT PDNS_RECORD * ppResultSet OPTIONAL, IN OUT PDNS_MSG_BUF * ppMessageResponse OPTIONAL ) /*++ Routine Description: Private query interface. This working code for the DnsQuery() public API Arguments: pszName -- name to query wType -- type of query Options -- flags to query pServersIp6 -- 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; BOOL femptyName = FALSE; DNSDBG( TRACE, ( "\n\nQuery_PrivateExW()\n" "\tName = %S\n" "\twType = %d\n" "\tOptions = %08x\n" "\tpServerList = %p\n" "\tpServerList4 = %p\n" "\tppMessage = %p\n", pwsName, wType, Options, pServerList, pServerList4, ppMessageResponse )); // clear OUT params if ( ppResultSet ) { *ppResultSet = NULL; } if ( ppMessageResponse ) { *ppMessageResponse = NULL; } // // must ask for some kind of results // if ( !ppResultSet && !ppMessageResponse ) { return ERROR_INVALID_PARAMETER; } // // map WIRE_ONLY flag // if ( Options & DNS_QUERY_WIRE_ONLY ) { Options |= DNS_QUERY_BYPASS_CACHE; Options |= DNS_QUERY_NO_HOSTS_FILE; Options |= DNS_QUERY_NO_LOCAL_NAME; } // // 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 // // // empty string name // // 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? (hope not!) // if ( pwsName ) { femptyName = !wcscmp( pwsName, L"" ); if ( !(Options & DNSQUERY_NO_SERVER_RECORDS) && ( femptyName || !wcscmp( pwsName, DNS_SERVER_QUERY_NAME ) ) && wType == DNS_TYPE_A && !ppMessageResponse && ppResultSet ) { status = GetDnsServerRRSet( ppResultSet, TRUE // unicode ); goto Done; } } // // NULL or empty treated as local host // if ( !pwsName || femptyName ) { pnameLocal = (PWSTR) Reg_GetHostName( DnsCharSetUnicode ); if ( !pnameLocal ) { return DNS_ERROR_NAME_NOT_FOUND_LOCALLY; } pwsName = (PCWSTR) pnameLocal; Options |= DNS_QUERY_CACHE_ONLY; goto SkipLiterals; } // // 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; } } // // UPNP IP6 literal hack // status = Query_CheckIp6Literal( pwsName, wType, ppResultSet ); if ( status != NO_ERROR ) { if ( status == DNS_ERROR_NUMERIC_NAME ) { DNS_ASSERT( wType == DNS_TYPE_AAAA ); DNS_ASSERT( !ppResultSet || *ppResultSet ); status = NO_ERROR; } goto Done; } SkipLiterals: // // cluster filtering? // if ( g_IsServer && (Options & DNSP_QUERY_INCLUDE_CLUSTER) ) { ENVAR_DWORD_INFO filterInfo; Reg_ReadDwordEnvar( RegIdFilterClusterIp, &filterInfo ); if ( filterInfo.fFound && filterInfo.Value ) { Options &= ~DNSP_QUERY_INCLUDE_CLUSTER; } } // // 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 || pServerList || pServerList4 || (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 || pServerList || (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 // 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, ( "Query_PrivateExW() RPC failed status = %d\n", rpcStatus )); goto InProcessQuery; } if ( status == DNS_ERROR_NO_TCPIP ) { DNSDBG( TRACE, ( "Query_PrivateExW() 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, ( "Query_PrivateExW() -- 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->Flags = Options; pblob->pServerList = pServerList; pblob->pServerList4 = pServerList4; // // 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 Query_PrivateExW()\n" "\tstatus = %d\n" "\tresult set = %p\n\n\n", status, *ppResultSet )); return( status ); } DNS_STATUS WINAPI Query_Shim( IN DNS_CHARSET CharSet, IN PCSTR pszName, IN WORD wType, IN DWORD Options, IN PDNS_ADDR_ARRAY pServList OPTIONAL, IN PIP4_ARRAY pServList4 OPTIONAL, OUT PDNS_RECORD * ppResultSet OPTIONAL, IN OUT PDNS_MSG_BUF * ppMessageResponse OPTIONAL ) /*++ Routine Description: Convert narrow to wide query. This routine handles narron-to-wide conversions to calling into Query_PrivateExW() which does the real work. Arguments: CharSet -- char set of original query pszName -- name to query wType -- type of query Options -- flags to query pServList -- array of DNS servers to use in query pServList4 -- 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. --*/ { DNS_STATUS status = NO_ERROR; PDNS_RECORD prrList = NULL; PWSTR pwideName = NULL; WORD nameLength; if ( !pszName ) { return ERROR_INVALID_PARAMETER; } // // name conversion // if ( CharSet == DnsCharSetUnicode ) { pwideName = (PWSTR) pszName; } else { 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 = Query_PrivateExW( pwideName, wType, Options, pServList, pServList4, ppResultSet ? &prrList : NULL, ppMessageResponse ); // // convert result records back to ANSI (or UTF8) // if ( ppResultSet && prrList ) { if ( CharSet == DnsCharSetUnicode ) { *ppResultSet = prrList; } else { *ppResultSet = Dns_RecordSetCopyEx( prrList, DnsCharSetUnicode, CharSet ); if ( ! *ppResultSet ) { status = DNS_ERROR_NO_MEMORY; } Dns_RecordListFree( prrList ); } } // // cleanup // Done: if ( pwideName != (PWSTR)pszName ) { FREE_HEAP( pwideName ); } return status; } DNS_STATUS WINAPI Query_Private( IN PCWSTR pszName, IN WORD wType, IN DWORD Options, IN PADDR_ARRAY pServerList, OPTIONAL OUT PDNS_RECORD * ppResultSet OPTIONAL ) /*++ Routine Description: Dnsapi internal query routine for update\FAZ routines. Thin wrapper on Query_Shim. --*/ { return Query_Shim( DnsCharSetUnicode, (PCHAR) pszName, wType, Options, pServerList, NULL, // no IP4 list ppResultSet, NULL // no message ); } // // SDK query API // DNS_STATUS WINAPI DnsQuery_UTF8( IN PCSTR pszName, IN WORD wType, IN DWORD Options, IN PIP4_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 Query_Shim( DnsCharSetUtf8, pszName, wType, Options, NULL, // no non-IP4 server list support pDnsServers, ppResultSet, ppMessageResponse ); } DNS_STATUS WINAPI DnsQuery_A( IN PCSTR pszName, IN WORD wType, IN DWORD Options, IN PIP4_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 Query_Shim( DnsCharSetAnsi, pszName, wType, Options, NULL, // no non-IP4 server list support pDnsServers, ppResultSet, ppMessageResponse ); } DNS_STATUS WINAPI DnsQuery_W( IN PCWSTR pwsName, IN WORD wType, IN DWORD Options, IN PIP4_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. --*/ { return Query_PrivateExW( pwsName, wType, Options, NULL, // no non-IP4 server list support pDnsServers, ppResultSet, ppMessageResponse ); } // // 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 = NULL; 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 -- pass to private query routine // status = Query_PrivateExW( (PWSTR) pQueryInfo->pName, type, pQueryInfo->Flags, pQueryInfo->pServerList, pQueryInfo->pServerListIp4, & 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 * 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 = (PSTR) 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 ) { 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 ); } // // 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 blob. 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; } // // Name collision API // // DCR_QUESTION: name collision -- is there any point to this? // DCR: eliminate NameCollision_UTF8() // DNS_STATUS WINAPI DnsCheckNameCollision_W( IN PCWSTR pszName, IN DWORD Options ) /*++ Routine Description: None. DCR: Check name collision IP4 only 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; PDNS_NETINFO pnetInfo = NULL; PDNS_ADDR_ARRAY plocalArray = NULL; if ( !pszName ) { return ERROR_INVALID_PARAMETER; } if ( Options == DNS_CHECK_AGAINST_HOST_ANY ) { wtype = DNS_TYPE_ANY; } // // query against name // status = DnsQuery_W( pszName, wtype, DNS_QUERY_BYPASS_CACHE, NULL, &prrList, NULL ); if ( status != NO_ERROR ) { if ( status == DNS_ERROR_RCODE_NAME_ERROR || status == DNS_INFO_NO_RECORDS ) { status = NO_ERROR; } goto Done; } // // HOST_ANY -- fails if any records // if ( Options == DNS_CHECK_AGAINST_HOST_ANY ) { status = DNS_ERROR_RCODE_YXRRSET; goto Done; } // // DCR: eliminate CheckNameCollision with DNS_CHECK_AGAINST_HOST_DOMAIN_NAME flag? // // not sure there are ANY callers with this flag as // the flag is always TRUE in NT5->today and no one has complained // if ( Options == DNS_CHECK_AGAINST_HOST_DOMAIN_NAME ) { WCHAR nameFull[ DNS_MAX_NAME_BUFFER_LENGTH ]; PWSTR phostName = (PWSTR) Reg_GetHostName( DnsCharSetUnicode ); PWSTR pprimaryName = (PWSTR) Reg_GetPrimaryDomainName( DnsCharSetUnicode ); PWSTR pdomainName = pprimaryName; // DCR: busted test both here and in NT5 fmatch = TRUE; if ( Dns_NameCompare_W( phostName, pszName ) ) { fmatch = TRUE; } // check against full primary name else if ( pdomainName && Dns_NameAppend_W( nameFull, DNS_MAX_NAME_BUFFER_LENGTH, phostName, pdomainName ) && Dns_NameCompare_W( nameFull, pszName ) ) { fmatch = TRUE; } // // DCR: if save this, functionalize as name check against netinfo // could use in local ip // could just return rank\adapter // if ( !fmatch ) { pnetInfo = GetNetworkInfo(); if ( pnetInfo ) { PDNS_ADAPTER padapter; NetInfo_AdapterLoopStart( pnetInfo ); while( padapter = NetInfo_GetNextAdapter( pnetInfo ) ) { pdomainName = padapter->pszAdapterDomain; if ( pdomainName && Dns_NameAppend_W( nameFull, DNS_MAX_NAME_BUFFER_LENGTH, phostName, pdomainName ) && Dns_NameCompare_W( nameFull, pszName ) ) { fmatch = TRUE; break; } } } } FREE_HEAP( phostName ); FREE_HEAP( pprimaryName ); if ( fmatch ) { status = DNS_ERROR_RCODE_YXRRSET; goto Done; } } // // checking against local address records // plocalArray = NetInfo_GetLocalAddrArray( pnetInfo, NULL, // no specific adapter 0, // no specific family 0, // no flags FALSE // no force ); if ( !plocalArray ) { status = DNS_ERROR_RCODE_YXRRSET; goto Done; } prr = prrList; while ( prr ) { if ( prr->Flags.S.Section != DNSREC_ANSWER ) { prr = prr->pNext; continue; } if ( prr->wType == DNS_TYPE_CNAME ) { status = DNS_ERROR_RCODE_YXRRSET; goto Done; } if ( prr->wType == DNS_TYPE_A && !DnsAddrArray_ContainsIp4( plocalArray, prr->Data.A.IpAddress ) ) { status = DNS_ERROR_RCODE_YXRRSET; goto Done; } prr = prr->pNext; } // matched all address Done: Dns_RecordListFree( prrList ); NetInfo_Free( pnetInfo ); DnsAddrArray_Free( plocalArray ); return status; } DNS_STATUS WINAPI DnsCheckNameCollision_A( IN PCSTR pszName, IN DWORD Options ) /*++ Routine Description: None. Arguments: None. Return Value: None. --*/ { PWSTR pname; DNS_STATUS status = NO_ERROR; // // convert to unicode and call // if ( !pszName ) { return ERROR_INVALID_PARAMETER; } pname = Dns_NameCopyAllocate( (PSTR) pszName, 0, DnsCharSetAnsi, DnsCharSetUnicode ); if ( !pname ) { return DNS_ERROR_NO_MEMORY; } status = DnsCheckNameCollision_W( pname, Options ); FREE_HEAP( pname ); return status; } DNS_STATUS WINAPI DnsCheckNameCollision_UTF8( IN PCSTR pszName, IN DWORD Options ) /*++ Routine Description: None. Arguments: None. Return Value: None. --*/ { PWSTR pname; DNS_STATUS status = NO_ERROR; // // convert to unicode and call // if ( !pszName ) { return ERROR_INVALID_PARAMETER; } pname = Dns_NameCopyAllocate( (PSTR) pszName, 0, DnsCharSetUtf8, DnsCharSetUnicode ); if ( !pname ) { return DNS_ERROR_NO_MEMORY; } status = DnsCheckNameCollision_W( pname, Options ); FREE_HEAP( pname ); return status; } // // End query.c //