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.
4861 lines
118 KiB
4861 lines
118 KiB
/*++
|
|
|
|
Copyright (c) 1996-2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
send.c
|
|
|
|
Abstract:
|
|
|
|
Domain Name System (DNS) API
|
|
|
|
Send response routines.
|
|
|
|
Author:
|
|
|
|
Jim Gilroy (jamesg) October, 1996
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#include "local.h"
|
|
|
|
|
|
//
|
|
// Disjoint name space
|
|
//
|
|
// If DNS name space is disjoint then NAME_ERROR response from one
|
|
// adapter does NOT necessarily mean that name does not exist. Rather
|
|
// must continue on other adapters.
|
|
//
|
|
// This flag should be set if name space is disjoint, off otherwise.
|
|
//
|
|
// DCR_PERF: auto-detect disjoint name space (really cool)
|
|
// DCR_ENHANCE: auto-detect disjoint name space (really cool)
|
|
// initially continue trying on other adapters and if they always
|
|
// coincide, then conclude non-disjoint (and turn off)
|
|
//
|
|
// DCR_ENHANCE: registry turn off of disjoint name space
|
|
//
|
|
// Note: should consider that name spaces often disjoint in that
|
|
// Intranet is hidden from Internet
|
|
//
|
|
|
|
BOOL fDisjointNameSpace = TRUE;
|
|
|
|
//
|
|
// Query \ response IP matching.
|
|
//
|
|
// Some resolvers (Win95) have required matching between DNS server IP
|
|
// queried and response. This flag allows this matching to be turned on.
|
|
// Better now than requiring SP later.
|
|
//
|
|
// DCR_ENHANCE: registry enable query\response IP matching.
|
|
//
|
|
|
|
BOOL fQueryIpMatching = FALSE;
|
|
|
|
|
|
//
|
|
// Timeouts
|
|
//
|
|
|
|
#define HARD_TIMEOUT_LIMIT 16 // 16 seconds, total of 31 seconds
|
|
#define INITIAL_UPDATE_TIMEOUT 2 // 3 seconds
|
|
#define MAX_UPDATE_TIMEOUT 24 // 24 seconds
|
|
#define DNS_MAX_QUERY_TIMEOUTS 10 // 10
|
|
#define ONE_HOUR_TIMEOUT 60*60 // One hour
|
|
|
|
// TCP timeout 10 seconds to come back
|
|
|
|
#define DEFAULT_TCP_TIMEOUT 10
|
|
|
|
|
|
// Retry limits
|
|
|
|
#define MAX_SINGLE_SERVER_RETRY (3)
|
|
|
|
|
|
#define NT_TCPIP_REG_LOCATION "System\\CurrentControlSet\\Services\\Tcpip\\Parameters"
|
|
#define DNS_QUERY_TIMEOUTS "DnsQueryTimeouts"
|
|
#define DNS_QUICK_QUERY_TIMEOUTS "DnsQuickQueryTimeouts"
|
|
#define DNS_MULTICAST_QUERY_TIMEOUTS "DnsMulticastQueryTimeouts"
|
|
|
|
//
|
|
// Timeouts
|
|
// MUST have terminating 0, this signals end of timeouts.
|
|
// This is better than a timeout limit as different query types can
|
|
// have different total retries.
|
|
//
|
|
|
|
DWORD QueryTimeouts[] =
|
|
{
|
|
1, // NT5 1,
|
|
1, // 2,
|
|
2, // 2,
|
|
4, // 4,
|
|
7, // 8,
|
|
0
|
|
};
|
|
|
|
DWORD RegistryQueryTimeouts[DNS_MAX_QUERY_TIMEOUTS + 1];
|
|
LPDWORD g_QueryTimeouts = QueryTimeouts;
|
|
|
|
DWORD QuickQueryTimeouts[] =
|
|
{
|
|
1,
|
|
2,
|
|
2,
|
|
0
|
|
};
|
|
|
|
DWORD RegistryQuickQueryTimeouts[DNS_MAX_QUERY_TIMEOUTS + 1];
|
|
LPDWORD g_QuickQueryTimeouts = QuickQueryTimeouts;
|
|
|
|
//
|
|
// Update timeouts.
|
|
// Must be long enough to handle zone lock on primary for XFR
|
|
// or time required for DS write.
|
|
//
|
|
|
|
DWORD UpdateTimeouts[] =
|
|
{
|
|
5,
|
|
10,
|
|
20,
|
|
0
|
|
};
|
|
|
|
//
|
|
// Multicast Query timeouts.
|
|
// Local only. 1sec timeout, three retries.
|
|
//
|
|
|
|
DWORD MulticastQueryTimeouts[] =
|
|
{
|
|
1,
|
|
1,
|
|
1,
|
|
0
|
|
};
|
|
|
|
DWORD RegistryMulticastQueryTimeouts[DNS_MAX_QUERY_TIMEOUTS + 1];
|
|
LPDWORD g_MulticastQueryTimeouts = MulticastQueryTimeouts;
|
|
|
|
|
|
//
|
|
// Query flag
|
|
//
|
|
// Flags that terminate query on adapter
|
|
|
|
#define RUN_FLAG_COMBINED_IGNORE_ADAPTER \
|
|
(RUN_FLAG_IGNORE_ADAPTER | RUN_FLAG_STOP_QUERY_ON_ADAPTER)
|
|
|
|
|
|
//
|
|
// Authoritative empty response
|
|
// - map to NXRRSET for tracking in send code
|
|
//
|
|
|
|
#define DNS_RCODE_AUTH_EMPTY_RESPONSE (DNS_RCODE_NXRRSET)
|
|
|
|
|
|
|
|
|
|
//
|
|
// Dummy no-send-to-this-server error code
|
|
//
|
|
|
|
#define DNS_ERROR_NO_SEND ((DWORD)(-100))
|
|
|
|
|
|
|
|
//
|
|
// OPT failure tracking
|
|
//
|
|
|
|
BOOL
|
|
Send_IsServerOptExclude(
|
|
IN PDNS_ADDR pAddr
|
|
);
|
|
|
|
VOID
|
|
Send_SetServerOptExclude(
|
|
IN PDNS_ADDR pAddr
|
|
);
|
|
|
|
|
|
|
|
//
|
|
// Netinfo send\recv utils
|
|
//
|
|
|
|
VOID
|
|
serverPriorityChange(
|
|
IN OUT PDNS_NETINFO pNetInfo,
|
|
IN OUT PDNS_ADAPTER pAdapter,
|
|
IN OUT PDNS_ADDR pServer,
|
|
IN DWORD NewPriority
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Changing server priority.
|
|
|
|
Arguments:
|
|
|
|
pNetInfo -- netinfo
|
|
|
|
pAdapter -- server's adapter
|
|
|
|
pServer -- server
|
|
|
|
NewPriority -- new priority
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// priority change
|
|
// - no change if loopback
|
|
// - otherwise
|
|
// - set priority
|
|
// - mark netinfo\adapter to save change
|
|
//
|
|
|
|
if ( !DnsAddr_IsLoopback( pServer, 0 ) )
|
|
{
|
|
pServer->Priority = NewPriority;
|
|
pAdapter->RunFlags |= RUN_FLAG_RESET_SERVER_PRIORITY;
|
|
pNetInfo->ReturnFlags |= RUN_FLAG_RESET_SERVER_PRIORITY;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
timeoutDnsServers(
|
|
IN PDNS_NETINFO pNetInfo,
|
|
IN DWORD dwTimeout
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Mark a DNS server that timed out.
|
|
|
|
Arguments:
|
|
|
|
pNetInfo -- struct with list of DNS servers
|
|
|
|
dwTimeout -- timeout in seconds
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PDNS_ADAPTER padapter;
|
|
PDNS_ADDR_ARRAY pserverArray;
|
|
PDNS_ADDR pserver;
|
|
DWORD lastSendIndex;
|
|
DWORD i;
|
|
DWORD j;
|
|
|
|
DNSDBG( SEND, (
|
|
"Enter timeoutDnsServers( %p, timeout=%d )\n",
|
|
pNetInfo,
|
|
dwTimeout ));
|
|
|
|
DNS_ASSERT( pNetInfo );
|
|
|
|
//
|
|
// find DNS server in list,
|
|
// -- drop its priority based on timeout
|
|
// -- if already has RCODE, then did not time out
|
|
//
|
|
// if change a priority, then set flag at top of adapter list, so
|
|
// that global copy may be updated
|
|
//
|
|
|
|
for ( i=0; i<pNetInfo->AdapterCount; i++ )
|
|
{
|
|
padapter = NetInfo_GetAdapterByIndex( pNetInfo, i );
|
|
|
|
// no sends on this adapter?
|
|
|
|
if ( !(padapter->RunFlags & RUN_FLAG_SENT_THIS_RETRY) )
|
|
{
|
|
DNSDBG( SEND, (
|
|
"No sends this retry on adapter %d\n",
|
|
padapter->InterfaceIndex ));
|
|
continue;
|
|
}
|
|
pserverArray = padapter->pDnsAddrs;
|
|
if ( !pserverArray )
|
|
{
|
|
DNSDBG( SEND, (
|
|
"WARNING: Adapter %d -- no DNS servers!\n",
|
|
padapter->InterfaceIndex ));
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// find DNS servers sent to
|
|
//
|
|
// - if it responded with status, then it didn't timeout
|
|
// (if responded with success, then query completed and
|
|
// we wouldn't be in this function)
|
|
//
|
|
// - go "easy" on OPT sends;
|
|
// don't drop priority, just note timeout
|
|
//
|
|
|
|
for ( j=0; j<pserverArray->AddrCount; j++ )
|
|
{
|
|
pserver = &pserverArray->AddrArray[j];
|
|
|
|
if ( TEST_SERVER_STATE(pserver, SRVFLAG_SENT_THIS_RETRY) )
|
|
{
|
|
DNSDBG( SEND, (
|
|
"Timeout on server %p (padapter=%p)\n",
|
|
pserver,
|
|
padapter ));
|
|
|
|
if ( TEST_SERVER_STATE(pserver, SRVFLAG_SENT_NON_OPT) )
|
|
{
|
|
SET_SERVER_STATE( pserver, SRVFLAG_TIMEOUT_NON_OPT );
|
|
|
|
serverPriorityChange(
|
|
pNetInfo,
|
|
padapter,
|
|
pserver,
|
|
pserver->Priority - dwTimeout - SRVPRI_TIMEOUT_DROP );
|
|
}
|
|
else
|
|
{
|
|
DNSDBG( SEND, (
|
|
"Timeout on server %p OPT only\n",
|
|
pserver ));
|
|
|
|
SET_SERVER_STATE( pserver, SRVFLAG_TIMEOUT_OPT );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
resetOnFinalTimeout(
|
|
IN PDNS_NETINFO pNetInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Markup network info on final timeout.
|
|
|
|
Arguments:
|
|
|
|
pNetInfo -- struct with list of DNS servers
|
|
|
|
dwTimeout -- timeout in seconds
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
DWORD i;
|
|
PDNS_ADAPTER padapter;
|
|
|
|
//
|
|
// We've timed out against all DNS server for a least
|
|
// one of the adapters. Update adapter status to show
|
|
// time out error.
|
|
//
|
|
// DCR: is final timeout correct
|
|
// - worried about timeout on some but not all servers
|
|
// case; adapter shouldn't show timeout should it?
|
|
//
|
|
|
|
for ( i=0; i<pNetInfo->AdapterCount; i++ )
|
|
{
|
|
padapter = NetInfo_GetAdapterByIndex( pNetInfo, i );
|
|
|
|
if ( padapter->Status == NO_ERROR &&
|
|
padapter->RunFlags & (RUN_FLAG_RESET_SERVER_PRIORITY | RUN_FLAG_SENT) )
|
|
{
|
|
padapter->Status = ERROR_TIMEOUT;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
verifyValidServer(
|
|
IN PDNS_NETINFO pNetInfo,
|
|
IN PDNS_ADDR pAddr
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Verify valid server IP.
|
|
|
|
DCR: we have no way to do this for IP6 yet.
|
|
|
|
Arguments:
|
|
|
|
pNetInfo -- struct with list of DNS servers
|
|
|
|
pAddr -- address of responding server
|
|
|
|
Return Value:
|
|
|
|
TRUE -- valid server.
|
|
FALSE -- bad \ unknown server.
|
|
|
|
--*/
|
|
{
|
|
PDNS_ADAPTER padapter;
|
|
DWORD i;
|
|
|
|
DNSDBG( SEND, (
|
|
"Enter verifyValidServer( %p, %s )\n",
|
|
pNetInfo,
|
|
DNSADDR_STRING(pAddr) ));
|
|
|
|
DNS_ASSERT( pNetInfo );
|
|
|
|
//
|
|
// accept any IP6 response
|
|
//
|
|
// DCR: IP6 server validity
|
|
// will need to determine
|
|
// - when we have fixed (global) IP6 address only
|
|
// - or have locked down default addresses already
|
|
// - otherwise will have to accept reponses to default
|
|
// query ... but even there could screen on interface
|
|
//
|
|
|
|
if ( DnsAddr_IsIp6(pAddr) )
|
|
{
|
|
DNSDBG( SEND, ( "Accepting IP6 address as valid server address.\n" ));
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// find DNS server in list
|
|
//
|
|
|
|
for ( i=0; i<pNetInfo->AdapterCount; i++ )
|
|
{
|
|
padapter = NetInfo_GetAdapterByIndex( pNetInfo, i );
|
|
|
|
if ( DnsAddrArray_ContainsAddr(
|
|
padapter->pDnsAddrs,
|
|
pAddr,
|
|
DNSADDR_MATCH_ADDR ) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
DNSDBG( SEND, (
|
|
"WARNING: address %s NOT found in network info!!!\n",
|
|
DNSADDR_STRING(pAddr) ));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
DNS_STATUS
|
|
resetServerAfterRecv(
|
|
IN PDNS_NETINFO pNetInfo,
|
|
IN PDNS_ADDR pAddr,
|
|
IN DNS_STATUS Status
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reset priority on DNS server that sent response.
|
|
|
|
Arguments:
|
|
|
|
pNetInfo -- struct with list of DNS servers
|
|
|
|
pAddr -- address of responding server
|
|
|
|
Status -- RCODE of response
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if continue query.
|
|
DNS_ERROR_RCODE_NAME_ERROR if all (valid) adapters have name-error or auth-empty response.
|
|
|
|
--*/
|
|
{
|
|
DWORD i;
|
|
DNS_STATUS result = DNS_ERROR_RCODE_NAME_ERROR;
|
|
BOOL fterminalNameError = FALSE;
|
|
#if DBG
|
|
BOOL freset = FALSE;
|
|
#endif
|
|
|
|
DNSDBG( SEND, (
|
|
"Enter resetServerAfterRecv( %p, %s rcode=%d)\n",
|
|
pNetInfo,
|
|
DNSADDR_STRING(pAddr),
|
|
Status ));
|
|
|
|
DNS_ASSERT( pNetInfo );
|
|
|
|
//
|
|
// find DNS server in list, clear its priority field
|
|
//
|
|
// note: going through full list here after found DNS
|
|
// as no guarantee that lists do not overlap (IP6 default
|
|
// servers almost certainly do);
|
|
//
|
|
// this avoids
|
|
// - starving DNS by failing to update priority
|
|
// - avoids unnecessary sends when NAME_ERROR would terminate
|
|
// send on adapter with duplicate DNS
|
|
//
|
|
|
|
for ( i=0; i<pNetInfo->AdapterCount; i++ )
|
|
{
|
|
PDNS_ADAPTER padapter;
|
|
PDNS_ADDR_ARRAY pserverArray;
|
|
DWORD j;
|
|
PDNS_ADDR pserver;
|
|
DWORD newPriority;
|
|
BOOL fpriReset = FALSE;
|
|
|
|
padapter = NetInfo_GetAdapterByIndex( pNetInfo, i );
|
|
|
|
pserverArray = padapter->pDnsAddrs;
|
|
if ( !pserverArray )
|
|
{
|
|
DNSDBG( SEND, (
|
|
"WARNING: Adapter %d -- no DNS servers!\n",
|
|
padapter->InterfaceIndex ));
|
|
continue;
|
|
}
|
|
|
|
for ( j=0; j<pserverArray->AddrCount; j++ )
|
|
{
|
|
pserver = &pserverArray->AddrArray[j];
|
|
|
|
if ( !DnsAddr_IsEqual( pAddr, pserver, DNSADDR_MATCH_ADDR ) )
|
|
{
|
|
continue;
|
|
}
|
|
pserver->Status = Status;
|
|
#if DBG
|
|
freset = TRUE;
|
|
#endif
|
|
//
|
|
// no DNS running
|
|
//
|
|
// WSAECONNRESET reported for reception of ICMP unreachable, so
|
|
// no DNS is currently running on the IP; that's a severe
|
|
// priority drop, worse than just TIMEOUT
|
|
//
|
|
|
|
if ( Status == WSAECONNRESET )
|
|
{
|
|
newPriority = pserver->Priority - SRVPRI_NO_DNS_DROP;
|
|
fpriReset = TRUE;
|
|
break;
|
|
}
|
|
|
|
// if SERVER_FAILURE rcode, may or may not indicate problem,
|
|
// (may be simply unable to contact remote DNS)
|
|
// but it certainly suggests trying other DNS servers in
|
|
// the list first
|
|
//
|
|
// DCR_FIX: SEVRFAIL response priority reset
|
|
// the explicitly correct approach would be to flag the
|
|
// SERVER_FAILURE error, but NOT reset the priority unless
|
|
// at the end of the query, we find another server in the list
|
|
// got a useful response
|
|
|
|
if ( Status == DNS_ERROR_RCODE_SERVER_FAILURE )
|
|
{
|
|
newPriority = pserver->Priority - SRVPRI_SERVFAIL_DROP;
|
|
fpriReset = TRUE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// other status code indicates functioning DNS server,
|
|
// - reset the server's priority
|
|
|
|
if ( (LONG)pserver->Priority < SRVPRI_RESPONSE )
|
|
{
|
|
newPriority = SRVPRI_RESPONSE;
|
|
fpriReset = TRUE;
|
|
}
|
|
|
|
//
|
|
// NAME_ERROR or AUTH-EMPTY response
|
|
// - save to server list for adapter to eliminate all
|
|
// further retries on this adapter's list
|
|
// - if not waiting for all adapters, then
|
|
// NAME_ERROR or no-records is terminal
|
|
|
|
if ( Status == DNS_ERROR_RCODE_NAME_ERROR ||
|
|
Status == DNS_INFO_NO_RECORDS )
|
|
{
|
|
padapter->Status = Status;
|
|
padapter->RunFlags |= RUN_FLAG_STOP_QUERY_ON_ADAPTER;
|
|
pNetInfo->ReturnFlags |= RUN_FLAG_HAVE_VALID_RESPONSE;
|
|
|
|
if ( !g_WaitForNameErrorOnAll )
|
|
{
|
|
fterminalNameError = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// priority reset?
|
|
// - never reset loopback -- this keeps this first
|
|
// - other
|
|
|
|
if ( fpriReset )
|
|
{
|
|
serverPriorityChange(
|
|
pNetInfo,
|
|
padapter,
|
|
pserver,
|
|
newPriority );
|
|
}
|
|
|
|
//
|
|
// end immediately on terminal NAME_ERROR
|
|
// - jumping here rather than above only to simplify
|
|
// handling of pri-rest (special casing of loopback, etc.)
|
|
//
|
|
|
|
if ( fterminalNameError )
|
|
{
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// do running check that still adapter worth querying
|
|
// - not ignoring in first place
|
|
// - hasn't received NAME_ERROR or AUTH_EMPTY response
|
|
//
|
|
// this is "at recv" check -- only trying to determine if we
|
|
// should stop query RIGHT NOW as a result of this receive;
|
|
// this does NOT check on whether there are any other servers
|
|
// worth querying as that is done when go back for next send
|
|
//
|
|
// note how this works -- result starts as NAME_ERROR, when find
|
|
// ANY adapter that hasn't gotten terminal response, then
|
|
// result shifts (and stays) at ERROR_SUCCESS
|
|
//
|
|
// note, if we fix the twice through list issue above, then have to
|
|
// change this so don't skip adapter lists after IP is found
|
|
//
|
|
|
|
if ( !(padapter->RunFlags & RUN_FLAG_COMBINED_IGNORE_ADAPTER) )
|
|
{
|
|
result = ERROR_SUCCESS;
|
|
}
|
|
}
|
|
|
|
Done:
|
|
|
|
#if DBG
|
|
if ( !freset )
|
|
{
|
|
DNSDBG( ANY, (
|
|
"ERROR: DNS server %s not in list.\n",
|
|
DNSADDR_STRING( pAddr ) ));
|
|
DNS_ASSERT( FALSE );
|
|
}
|
|
#endif
|
|
return( result );
|
|
}
|
|
|
|
|
|
|
|
PDNS_ADDR
|
|
bestDnsServerForNextSend(
|
|
IN PDNS_ADAPTER pAdapter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get best DNS server IP address from list.
|
|
|
|
Arguments:
|
|
|
|
pAdapter -- struct with list of DNS servers
|
|
|
|
Return Value:
|
|
|
|
Ptr to server info of best send.
|
|
NULL if no server on adapter is worth sending to; this is
|
|
the case if all servers have received a response.
|
|
|
|
--*/
|
|
{
|
|
PDNS_ADDR pserver;
|
|
PDNS_ADDR_ARRAY pserverArray;
|
|
PDNS_ADDR pbestServer = NULL;
|
|
DWORD j;
|
|
DWORD status;
|
|
LONG priority;
|
|
LONG priorityBest = MINLONG;
|
|
DWORD sent;
|
|
DWORD sentBest = MAXDWORD;
|
|
|
|
|
|
DNSDBG( SEND, (
|
|
"Enter bestDnsServerForNextSend( %p )\n",
|
|
pAdapter ));
|
|
|
|
if ( !pAdapter || !(pserverArray = pAdapter->pDnsAddrs) )
|
|
{
|
|
DNSDBG( SEND, (
|
|
"WARNING: Leaving bestDnsServerForNextSend, no server list\n" ));
|
|
return( NULL );
|
|
}
|
|
|
|
//
|
|
// if already received name error on server in this list, done
|
|
//
|
|
// DCR: single flag test for no further query on this adapter
|
|
//
|
|
|
|
if ( pAdapter->Status == DNS_ERROR_RCODE_NAME_ERROR ||
|
|
pAdapter->Status == DNS_INFO_NO_RECORDS )
|
|
{
|
|
DNSDBG( SEND, (
|
|
"Leaving bestDnsServerForNextSend, NAME_ERROR already received\n"
|
|
"\ton server in server list %p\n",
|
|
pAdapter ));
|
|
return( NULL );
|
|
}
|
|
|
|
//
|
|
// check each server in list
|
|
//
|
|
|
|
for ( j=0; j<pserverArray->AddrCount; j++ )
|
|
{
|
|
pserver = &pserverArray->AddrArray[j];
|
|
|
|
//
|
|
// skip server?
|
|
// - already recieved a response
|
|
// - or failed in send
|
|
// - or already sent in current retry
|
|
//
|
|
|
|
if ( TEST_SERVER_VALID_RECV(pserver) )
|
|
{
|
|
// NAME_ERROR or EMPTY_AUTH then adapter should have been
|
|
// marked as "done" and we shouldn't be here
|
|
// NO_ERROR should have exited immediately
|
|
|
|
DNS_ASSERT( pserver->Status != NO_ERROR &&
|
|
pserver->Status != DNS_ERROR_RCODE_NAME_ERROR &&
|
|
pserver->Status != DNS_INFO_NO_RECORDS );
|
|
continue;
|
|
}
|
|
if ( TEST_SERVER_STATE( pserver, SRVFLAG_SENT_THIS_RETRY ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// check for best priority
|
|
// - ideal is unsent, high priority server
|
|
//
|
|
// DCR: skip NO_DNS server for a while
|
|
// skip timeout server for a little while
|
|
// perhaps this should be done be ignoring these
|
|
// when list is sent down?
|
|
//
|
|
// three cases based on sent\not:
|
|
// - server not sent, best has been sent
|
|
// => become best unless priority at NO_DNS level
|
|
// - sent levels equal
|
|
// => become best based on best priority
|
|
// - server has sent, best has not
|
|
// => become best only if current best at NO_DNS level, and
|
|
// we're higher
|
|
//
|
|
|
|
priority = (LONG) pserver->Priority;
|
|
sent = TEST_SERVER_STATE( pserver, SRVFLAG_SENT );
|
|
|
|
if ( !pbestServer )
|
|
{
|
|
// no-op, we become the best
|
|
}
|
|
else if ( sent < sentBest )
|
|
{
|
|
if ( priority < SRVPRI_NO_DNS )
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
else if ( sent == sentBest )
|
|
{
|
|
if ( priority < priorityBest )
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
else // sent already, best has not
|
|
{
|
|
if ( priority < priorityBest ||
|
|
priorityBest > SRVPRI_NO_DNS )
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// found new best server
|
|
// - save "best" info for comparison
|
|
// - declare immediate victory if unsent and high priority
|
|
|
|
pbestServer = pserver;
|
|
priorityBest = priority;
|
|
sentBest = sent;
|
|
|
|
if ( priority >= 0 && !sent )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return( pbestServer );
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
markDuplicateSends(
|
|
IN OUT PDNS_NETINFO pNetInfo,
|
|
IN PDNS_ADDR pSendServer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Mark any matching servers to avoid duplicate sends.
|
|
|
|
Arguments:
|
|
|
|
pNetInfo -- network info blob for send
|
|
|
|
pSendServer -- DNS server being sent to
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
DWORD i;
|
|
|
|
DNSDBG( SEND, (
|
|
"Enter markDuplicateSends( %p, %p )\n",
|
|
pNetInfo,
|
|
pSendServer ));
|
|
|
|
if ( !pNetInfo )
|
|
{
|
|
DNS_ASSERT( FALSE );
|
|
return;
|
|
}
|
|
|
|
//
|
|
// find matching DNS servers
|
|
// - note check MUST include scope as different scope
|
|
// is different DNS
|
|
//
|
|
// mark matches as sent, sent this pass
|
|
// note: currently there is no reason that flags should not
|
|
// exactly match, so just copy flags
|
|
//
|
|
|
|
for ( i=0; i<pNetInfo->AdapterCount; i++ )
|
|
{
|
|
PDNS_ADAPTER padapter;
|
|
PDNS_ADDR_ARRAY pserverArray;
|
|
DWORD j;
|
|
|
|
padapter = NetInfo_GetAdapterByIndex( pNetInfo, i );
|
|
|
|
pserverArray = padapter->pDnsAddrs;
|
|
if ( !pserverArray )
|
|
{
|
|
continue;
|
|
}
|
|
for ( j=0; j<pserverArray->AddrCount; j++ )
|
|
{
|
|
PDNS_ADDR pserver = &pserverArray->AddrArray[j];
|
|
|
|
if ( DnsAddr_IsEqual(
|
|
pserver,
|
|
pSendServer,
|
|
DNSADDR_MATCH_ADDR ) )
|
|
{
|
|
if ( pserver != pSendServer )
|
|
{
|
|
DNSDBG( SEND, (
|
|
"Marking duplicate server %p on adapter %d\n",
|
|
pserver,
|
|
padapter->InterfaceIndex ));
|
|
|
|
pserver->Flags = pSendServer->Flags;
|
|
DNS_ASSERT( pserver->Status == pSendServer->Status );
|
|
}
|
|
padapter->RunFlags |= (RUN_FLAG_SENT | RUN_FLAG_SENT_THIS_RETRY);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
DNS_STATUS
|
|
sendUsingServerInfo(
|
|
IN OUT PDNS_MSG_BUF pMsg,
|
|
IN OUT PDNS_NETINFO pNetInfo,
|
|
IN OUT PDNS_ADDR pServInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Send DNS message using server info.
|
|
|
|
This function encapsulates the process of checking
|
|
server info for validity, sending (as appropriate)
|
|
and marking server, netinfo info.
|
|
|
|
Note: right now this is UDP only
|
|
|
|
Arguments:
|
|
|
|
pMsg - message info for message to send
|
|
|
|
pNetInfo - netinfo blob for send
|
|
|
|
pServInfo - info of server to send to
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
ErrorCode on send failure.
|
|
|
|
--*/
|
|
{
|
|
DNS_STATUS status;
|
|
BOOL fnoOpt;
|
|
|
|
DNSDBG( SEND, (
|
|
"sendUsingServerInfo( msg=%p, ni=%p, servinfo=%p )\n",
|
|
pMsg,
|
|
pNetInfo,
|
|
pServInfo ));
|
|
|
|
//
|
|
// check that haven't
|
|
// - already completed send\recv
|
|
// - sent this pass
|
|
//
|
|
|
|
if ( TEST_SERVER_VALID_RECV( pServInfo ) ||
|
|
TEST_SERVER_STATE( pServInfo, SRVFLAG_SENT_THIS_RETRY ) )
|
|
{
|
|
DNSDBG( SEND, (
|
|
"Skipping send on server %p -- %s.\n",
|
|
pServInfo,
|
|
( TEST_SERVER_VALID_RECV( pServInfo ) )
|
|
? "has valid recv"
|
|
: "already sent this retry" ));
|
|
return DNS_ERROR_NO_SEND;
|
|
}
|
|
|
|
//
|
|
// check OPT status
|
|
// - previous OPT send that timed OUT, then send non-OPT
|
|
//
|
|
// DCR: known OPT-ok list could screen wasted send
|
|
|
|
fnoOpt = TEST_SERVER_STATE( pServInfo, SRVFLAG_SENT_OPT );
|
|
|
|
//
|
|
// send
|
|
//
|
|
|
|
status = Send_MessagePrivate(
|
|
pMsg,
|
|
pServInfo,
|
|
fnoOpt );
|
|
|
|
if ( status == ERROR_SUCCESS )
|
|
{
|
|
DNS_ASSERT( !fnoOpt || !pMsg->fLastSendOpt );
|
|
|
|
SET_SERVER_STATE(
|
|
pServInfo,
|
|
pMsg->fLastSendOpt
|
|
? (SRVFLAG_SENT_OPT | SRVFLAG_SENT_THIS_RETRY)
|
|
: (SRVFLAG_SENT_NON_OPT | SRVFLAG_SENT_THIS_RETRY)
|
|
);
|
|
|
|
// screen out duplicate sends
|
|
|
|
markDuplicateSends( pNetInfo, pServInfo );
|
|
}
|
|
else
|
|
{
|
|
pServInfo->Status = status;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
DNS_STATUS
|
|
SendUdpToNextDnsServers(
|
|
IN OUT PDNS_MSG_BUF pMsgSend,
|
|
IN OUT PDNS_NETINFO pNetInfo,
|
|
IN DWORD cRetryCount,
|
|
IN DWORD dwTimeout,
|
|
OUT PDWORD pSendCount
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends to next DNS servers in list.
|
|
|
|
Arguments:
|
|
|
|
pMsgSend -- message to send
|
|
|
|
pNetInfo -- per adapter DNS info
|
|
|
|
cRetryCount -- retry for this send
|
|
|
|
dwTimeout -- timeout on last send, if timed out
|
|
|
|
pSendCount -- addr to receive send count
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful send.
|
|
ERROR_TIMEOUT if no DNS servers left to send to.
|
|
Winsock error code on send failure.
|
|
|
|
--*/
|
|
{
|
|
DWORD i;
|
|
DWORD j;
|
|
DWORD sendCount = 0;
|
|
BOOL fignoredAdapter = FALSE;
|
|
PDNS_ADAPTER padapter;
|
|
PDNS_ADDR_ARRAY pserverArray;
|
|
PDNS_ADDR pserver;
|
|
DNS_STATUS status = ERROR_TIMEOUT;
|
|
|
|
DNSDBG( SEND, (
|
|
"Enter SendUdpToNextDnsServers()\n"
|
|
"\tretry = %d\n",
|
|
cRetryCount ));
|
|
|
|
|
|
//
|
|
// if netinfo not initialized for send, init
|
|
//
|
|
|
|
if ( !(pNetInfo->ReturnFlags & RUN_FLAG_NETINFO_PREPARED) )
|
|
{
|
|
DNSDBG( SEND, ( "Netinfo not prepared for send -- preparing now.\n" ));
|
|
|
|
NetInfo_Clean(
|
|
pNetInfo,
|
|
CLEAR_LEVEL_QUERY );
|
|
}
|
|
|
|
#if DBG
|
|
//
|
|
// verify i'm getting a clean list on start
|
|
//
|
|
|
|
if ( cRetryCount == 0 )
|
|
{
|
|
for ( i=0; i<pNetInfo->AdapterCount; i++ )
|
|
{
|
|
padapter = NetInfo_GetAdapterByIndex( pNetInfo, i );
|
|
|
|
// ignore this adapter because there are no DNS
|
|
// servers configured?
|
|
|
|
if ( padapter->InfoFlags & AINFO_FLAG_IGNORE_ADAPTER )
|
|
{
|
|
continue;
|
|
}
|
|
DNS_ASSERT( !(padapter->RunFlags &
|
|
( RUN_FLAG_SENT_THIS_RETRY |
|
|
RUN_FLAG_SENT |
|
|
RUN_FLAG_HAVE_VALID_RESPONSE ) ) );
|
|
DNS_ASSERT( padapter->Status == 0 );
|
|
|
|
pserverArray = padapter->pDnsAddrs;
|
|
if ( !pserverArray )
|
|
{
|
|
continue;
|
|
}
|
|
for ( j=0; j<pserverArray->AddrCount; j++ )
|
|
{
|
|
DNS_ASSERT( pserverArray->AddrArray[j].Status == SRVSTATUS_NEW );
|
|
DNS_ASSERT( pserverArray->AddrArray[j].Flags == SRVFLAG_NEW );
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// if previous send timed out, update adapter list
|
|
// - but ONLY do this when sending to individual servers in list
|
|
// - timeout on all servers just produces an unnecessary copy and
|
|
// can only change ordering relative to servers which have already
|
|
// responded with RCODE; since its a timeout, this isn't going to
|
|
// lower these server's priority so no point
|
|
//
|
|
|
|
if ( dwTimeout && cRetryCount && cRetryCount < MAX_SINGLE_SERVER_RETRY )
|
|
{
|
|
timeoutDnsServers( pNetInfo, dwTimeout );
|
|
}
|
|
|
|
//
|
|
// clean "sent this retry" bit on all servers
|
|
//
|
|
// this allows us to track when server has already been
|
|
// sent to on this pass and avoid duplicate send
|
|
//
|
|
|
|
for ( i=0; i<pNetInfo->AdapterCount; i++ )
|
|
{
|
|
padapter = NetInfo_GetAdapterByIndex( pNetInfo, i );
|
|
|
|
padapter->RunFlags &= CLEAR_LEVEL_RETRY;
|
|
|
|
pserverArray = padapter->pDnsAddrs;
|
|
if ( !pserverArray )
|
|
{
|
|
continue;
|
|
}
|
|
for ( j=0; j<pserverArray->AddrCount; j++ )
|
|
{
|
|
CLEAR_SERVER_RETRY_STATE( & pserverArray->AddrArray[j] );
|
|
}
|
|
}
|
|
|
|
//
|
|
// send on DNS server(s) for adapter(s)
|
|
//
|
|
|
|
for ( i=0; i<pNetInfo->AdapterCount; i++ )
|
|
{
|
|
padapter = NetInfo_GetAdapterByIndex( pNetInfo, i );
|
|
|
|
//
|
|
// skip this adapter
|
|
// - no servers
|
|
// - not querying this adapter name
|
|
// - already responded to this name
|
|
// or
|
|
// - unreachable DNS servers and either of
|
|
// - have at least some response (NAME_ERROR) from another
|
|
// adapter's DNS server
|
|
// - first try on this query; in this case, note skipping the
|
|
// adapter so we can use it, if no valid DNS are found
|
|
//
|
|
// note, the goal here is to keep adapters with "unreachable" DNS servers
|
|
// around to resolve, but NOT wait for timeouts on them if other adapter's
|
|
// servers have already NAME_ERROR'd
|
|
//
|
|
// DCR: go back to simple IGNORE check on unreachable adapter IF
|
|
// - could verify unreachability of DNS server
|
|
// but want to do this at run time, when other DNS servers are
|
|
// not coming through, rather than when we build netinfo list itself
|
|
//
|
|
|
|
pserverArray = padapter->pDnsAddrs;
|
|
if ( !pserverArray )
|
|
{
|
|
continue;
|
|
}
|
|
if ( padapter->RunFlags & RUN_FLAG_STOP_QUERY_ON_ADAPTER )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( padapter->InfoFlags &
|
|
(AINFO_FLAG_IGNORE_ADAPTER |
|
|
AINFO_FLAG_SERVERS_IP6_DEFAULT |
|
|
AINFO_FLAG_SERVERS_AUTO_LOOPBACK ) )
|
|
{
|
|
if ( pNetInfo->ReturnFlags & RUN_FLAG_HAVE_VALID_RESPONSE )
|
|
{
|
|
continue;
|
|
}
|
|
if ( cRetryCount == 0 )
|
|
{
|
|
fignoredAdapter = TRUE;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
// first three attempts, we only go to one DNS on a given adapter
|
|
//
|
|
// - first time through ONLY to first server in first adapter list
|
|
// - on subsequent tries go to best server in all lists
|
|
//
|
|
|
|
if ( cRetryCount < MAX_SINGLE_SERVER_RETRY )
|
|
{
|
|
//
|
|
// do this in loop, in case send to "best" server fails
|
|
//
|
|
|
|
while ( 1 )
|
|
{
|
|
pserver = bestDnsServerForNextSend( padapter );
|
|
if ( !pserver )
|
|
{
|
|
break; // no more unsent servers
|
|
}
|
|
status = sendUsingServerInfo(
|
|
pMsgSend,
|
|
pNetInfo,
|
|
pserver );
|
|
|
|
if ( status == ERROR_SUCCESS )
|
|
{
|
|
sendCount++;
|
|
if ( cRetryCount == 0 )
|
|
{
|
|
goto Done;
|
|
}
|
|
break; // continue with next adapter
|
|
}
|
|
// send failed on server, try another
|
|
}
|
|
}
|
|
|
|
//
|
|
// after first three tries, send to all servers that
|
|
// have not already responded (have RCODE, as if NO_ERROR) we
|
|
// already finished
|
|
//
|
|
|
|
else
|
|
{
|
|
for ( j=0; j<pserverArray->AddrCount; j++ )
|
|
{
|
|
status = sendUsingServerInfo(
|
|
pMsgSend,
|
|
pNetInfo,
|
|
&pserverArray->AddrArray[j] );
|
|
|
|
if ( status == ERROR_SUCCESS )
|
|
{
|
|
sendCount++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// no reachable DNS servers -- but possibly reachable ones?
|
|
//
|
|
// if first pass and we found NO reachable DNS servers, BUT
|
|
// we skipped over some unreachable ones -- use them
|
|
//
|
|
// note that fignoreAdapter is sufficient test, because it can
|
|
// only be set on cRetryCount == 0, which will jump directly to
|
|
// Done: label on a successful send
|
|
//
|
|
// note that we are doing this by tail recursion (just to keep it
|
|
// simple) and the recursion termination is guaranteed by bumping
|
|
// the retry count
|
|
//
|
|
|
|
if ( fignoredAdapter )
|
|
{
|
|
return SendUdpToNextDnsServers(
|
|
pMsgSend,
|
|
pNetInfo,
|
|
1, // bump retry count to 1
|
|
dwTimeout,
|
|
pSendCount );
|
|
}
|
|
|
|
Done:
|
|
|
|
//
|
|
// if sent packet, success
|
|
//
|
|
|
|
*pSendCount = sendCount;
|
|
|
|
DNSDBG( SEND, (
|
|
"Leave SendUdpToNextDnsServers()\n"
|
|
"\tsends = %d\n",
|
|
sendCount ));
|
|
|
|
if ( sendCount )
|
|
{
|
|
return( ERROR_SUCCESS );
|
|
}
|
|
|
|
// if no packets sent, alert caller we're done
|
|
// - this is possible if servers have responded uselessly
|
|
// (NAME_ERROR, SERVER_FAILURE)
|
|
|
|
if ( status == ERROR_SUCCESS )
|
|
{
|
|
status = ERROR_TIMEOUT;
|
|
}
|
|
return( status );
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// Message addressing routines
|
|
//
|
|
|
|
VOID
|
|
Send_SetMsgRemoteSockaddr(
|
|
IN OUT PDNS_MSG_BUF pMsg,
|
|
IN PDNS_ADDR pAddr
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize remote sockaddr.
|
|
|
|
Arguments:
|
|
|
|
pMsg - message to send
|
|
|
|
pAddr - IP address to send
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// fill in sockaddr for IP4 or IP6
|
|
//
|
|
|
|
DnsAddr_Copy(
|
|
& pMsg->RemoteAddress,
|
|
pAddr );
|
|
|
|
pMsg->RemoteAddress.SockaddrIn.sin_port = DNS_PORT_NET_ORDER;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
Send_SetMessageForRecv(
|
|
IN OUT PDNS_MSG_BUF pMsgRecv,
|
|
IN PDNS_MSG_BUF pMsgSend
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set message for recv.
|
|
|
|
Arguments:
|
|
|
|
pMsgRecv - ptr message to recv
|
|
|
|
pMsgSend - ptr to message sent
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
DNSDBG( SOCKET, (
|
|
"Send_SetMessageForRecv( %p, %p )\n", pMsgRecv, pMsgSend ));
|
|
|
|
//
|
|
// TCP -- single connection at a time
|
|
// -- sockaddr not filled in during recv(), so fill it in now
|
|
//
|
|
|
|
if ( pMsgSend->fTcp )
|
|
{
|
|
pMsgRecv->fTcp;
|
|
pMsgRecv->Socket = pMsgSend->Socket;
|
|
|
|
RtlCopyMemory(
|
|
& pMsgRecv->RemoteAddress,
|
|
& pMsgSend->RemoteAddress,
|
|
sizeof(DNS_ADDR) );
|
|
}
|
|
|
|
//
|
|
// UDP -- can recv on either 4 or 6 socket
|
|
// -- sockaddr buffer must accomodate either
|
|
//
|
|
|
|
else
|
|
{
|
|
pMsgRecv->Socket = 0;
|
|
pMsgRecv->Socket4 = pMsgSend->Socket4;
|
|
pMsgRecv->Socket6 = pMsgSend->Socket6;
|
|
|
|
pMsgRecv->RemoteAddress.SockaddrLength = DNS_ADDR_MAX_SOCKADDR_LENGTH;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
PDNS_ADDR
|
|
Send_CopyRecvIp(
|
|
OUT PDNS_ADDR pAddr,
|
|
IN PDNS_MSG_BUF pMsg
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Copy address from recv.
|
|
|
|
Arguments:
|
|
|
|
pAddr - addr of IP address to recv copy
|
|
|
|
pMsg - message to send
|
|
|
|
Return Value:
|
|
|
|
Ptr to addr written -- if successful.
|
|
NULL on bad recv addr.
|
|
|
|
--*/
|
|
{
|
|
INT family;
|
|
|
|
DnsAddr_Copy(
|
|
pAddr,
|
|
& pMsg->RemoteAddress );
|
|
|
|
if ( DnsAddr_Family(pAddr) )
|
|
{
|
|
return pAddr;
|
|
}
|
|
else
|
|
{
|
|
DnsAddr_Clear( pAddr );
|
|
|
|
DNSDBG( ANY, (
|
|
"ERROR: invalid recv sockaddr (family %d) for message %p!\n",
|
|
pMsg->RemoteAddress.Sockaddr.sa_family,
|
|
pMsg ));
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Send routines
|
|
//
|
|
|
|
DNS_STATUS
|
|
Send_MessagePrivate(
|
|
IN OUT PDNS_MSG_BUF pMsg,
|
|
IN PDNS_ADDR pSendAddr,
|
|
IN BOOL fNoOpt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Send a DNS packet.
|
|
|
|
This is the generic send routine used for ANY send of a DNS message.
|
|
|
|
It assumes nothing about the message type, but does assume:
|
|
- pCurrent points at byte following end of desired data
|
|
- RR count bytes are in HOST byte order
|
|
|
|
Arguments:
|
|
|
|
pMsg - message info for message to send
|
|
|
|
pSendAddr - ptr to IP address to send to
|
|
OPTIONAL, required only if UDP and message sockaddr not set
|
|
|
|
fIp4 -- TRUE if IP4, FALSE for IP6
|
|
|
|
fNoOpt - TRUE if OPT send is forbidden
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful.
|
|
FALSE on send error.
|
|
|
|
--*/
|
|
{
|
|
PDNS_HEADER pmsgHead;
|
|
INT err;
|
|
WORD sendLength;
|
|
BOOL fexcludedOpt = FALSE;
|
|
|
|
DNSDBG( SEND, (
|
|
"Send_MessagePrivate()\n"
|
|
"\tpMsg = %p\n"
|
|
"\tpSendAddr = %p\n"
|
|
"\tNo OPT = %d\n",
|
|
pMsg,
|
|
pSendAddr,
|
|
fNoOpt ));
|
|
|
|
//
|
|
// set header flags
|
|
//
|
|
// note: since route sends both queries and responses
|
|
// caller must set these flags
|
|
//
|
|
|
|
pmsgHead = &pMsg->MessageHead;
|
|
pmsgHead->Reserved = 0;
|
|
|
|
//
|
|
// set send IP (if given)
|
|
//
|
|
|
|
if ( pSendAddr )
|
|
{
|
|
Send_SetMsgRemoteSockaddr(
|
|
pMsg,
|
|
pSendAddr );
|
|
}
|
|
|
|
//
|
|
// set message length and OPT inclusion
|
|
//
|
|
// OPT approach is
|
|
// - write to pCurrent packet end
|
|
// - handles NO OPT written and using OPT
|
|
// - unless HAVE written OPT, and specifically excluding
|
|
// note, that zero IP (TCP previously connected) gets
|
|
// excluded
|
|
//
|
|
// DCR: we haven't handled OPT for TCP connected and not-aware of IP
|
|
// case here
|
|
//
|
|
// DCR: for now excluding OPT on updates, because harder to detect on
|
|
// the recv end why the reason for the failure
|
|
//
|
|
|
|
{
|
|
PCHAR pend = pMsg->pCurrent;
|
|
|
|
if ( pMsg->pPreOptEnd
|
|
&&
|
|
( fNoOpt
|
|
||
|
|
g_UseEdns == 0
|
|
||
|
|
pMsg->MessageHead.Opcode == DNS_OPCODE_UPDATE
|
|
||
|
|
//Send_IsServerOptExclude( &pMsg->RemoteIp ) ) )
|
|
( pSendAddr && Send_IsServerOptExclude( pSendAddr ) ) ) )
|
|
|
|
{
|
|
ASSERT( pMsg->pPreOptEnd > (PCHAR)pmsgHead );
|
|
ASSERT( pMsg->pPreOptEnd < pend );
|
|
|
|
pend = pMsg->pPreOptEnd;
|
|
pmsgHead->AdditionalCount--;
|
|
fexcludedOpt = TRUE;
|
|
}
|
|
|
|
sendLength = (WORD)(pend - (PCHAR)pmsgHead);
|
|
|
|
pMsg->fLastSendOpt = (pMsg->pPreOptEnd && (pend != pMsg->pPreOptEnd));
|
|
}
|
|
|
|
IF_DNSDBG( SEND )
|
|
{
|
|
pMsg->MessageLength = sendLength;
|
|
DnsDbg_Message(
|
|
"Sending packet",
|
|
pMsg );
|
|
}
|
|
|
|
//
|
|
// flip header count bytes
|
|
//
|
|
|
|
DNS_BYTE_FLIP_HEADER_COUNTS( pmsgHead );
|
|
|
|
//
|
|
// TCP -- send until all info transmitted
|
|
//
|
|
|
|
if ( pMsg->fTcp )
|
|
{
|
|
PCHAR psend;
|
|
|
|
//
|
|
// TCP message always begins with bytes being sent
|
|
//
|
|
// - send length = message length plus two byte size
|
|
// - flip bytes in message length
|
|
// - send starting at message length
|
|
//
|
|
|
|
pMsg->MessageLength = htons( sendLength );
|
|
|
|
sendLength += sizeof(WORD);
|
|
|
|
psend = (PCHAR) &pMsg->MessageLength;
|
|
|
|
while ( sendLength )
|
|
{
|
|
err = send(
|
|
pMsg->Socket,
|
|
psend,
|
|
(INT) sendLength,
|
|
0 );
|
|
|
|
if ( err == 0 || err == SOCKET_ERROR )
|
|
{
|
|
err = GetLastError();
|
|
|
|
//
|
|
// WSAESHUTDOWN is ok, client got timed out connection and
|
|
// closed
|
|
//
|
|
// WSAENOTSOCK may also occur if FIN recv'd and connection
|
|
// closed by TCP receive thread before the send
|
|
//
|
|
|
|
if ( err == WSAESHUTDOWN )
|
|
{
|
|
IF_DNSDBG( ANY )
|
|
{
|
|
DNS_PRINT((
|
|
"WARNING: send() failed on shutdown socket %d.\n"
|
|
"\tpMsgInfo at %p\n",
|
|
pMsg->Socket,
|
|
pMsg ));
|
|
}
|
|
}
|
|
else if ( err == WSAENOTSOCK )
|
|
{
|
|
IF_DNSDBG( ANY )
|
|
{
|
|
DNS_PRINT((
|
|
"ERROR: send() on closed socket %d.\n"
|
|
"\tpMsgInfo at %p\n",
|
|
pMsg->Socket,
|
|
pMsg ));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DNS_LOG_EVENT(
|
|
DNS_EVENT_SEND_CALL_FAILED,
|
|
0,
|
|
NULL,
|
|
err );
|
|
|
|
IF_DNSDBG( ANY )
|
|
{
|
|
DNS_PRINT(( "ERROR: TCP send() failed, err = %d.\n" ));
|
|
}
|
|
}
|
|
goto Done;
|
|
}
|
|
sendLength -= (WORD)err;
|
|
psend += err;
|
|
}
|
|
}
|
|
|
|
//
|
|
// UDP
|
|
//
|
|
|
|
else
|
|
{
|
|
//
|
|
// get socket for send
|
|
//
|
|
// note UDP sockets may or may not be open before send as
|
|
// don't know whether we need to do send on specific protocols
|
|
// until sending to address of given protocol
|
|
//
|
|
|
|
if ( !Socket_CreateMessageSocket(pMsg) )
|
|
{
|
|
err = GetLastError();
|
|
if ( err == NO_ERROR )
|
|
{
|
|
DNS_ASSERT( err != NO_ERROR );
|
|
err = WSAEPROTONOSUPPORT;
|
|
}
|
|
goto Done;
|
|
}
|
|
DNS_ASSERT( pMsg->Socket != 0 );
|
|
DNS_ASSERT( sendLength <= DNS_RFC_MAX_UDP_PACKET_LENGTH );
|
|
|
|
err = sendto(
|
|
pMsg->Socket,
|
|
(PCHAR) pmsgHead,
|
|
sendLength,
|
|
0,
|
|
(PSOCKADDR) &pMsg->RemoteAddress.Sockaddr,
|
|
pMsg->RemoteAddress.SockaddrLength
|
|
);
|
|
|
|
if ( err == SOCKET_ERROR )
|
|
{
|
|
err = GetLastError();
|
|
|
|
DNS_LOG_EVENT(
|
|
DNS_EVENT_SENDTO_CALL_FAILED,
|
|
0,
|
|
NULL,
|
|
err );
|
|
|
|
IF_DNSDBG( ANY )
|
|
{
|
|
DNS_PRINT((
|
|
"ERROR: UDP sendto() failed => %d.\n",
|
|
err ));
|
|
|
|
DnsDbg_DnsAddr(
|
|
"sendto() failed sockaddr",
|
|
&pMsg->RemoteAddress );
|
|
|
|
DnsDbg_Message(
|
|
"sendto() failed message",
|
|
pMsg );
|
|
}
|
|
goto Done;
|
|
}
|
|
}
|
|
|
|
err = ERROR_SUCCESS;
|
|
|
|
Done:
|
|
|
|
DNS_BYTE_FLIP_HEADER_COUNTS( pmsgHead );
|
|
|
|
// restore OPT in count if required
|
|
|
|
if ( fexcludedOpt )
|
|
{
|
|
pmsgHead->AdditionalCount++;
|
|
}
|
|
|
|
Trace_LogSendEvent( pMsg, err );
|
|
|
|
return( (DNS_STATUS)err );
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// UDP
|
|
//
|
|
|
|
DNS_STATUS
|
|
Recv_Udp(
|
|
IN OUT PDNS_MSG_BUF pMsg
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Receives DNS message
|
|
|
|
Arguments:
|
|
|
|
pMsg - message buffer for recv
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
Error status on failure.
|
|
|
|
--*/
|
|
{
|
|
PDNS_HEADER pdnsHeader;
|
|
LONG err = ERROR_SUCCESS;
|
|
struct timeval selectTimeout;
|
|
struct fd_set fdset;
|
|
|
|
DNSDBG( RECV, (
|
|
"Enter Recv_Udp( %p )\n",
|
|
pMsg ));
|
|
|
|
DNS_ASSERT( !pMsg->fTcp );
|
|
|
|
//
|
|
// setup recv set
|
|
//
|
|
|
|
FD_ZERO( &fdset );
|
|
|
|
if ( pMsg->Socket6 )
|
|
{
|
|
FD_SET( pMsg->Socket6, &fdset );
|
|
}
|
|
if ( pMsg->Socket4 )
|
|
{
|
|
FD_SET( pMsg->Socket4, &fdset );
|
|
}
|
|
if ( fdset.fd_count == 0 )
|
|
{
|
|
DNSDBG( ANY, (
|
|
"ERROR: UDP recv on msg %p with no sockets!\n",
|
|
pMsg ));
|
|
return( ERROR_INVALID_PARAMETER );
|
|
}
|
|
|
|
// set timeout
|
|
|
|
if ( pMsg->Timeout > HARD_TIMEOUT_LIMIT &&
|
|
pMsg->Timeout != ONE_HOUR_TIMEOUT )
|
|
{
|
|
DNSDBG( RECV, (
|
|
"ERROR: timeout %d, exceeded hard limit.\n",
|
|
pMsg->Timeout ));
|
|
|
|
return( ERROR_TIMEOUT );
|
|
}
|
|
selectTimeout.tv_usec = 0;
|
|
selectTimeout.tv_sec = pMsg->Timeout;
|
|
|
|
pdnsHeader = &pMsg->MessageHead;
|
|
|
|
|
|
//
|
|
// wait for stack to indicate packet reception
|
|
//
|
|
|
|
err = select( 0, &fdset, NULL, NULL, &selectTimeout );
|
|
|
|
if ( err <= 0 )
|
|
{
|
|
if ( err < 0 )
|
|
{
|
|
// select error
|
|
err = WSAGetLastError();
|
|
DNS_PRINT(( "ERROR: select() error = %d\n", err ));
|
|
return( err );
|
|
}
|
|
else
|
|
{
|
|
DNS_PRINT(( "ERROR: timeout on response %p\n", pMsg ));
|
|
return( ERROR_TIMEOUT );
|
|
}
|
|
}
|
|
|
|
//
|
|
// receive
|
|
//
|
|
|
|
if ( fdset.fd_count != 1 )
|
|
{
|
|
if ( fdset.fd_count == 0 )
|
|
{
|
|
DNS_ASSERT( FALSE );
|
|
return( ERROR_TIMEOUT );
|
|
}
|
|
DNS_PRINT((
|
|
"WARNING: Recv_Udp on multiple sockets!\n" ));
|
|
}
|
|
|
|
pMsg->Socket = fdset.fd_array[0];
|
|
|
|
err = recvfrom(
|
|
pMsg->Socket,
|
|
(PCHAR) pdnsHeader,
|
|
DNS_MAX_UDP_PACKET_BUFFER_LENGTH,
|
|
0,
|
|
&pMsg->RemoteAddress.Sockaddr,
|
|
&pMsg->RemoteAddress.SockaddrLength );
|
|
|
|
if ( err == SOCKET_ERROR )
|
|
{
|
|
err = GetLastError();
|
|
|
|
Trace_LogRecvEvent(
|
|
pMsg,
|
|
err,
|
|
FALSE // UDP
|
|
);
|
|
|
|
if ( err == WSAECONNRESET )
|
|
{
|
|
DNSDBG( RECV, (
|
|
"Leave Recv_Udp( %p ) with CONNRESET\n",
|
|
pMsg ));
|
|
return( err );
|
|
}
|
|
|
|
// message sent was too big
|
|
// sender was in error -- should have sent TCP
|
|
|
|
if ( err == WSAEMSGSIZE )
|
|
{
|
|
pMsg->MessageLength = DNS_MAX_UDP_PACKET_BUFFER_LENGTH;
|
|
|
|
DnsDbg_Message(
|
|
"ERROR: Recv UDP packet over 512 bytes.\n",
|
|
pMsg );
|
|
}
|
|
IF_DNSDBG( ANY )
|
|
{
|
|
DnsDbg_Lock();
|
|
DNS_PRINT((
|
|
"ERROR: recvfrom(sock=%d) of UDP request failed.\n"
|
|
"\tGetLastError() = 0x%08lx.\n",
|
|
socket,
|
|
err ));
|
|
DnsDbg_DnsAddr(
|
|
"recvfrom failed sockaddr\n",
|
|
& pMsg->RemoteAddress );
|
|
DnsDbg_Unlock();
|
|
}
|
|
return( err );
|
|
}
|
|
|
|
//
|
|
// successful receive
|
|
// - save remote IP
|
|
// - set message length
|
|
// - flip header fields
|
|
//
|
|
|
|
DNS_ASSERT( err <= DNS_MAX_UDP_PACKET_BUFFER_LENGTH );
|
|
pMsg->MessageLength = (WORD)err;
|
|
DNS_BYTE_FLIP_HEADER_COUNTS( &pMsg->MessageHead );
|
|
|
|
err = ERROR_SUCCESS;
|
|
|
|
Trace_LogRecvEvent(
|
|
pMsg,
|
|
0,
|
|
FALSE // UDP
|
|
);
|
|
|
|
IF_DNSDBG( RECV )
|
|
{
|
|
DnsDbg_Message(
|
|
"Received message",
|
|
pMsg );
|
|
}
|
|
return( err );
|
|
}
|
|
|
|
|
|
|
|
DNS_STATUS
|
|
Send_AndRecvUdpWithParam(
|
|
IN OUT PDNS_MSG_BUF pMsgSend,
|
|
OUT PDNS_MSG_BUF pMsgRecv,
|
|
IN DWORD dwFlags,
|
|
IN PADDR_ARRAY pServerList,
|
|
IN OUT PDNS_NETINFO pNetInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends to and waits to recv from remote DNS.
|
|
|
|
DCR: move to use send blob
|
|
|
|
Arguments:
|
|
|
|
pMsgSend - message to send
|
|
|
|
ppMsgRecv - and reuse
|
|
|
|
dwFlags -- query flags
|
|
|
|
pServerList -- list of server to use; overrides adapter info
|
|
|
|
pNetInfo -- adapter list DNS server info
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful response.
|
|
Error status for "best RCODE" response if rcode.
|
|
ERROR_TIMEOUT on timeout.
|
|
Error status on send\recv failure.
|
|
|
|
--*/
|
|
{
|
|
INT retry;
|
|
DWORD timeout;
|
|
DNS_STATUS status = ERROR_TIMEOUT;
|
|
DNS_ADDR recvIp;
|
|
PDNS_ADDR precvIp = NULL;
|
|
DWORD rcode = 0;
|
|
DWORD ignoredRcode = 0;
|
|
DWORD sendCount;
|
|
DWORD sentCount;
|
|
DWORD sendTime;
|
|
BOOL frecvRetry;
|
|
BOOL fupdate = FALSE; // prefix
|
|
PDNS_NETINFO ptempNetInfo = NULL;
|
|
|
|
|
|
DNSDBG( SEND, (
|
|
"Enter Send_AndRecvUdpWithParam()\n"
|
|
"\ttime %d\n"
|
|
"\tsend msg at %p\n"
|
|
"\tsocket %d\n"
|
|
"\trecv msg at %p\n"
|
|
"\tflags %08x\n"
|
|
"\tserver IP array %p\n"
|
|
"\tadapter info at %p\n",
|
|
Dns_GetCurrentTimeInSeconds(),
|
|
pMsgSend,
|
|
pMsgSend->Socket,
|
|
pMsgRecv,
|
|
dwFlags,
|
|
pServerList,
|
|
pNetInfo ));
|
|
|
|
// verify params
|
|
|
|
if ( !pMsgSend || !pMsgRecv || (!pNetInfo && !pServerList) )
|
|
{
|
|
return( ERROR_INVALID_PARAMETER );
|
|
}
|
|
|
|
//
|
|
// server IP array?
|
|
// - if given overrides netinfo
|
|
//
|
|
|
|
// should be able to just use update netinfo
|
|
//if ( aipServers && !pNetInfo )
|
|
if ( pServerList )
|
|
{
|
|
ptempNetInfo = NetInfo_CreateFromAddrArray(
|
|
pServerList,
|
|
0, // no single IP
|
|
FALSE, // no search info
|
|
NULL );
|
|
if ( !ptempNetInfo )
|
|
{
|
|
return( DNS_ERROR_NO_MEMORY );
|
|
}
|
|
pNetInfo = ptempNetInfo;
|
|
}
|
|
|
|
//
|
|
// DCR: allow sockets sent into send functions
|
|
// - then must know initial state to avoid close
|
|
//
|
|
|
|
//
|
|
// if already have TCP socket -- invalid
|
|
//
|
|
|
|
if ( pMsgSend->fTcp && pMsgSend->Socket )
|
|
{
|
|
status = ERROR_INVALID_PARAMETER;
|
|
goto Done;
|
|
}
|
|
|
|
pMsgSend->fTcp = FALSE;
|
|
pMsgRecv->Socket = 0;
|
|
pMsgRecv->fTcp = FALSE;
|
|
|
|
// determine UPDATE or standard QUERY
|
|
|
|
fupdate = ( pMsgSend->MessageHead.Opcode == DNS_OPCODE_UPDATE );
|
|
|
|
//
|
|
// loop sending until
|
|
// - receive successful response
|
|
// or
|
|
// - receive errors response from all servers
|
|
// or
|
|
// - reach final timeout on all servers
|
|
//
|
|
//
|
|
// DCR: should support setting of timeouts on individual queries
|
|
//
|
|
|
|
retry = 0;
|
|
sendCount = 0;
|
|
|
|
while ( 1 )
|
|
{
|
|
if ( fupdate )
|
|
{
|
|
timeout = UpdateTimeouts[retry];
|
|
}
|
|
else
|
|
{
|
|
if ( dwFlags & DNS_QUERY_USE_QUICK_TIMEOUTS )
|
|
{
|
|
timeout = g_QuickQueryTimeouts[retry];
|
|
}
|
|
else
|
|
{
|
|
timeout = g_QueryTimeouts[retry];
|
|
}
|
|
}
|
|
|
|
//
|
|
// zero timeout indicates end of retries for this query type
|
|
//
|
|
|
|
if ( timeout == 0 )
|
|
{
|
|
// save timeout for adapter?
|
|
//
|
|
// if multiple adapters and some timed out and some
|
|
// didn't then saving timeout is relevant
|
|
//
|
|
// DCR: this doesn't make much sense
|
|
// the actual test i moved inside the function
|
|
|
|
if ( pNetInfo &&
|
|
pNetInfo->AdapterCount > 1 &&
|
|
ignoredRcode &&
|
|
status == ERROR_TIMEOUT )
|
|
{
|
|
resetOnFinalTimeout( pNetInfo );
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// send to best DNS servers in adapter list
|
|
// - servers sent to varies according to retry
|
|
// - send in previous timeout, if some servers did not respond
|
|
//
|
|
|
|
status = SendUdpToNextDnsServers(
|
|
pMsgSend,
|
|
pNetInfo,
|
|
retry,
|
|
sendCount ? pMsgRecv->Timeout : 0,
|
|
& sendCount );
|
|
|
|
sentCount = sendCount;
|
|
|
|
if ( status != ERROR_SUCCESS )
|
|
{
|
|
// if no more servers to send to, done
|
|
DNSDBG( RECV, (
|
|
"No more DNS servers to send message %p\n"
|
|
"\tprevious RCODE = %d\n",
|
|
pMsgSend,
|
|
ignoredRcode ));
|
|
goto ErrorReturn;
|
|
}
|
|
retry++;
|
|
precvIp = NULL;
|
|
rcode = DNS_RCODE_NO_ERROR;
|
|
pMsgRecv->Timeout = timeout;
|
|
DNS_ASSERT( sendCount != 0 );
|
|
|
|
frecvRetry = FALSE;
|
|
sendTime = GetCurrentTimeInSeconds();
|
|
|
|
//
|
|
// receive response
|
|
//
|
|
// note: the loop is strictly to allow us to drop back into
|
|
// receive if one server is misbehaving;
|
|
// in that case we go back into the receive without resending
|
|
// to allow other servers to respond
|
|
//
|
|
|
|
while ( sendCount )
|
|
{
|
|
//
|
|
// if have to retry recv, adjust down timeout
|
|
// - note, one second granularity is handled by
|
|
// rounding up at zero so we wait 0-1s beyond official
|
|
// timeout value
|
|
//
|
|
// DCR: calculate timeouts in ms?
|
|
//
|
|
|
|
if ( frecvRetry )
|
|
{
|
|
DWORD timeLeft;
|
|
|
|
timeLeft = timeout + sendTime - GetCurrentTimeInSeconds();
|
|
|
|
if ( (LONG)timeLeft < 0 )
|
|
{
|
|
status = ERROR_TIMEOUT;
|
|
break;
|
|
}
|
|
else if ( timeLeft == 0 )
|
|
{
|
|
timeLeft = 1;
|
|
}
|
|
pMsgRecv->Timeout = timeLeft;
|
|
}
|
|
frecvRetry = TRUE;
|
|
|
|
Send_SetMessageForRecv( pMsgRecv, pMsgSend );
|
|
|
|
status = Recv_Udp( pMsgRecv );
|
|
|
|
precvIp = Send_CopyRecvIp( &recvIp, pMsgRecv );
|
|
|
|
// recv wait completed
|
|
// - if timeout, commence next retry
|
|
// - if CONNRESET
|
|
// - indicate NO server on IP
|
|
// - back to recv if more DNS servers outstanding,
|
|
// - if success, verify packet
|
|
|
|
if ( status != ERROR_SUCCESS )
|
|
{
|
|
if ( status == ERROR_TIMEOUT )
|
|
{
|
|
break;
|
|
}
|
|
if ( status == WSAECONNRESET )
|
|
{
|
|
resetServerAfterRecv(
|
|
pNetInfo,
|
|
precvIp,
|
|
status );
|
|
|
|
sendCount--;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// no recv
|
|
// - unexpected winsock errors not handled above
|
|
// - should always have remote IP on success recv
|
|
|
|
if ( !precvIp )
|
|
{
|
|
DNSDBG( ANY, (
|
|
"Recv error %d -- no recv IP\n",
|
|
status ));
|
|
DNS_ASSERT( status != ERROR_SUCCESS );
|
|
continue;
|
|
}
|
|
|
|
// check XID match
|
|
|
|
if ( pMsgRecv->MessageHead.Xid != pMsgSend->MessageHead.Xid )
|
|
{
|
|
DNS_PRINT(( "WARNING: Incorrect XID in response. Ignoring.\n" ));
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// check DNS server IP match
|
|
//
|
|
|
|
if ( g_QueryIpMatching &&
|
|
!verifyValidServer( pNetInfo, precvIp ) )
|
|
{
|
|
DNS_PRINT((
|
|
"WARNING: Ignoring response from %s to query %p\n"
|
|
"\tIP does not match valid server\n",
|
|
DNSADDR_STRING(precvIp),
|
|
pMsgSend ));
|
|
continue;
|
|
}
|
|
|
|
// valid receive, drop outstanding send count
|
|
|
|
sendCount--;
|
|
|
|
//
|
|
// check question match
|
|
// - this is "Maggs Bug" check
|
|
// - ASSERT here just to investigate issue locally
|
|
// and make sure check is not bogus
|
|
// - specifically doing after sendCount decrement
|
|
// as this server will NOT send us a valid response
|
|
// - some servers don't reply with question so ignore
|
|
// if QuestionCount == 0
|
|
//
|
|
|
|
if ( pMsgRecv->MessageHead.QuestionCount != 0
|
|
&&
|
|
!Dns_IsSamePacketQuestion(
|
|
pMsgRecv,
|
|
pMsgSend ))
|
|
{
|
|
DNS_PRINT((
|
|
"ERROR: Bad question response from server %08x!\n"
|
|
"\tXID match, but question does not match question sent!\n",
|
|
recvIp ));
|
|
DNS_ASSERT( FALSE );
|
|
continue;
|
|
}
|
|
|
|
// suck out RCODE
|
|
|
|
rcode = pMsgRecv->MessageHead.ResponseCode;
|
|
|
|
//
|
|
// good response?
|
|
//
|
|
// special case AUTH-EMPTY and delegations
|
|
//
|
|
// - AUTH-EMPTY gets similar treatment to name error
|
|
// (this adapter can be considered to be finished)
|
|
//
|
|
// - referrals can be treated like SERVER_FAILURE
|
|
// (avoid this server for rest of query; server may
|
|
// be fine for direct lookup, so don't drop priority)
|
|
//
|
|
|
|
//
|
|
// DCR_CLEANUP: functionalize packet-categorization
|
|
// this would be standard errors
|
|
// plus AUTH-EMPTY versus referral
|
|
// plus OPT issues, etc
|
|
// could be called from TCP side also
|
|
//
|
|
// then would have separate determination about whether
|
|
// packet was terminal (below)
|
|
//
|
|
|
|
if ( rcode == DNS_RCODE_NO_ERROR )
|
|
{
|
|
if ( pMsgRecv->MessageHead.AnswerCount != 0 || fupdate )
|
|
{
|
|
goto GoodRecv;
|
|
}
|
|
|
|
//
|
|
// auth-empty
|
|
// - authoritative
|
|
// - or from cache, recursive response (hence not delegation)
|
|
//
|
|
// note: using dummy RCODE here as "ignored RCODE" serves
|
|
// the role of "best saved status" and roughly
|
|
// prioritizes in the way we want
|
|
//
|
|
// DCR: could change to "best saved status" as mapping is
|
|
// pretty much the same; would explicitly have to
|
|
// check
|
|
|
|
if ( pMsgRecv->MessageHead.Authoritative == 1 ||
|
|
( pMsgRecv->MessageHead.RecursionAvailable == 1 &&
|
|
pMsgRecv->MessageHead.RecursionDesired ) )
|
|
{
|
|
DNSDBG( RECV, (
|
|
"Recv AUTH-EMPTY response from %s\n",
|
|
DNSADDR_STRING(precvIp) ));
|
|
rcode = DNS_RCODE_AUTH_EMPTY_RESPONSE;
|
|
status = DNS_INFO_NO_RECORDS;
|
|
}
|
|
|
|
// referral
|
|
|
|
else if ( pMsgRecv->MessageHead.RecursionAvailable == 0 )
|
|
{
|
|
DNSDBG( RECV, (
|
|
"Recv referral response from %s\n",
|
|
DNSADDR_STRING(precvIp) ));
|
|
|
|
rcode = DNS_RCODE_SERVER_FAILURE;
|
|
status = DNS_ERROR_REFERRAL_RESPONSE;
|
|
}
|
|
|
|
// bogus (bad packet) response
|
|
|
|
else
|
|
{
|
|
rcode = DNS_RCODE_SERVER_FAILURE;
|
|
status = DNS_ERROR_BAD_PACKET;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = Dns_MapRcodeToStatus( (UCHAR)rcode );
|
|
}
|
|
|
|
//
|
|
// OPT failure screening
|
|
//
|
|
// DCR: FORMERR overload on OPT for update
|
|
// unless we read result to see if OPT, no way
|
|
// to determine if this is update problem or
|
|
// OPT problem
|
|
//
|
|
// - note, that checking if in list doesn't work because
|
|
// of MT issue (another query adds setting)
|
|
//
|
|
// - could be fixed by setting flag in network info
|
|
//
|
|
|
|
if ( rcode == DNS_RCODE_FORMAT_ERROR &&
|
|
!fupdate )
|
|
{
|
|
Send_SetServerOptExclude( precvIp );
|
|
|
|
// redo send but explicitly force OPT excluse
|
|
|
|
Send_MessagePrivate(
|
|
pMsgSend,
|
|
precvIp,
|
|
TRUE // exclude OPT
|
|
);
|
|
|
|
sendCount++;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// error RCODE do NOT terminate query
|
|
// - SERVER_FAILUREs
|
|
// - malfunctioning server
|
|
// - disjoint nets \ DNS namespace issues
|
|
//
|
|
// RCODE error removes particular server from further consideration
|
|
// during THIS query
|
|
//
|
|
// generally the higher RCODEs are more interesting
|
|
// NAME_ERROR > SERVER_FAILURE
|
|
// and
|
|
// update RCODEs > NAME_ERROR
|
|
// save the highest as return when no ERROR_SUCCESS response
|
|
//
|
|
// however for query NAME_ERROR is the highest RCODE,
|
|
// IF it is received on all adapters (if not REFUSED on one
|
|
// adapter may indicate that there really is a name)
|
|
//
|
|
// for UPDATE, REFUSED and higher are terminal RCODEs.
|
|
// downlevel servers (non-UPDATE-aware) servers would give
|
|
// FORMERR or NOTIMPL, so these are either valid responses or
|
|
// the zone has a completely busted server which must be detected
|
|
// and removed
|
|
//
|
|
//
|
|
// DCR_CLEANUP: functionalize packet-termination
|
|
// essentially is this type of packet terminal for
|
|
// this query;
|
|
// could be called from TCP side also
|
|
//
|
|
|
|
if ( rcode > ignoredRcode )
|
|
{
|
|
ignoredRcode = rcode;
|
|
}
|
|
|
|
//
|
|
// reset server priority for good recv
|
|
// - return ERROR_SUCCESS unless all adapters
|
|
// are
|
|
//
|
|
|
|
status = resetServerAfterRecv(
|
|
pNetInfo,
|
|
precvIp,
|
|
status );
|
|
|
|
//
|
|
// if all adapters are done (NAME_ERROR or NO_RECORDS)
|
|
// - return NAME_ERROR\NO_RECORDS rcode
|
|
// NO_RECORDS highest priority
|
|
// then NAME_ERROR
|
|
// then anything else
|
|
|
|
if ( status == DNS_ERROR_RCODE_NAME_ERROR )
|
|
{
|
|
if ( !fupdate && ignoredRcode != DNS_RCODE_AUTH_EMPTY_RESPONSE )
|
|
{
|
|
ignoredRcode = DNS_RCODE_NAME_ERROR;
|
|
}
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
//
|
|
// update RCODEs are terminal
|
|
//
|
|
|
|
if ( fupdate && rcode >= DNS_RCODE_REFUSED )
|
|
{
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
// continue wait for any other outstanding servers
|
|
}
|
|
|
|
DNSDBG( RECV, (
|
|
"Failed retry = %d for message %p\n"
|
|
"\tstatus = %d\n"
|
|
"\ttimeout = %d\n"
|
|
"\tservers out = %d\n"
|
|
"\tlast rcode = %d\n"
|
|
"\tignored RCODE = %d\n\n",
|
|
(retry - 1),
|
|
pMsgSend,
|
|
status,
|
|
timeout,
|
|
sendCount,
|
|
rcode,
|
|
ignoredRcode ));
|
|
continue;
|
|
|
|
} // end loop sending/recving packets
|
|
|
|
//
|
|
// falls here on retry exhausted
|
|
//
|
|
// note that any ignored RCODE takes precendence over failing
|
|
// status (which may be winsock error, timeout, or bogus
|
|
// NAME_ERROR from resetServerPriorities())
|
|
//
|
|
|
|
ErrorReturn:
|
|
|
|
// this can also hit on winsock error in DnsSend()
|
|
//
|
|
//DNS_ASSERT( ignoredRcode || status == ERROR_TIMEOUT );
|
|
|
|
//
|
|
// error responses from all servers or timeouts
|
|
//
|
|
|
|
DNSDBG( RECV, (
|
|
"Error or timeouts from all servers for message %p\n"
|
|
"\treturning RCODE = %d\n",
|
|
pMsgSend,
|
|
ignoredRcode ));
|
|
|
|
if ( ignoredRcode )
|
|
{
|
|
// empty-auth reponse is tracked with bogus RCODE,
|
|
// switch to status code -- DNS_INFO_NO_RECORDS
|
|
|
|
if ( !fupdate && ignoredRcode == DNS_RCODE_AUTH_EMPTY_RESPONSE )
|
|
{
|
|
status = DNS_INFO_NO_RECORDS;
|
|
}
|
|
else
|
|
{
|
|
status = Dns_MapRcodeToStatus( (UCHAR)ignoredRcode );
|
|
}
|
|
}
|
|
goto Done;
|
|
|
|
|
|
GoodRecv:
|
|
|
|
resetServerAfterRecv(
|
|
pNetInfo,
|
|
precvIp,
|
|
rcode );
|
|
|
|
DNSDBG( RECV, (
|
|
"Recv'd response for query at %p from DNS %s\n",
|
|
pMsgSend,
|
|
DNSADDR_STRING(precvIp) ));
|
|
|
|
Done:
|
|
|
|
//
|
|
// close UDP sockets
|
|
//
|
|
// DCR_ENHANCE: allow for possibility of keeping socket alive
|
|
//
|
|
|
|
Socket_CloseMessageSockets( pMsgSend );
|
|
Socket_ClearMessageSockets( pMsgRecv );
|
|
|
|
IF_DNSDBG( RECV )
|
|
{
|
|
DNSDBG( SEND, (
|
|
"Leave Send_AndRecvUdp()\n"
|
|
"\tstatus = %d\n"
|
|
"\ttime = %d\n"
|
|
"\tsend msg = %p\n"
|
|
"\trecv msg = %p\n",
|
|
status,
|
|
Dns_GetCurrentTimeInSeconds(),
|
|
pMsgSend,
|
|
pMsgRecv ));
|
|
|
|
DnsDbg_NetworkInfo(
|
|
"Network info after UDP recv\n",
|
|
pNetInfo );
|
|
}
|
|
|
|
// if allocated adapter list free it
|
|
|
|
if ( ptempNetInfo )
|
|
{
|
|
NetInfo_Free( ptempNetInfo );
|
|
}
|
|
|
|
// should not return NXRRSET except on update
|
|
|
|
ASSERT( fupdate || status != DNS_ERROR_RCODE_NXRRSET );
|
|
|
|
return( status );
|
|
}
|
|
|
|
|
|
|
|
DNS_STATUS
|
|
Send_AndRecvMulticast(
|
|
IN OUT PDNS_MSG_BUF pMsgSend,
|
|
OUT PDNS_MSG_BUF pMsgRecv,
|
|
IN OUT PDNS_NETINFO pNetInfo OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends to and waits to recv from remote DNS.
|
|
|
|
Arguments:
|
|
|
|
pMsgSend - message to send
|
|
|
|
ppMsgRecv - and reuse
|
|
|
|
pNetInfo -- adapter list DNS server info
|
|
|
|
DCR - pNetInfo parameter could be leveraged to
|
|
identify specific networks to target a multicast
|
|
query against. For example, there could be a multihomed
|
|
machine that is configured to only multicast on one
|
|
of many adapters, thus filtering out useless mDNS packets.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful response.
|
|
NAME_ERROR on timeout.
|
|
Error status on send\recv failure.
|
|
|
|
--*/
|
|
{
|
|
#if 1
|
|
return DNS_ERROR_RCODE_NAME_ERROR;
|
|
#else
|
|
SOCKET s;
|
|
INT fcreatedSocket = FALSE;
|
|
INT retry;
|
|
DWORD timeout;
|
|
DNS_STATUS status = ERROR_TIMEOUT;
|
|
IP4_ADDRESS recvIp = 0;
|
|
DWORD rcode = 0;
|
|
DWORD ignoredRcode = 0;
|
|
|
|
DNSDBG( SEND, (
|
|
"Enter Send_AndRecvMulticast()\n"
|
|
"\tsend msg at %p\n"
|
|
"\tsocket %d\n"
|
|
"\trecv msg at %p\n",
|
|
pMsgSend,
|
|
pMsgSend->Socket,
|
|
pMsgRecv ));
|
|
|
|
// verify params
|
|
|
|
if ( !pMsgSend || !pMsgRecv )
|
|
{
|
|
return( ERROR_INVALID_PARAMETER );
|
|
}
|
|
|
|
if ( pMsgSend->MessageHead.Opcode == DNS_OPCODE_UPDATE )
|
|
{
|
|
return( ERROR_INVALID_PARAMETER );
|
|
}
|
|
|
|
//
|
|
// TCP invalid -- invalid
|
|
//
|
|
// problem is we either leak TCP socket, or we close
|
|
// it here and may screw things up at higher level
|
|
//
|
|
|
|
if ( pMsgSend->fTcp &&
|
|
(pMsgSend->Socket4 || pMsgSend->Socket6) )
|
|
{
|
|
status = ERROR_INVALID_PARAMETER;
|
|
goto Done;
|
|
}
|
|
|
|
pMsgSend->fTcp = FALSE;
|
|
pMsgRecv->fTcp = FALSE;
|
|
pMsgRecv->Socket = 0;
|
|
pMsgRecv->Socket6 = 0;
|
|
pMsgRecv->Socket4 = 0;
|
|
|
|
//
|
|
// loop sending until
|
|
// - receive successful response
|
|
// or
|
|
// - receive errors response from all servers
|
|
// or
|
|
// - reach final timeout on all servers
|
|
//
|
|
|
|
retry = 0;
|
|
|
|
while ( 1 )
|
|
{
|
|
timeout = g_MulticastQueryTimeouts[retry];
|
|
|
|
//
|
|
// zero timeout indicates end of retries for this query type
|
|
//
|
|
|
|
if ( timeout == 0 )
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// send to multicast DNS address
|
|
//
|
|
|
|
if ( retry == 0 )
|
|
{ //
|
|
// setup mcast address
|
|
//
|
|
|
|
status = DnsAddr_BuildMcast(
|
|
&addr,
|
|
Family,
|
|
pName
|
|
);
|
|
if ( status != NO_ERROR )
|
|
{
|
|
goto Failed;
|
|
}
|
|
|
|
//
|
|
// create multicast socket
|
|
// - bound to this address and DNS port
|
|
//
|
|
|
|
sock = Socket_Create(
|
|
Family,
|
|
SOCK_DGRAM,
|
|
&addr,
|
|
MCAST_PORT_NET_ORDER,
|
|
FALSE,
|
|
TRUE );
|
|
|
|
if ( sock == 0 )
|
|
{
|
|
goto Failed;
|
|
}
|
|
|
|
|
|
|
|
Dns_InitializeMsgRemoteSockaddr( pMsgSend, MCAST_DNS_RADDR );
|
|
}
|
|
|
|
Dns_Send( pMsgSend );
|
|
|
|
retry++;
|
|
rcode = DNS_RCODE_NO_ERROR;
|
|
pMsgRecv->Timeout = timeout;
|
|
|
|
//
|
|
// receive response
|
|
//
|
|
// note: the loop is strictly to allow us to drop back into
|
|
// receive if one server is misbehaving;
|
|
// in that case we go back into the receive without resending
|
|
// to allow other servers to respond
|
|
//
|
|
|
|
Send_SetMessageForRecv( pMsgRecv, pMsgSend );
|
|
|
|
status = Recv_Udp( pMsgRecv );
|
|
|
|
// recv wait completed
|
|
// - if timeout, commence next retry
|
|
// - if CONNRESET
|
|
// - back to recv if more DNS servers outstanding,
|
|
// - otherwise equivalent treat as timeout, except with
|
|
// very long timeout
|
|
// - if success, verify packet
|
|
|
|
if ( status != ERROR_SUCCESS )
|
|
{
|
|
if ( status == ERROR_TIMEOUT )
|
|
{
|
|
continue;
|
|
}
|
|
if ( status == WSAECONNRESET )
|
|
{
|
|
pMsgRecv->Timeout = NO_DNS_PRIORITY_DROP;
|
|
status = ERROR_TIMEOUT;
|
|
continue;
|
|
}
|
|
goto Done;
|
|
}
|
|
|
|
// check XID match
|
|
|
|
if ( pMsgRecv->MessageHead.Xid != pMsgSend->MessageHead.Xid )
|
|
{
|
|
DNS_PRINT(( "WARNING: Incorrect XID in response. Ignoring.\n" ));
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// good response?
|
|
//
|
|
// special case AUTH-EMPTY and delegations
|
|
//
|
|
// - AUTH-EMPTY gets similar treatment to name error
|
|
// (this adapter can be considered to be finished)
|
|
//
|
|
// - referrals can be treated like SERVER_FAILURE
|
|
// (avoid this server for rest of query; server may
|
|
// be fine for direct lookup, so don't drop priority)
|
|
//
|
|
|
|
rcode = pMsgRecv->MessageHead.ResponseCode;
|
|
|
|
if ( rcode == DNS_RCODE_NO_ERROR )
|
|
{
|
|
if ( pMsgRecv->MessageHead.AnswerCount != 0 )
|
|
{
|
|
goto Done;
|
|
}
|
|
|
|
// auth-empty
|
|
|
|
if ( pMsgRecv->MessageHead.Authoritative == 1 )
|
|
{
|
|
DNSDBG( RECV, (
|
|
"Recv AUTH-EMPTY response from %s\n",
|
|
MSG_REMOTE_IPADDR_STRING(pMsgRecv) ));
|
|
rcode = DNS_RCODE_AUTH_EMPTY_RESPONSE;
|
|
}
|
|
}
|
|
} // end loop sending/recving packets
|
|
|
|
Done:
|
|
|
|
//
|
|
// if created socket -- close it
|
|
//
|
|
// DCR_ENHANCE: allow for possibility of keeping socket alive
|
|
//
|
|
|
|
Socket_CloseMessageSockets( pMsgSend );
|
|
Socket_ClearMessageSockets( pMsgRecv );
|
|
|
|
IF_DNSDBG( RECV )
|
|
{
|
|
DNSDBG( SEND, (
|
|
"Leave Send_AndRecvMulticast()\n"
|
|
"\tstatus = %d\n"
|
|
"\ttime = %d\n"
|
|
"\tsend msg at %p\n"
|
|
"\trecv msg at %p\n",
|
|
status,
|
|
Dns_GetCurrentTimeInSeconds(),
|
|
pMsgSend,
|
|
pMsgRecv ));
|
|
}
|
|
|
|
if ( status == ERROR_TIMEOUT )
|
|
{
|
|
status = DNS_ERROR_RCODE_NAME_ERROR;
|
|
}
|
|
|
|
return( status );
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// TCP routines
|
|
//
|
|
|
|
DNS_STATUS
|
|
Send_OpenTcpConnectionAndSend(
|
|
IN OUT PDNS_MSG_BUF pMsg,
|
|
IN PDNS_ADDR pServAddr,
|
|
IN BOOL fBlocking
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Connect via TCP to desired server.
|
|
|
|
Arguments:
|
|
|
|
pMsg -- message info to set with connection socket
|
|
|
|
pServAddr -- IP of DNS server to connect to
|
|
|
|
fBlocking -- blocking connection
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful.
|
|
FALSE on connect error.
|
|
|
|
--*/
|
|
{
|
|
SOCKET s;
|
|
INT err;
|
|
|
|
//
|
|
// setup a TCP socket
|
|
// - INADDR_ANY -- let stack select source IP
|
|
//
|
|
|
|
s = Socket_Create(
|
|
DnsAddr_Family( pServAddr ),
|
|
SOCK_STREAM,
|
|
NULL, // default binding
|
|
0, // any port
|
|
0 // no flags
|
|
);
|
|
|
|
if ( s == 0 )
|
|
{
|
|
DNS_PRINT((
|
|
"ERROR: unable to create TCP socket to create TCP"
|
|
"\tconnection to %s.\n",
|
|
DNSADDR_STRING( pServAddr ) ));
|
|
|
|
pMsg->Socket = 0;
|
|
err = WSAGetLastError();
|
|
if ( !err )
|
|
{
|
|
DNS_ASSERT( FALSE );
|
|
err = WSAENOTSOCK;
|
|
}
|
|
return( err );
|
|
}
|
|
|
|
//
|
|
// set TCP params
|
|
// - do before connect(), so can directly use sockaddr buffer
|
|
//
|
|
|
|
pMsg->fTcp = TRUE;
|
|
Send_SetMsgRemoteSockaddr( pMsg, pServAddr );
|
|
|
|
//
|
|
// connect
|
|
//
|
|
|
|
err = connect(
|
|
s,
|
|
& pMsg->RemoteAddress.Sockaddr,
|
|
pMsg->RemoteAddress.SockaddrLength
|
|
);
|
|
if ( err )
|
|
{
|
|
PCHAR pchIpString;
|
|
|
|
err = GetLastError();
|
|
pchIpString = MSG_REMOTE_IPADDR_STRING( pMsg );
|
|
|
|
DNS_LOG_EVENT(
|
|
DNS_EVENT_CANNOT_CONNECT_TO_SERVER,
|
|
1,
|
|
&pchIpString,
|
|
err );
|
|
|
|
DNSDBG( TCP, (
|
|
"Unable to establish TCP connection to %s.\n"
|
|
"\tstatus = %d\n",
|
|
pchIpString,
|
|
err ));
|
|
|
|
Socket_Close( s );
|
|
pMsg->Socket = 0;
|
|
if ( !err )
|
|
{
|
|
err = WSAENOTCONN;
|
|
}
|
|
return( err );
|
|
}
|
|
|
|
DNSDBG( TCP, (
|
|
"Connected to %s for message at %p.\n"
|
|
"\tsocket = %d\n",
|
|
MSG_REMOTE_IPADDR_STRING( pMsg ),
|
|
pMsg,
|
|
s ));
|
|
|
|
pMsg->Socket = s;
|
|
|
|
//
|
|
// send desired packet
|
|
//
|
|
|
|
err = Send_MessagePrivate(
|
|
pMsg,
|
|
NULL, // no address
|
|
TRUE // no OPT
|
|
);
|
|
|
|
return( (DNS_STATUS)err );
|
|
|
|
} // Send_OpenTcpConnectionAndSend
|
|
|
|
|
|
|
|
DNS_STATUS
|
|
Dns_RecvTcp(
|
|
IN OUT PDNS_MSG_BUF pMsg
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Receive TCP DNS message.
|
|
|
|
EXPORTED (security.c): Dns_RecvTcp
|
|
EXPORTED rename Recv_Tcp
|
|
|
|
Arguments:
|
|
|
|
pMsg - message info buffer to receive packet; must contain connected
|
|
TCP socket
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successfully receive a message.
|
|
Error code on failure.
|
|
|
|
--*/
|
|
{
|
|
PCHAR pchrecv; // ptr to recv location
|
|
INT recvLength; // length left to recv()
|
|
SOCKET socket;
|
|
INT err = NO_ERROR;
|
|
WORD messageLength;
|
|
struct timeval selectTimeout;
|
|
struct fd_set fdset;
|
|
|
|
DNS_ASSERT( pMsg );
|
|
|
|
socket = pMsg->Socket;
|
|
|
|
DNSDBG( RECV, (
|
|
"Enter Dns_RecvTcp( %p )\n"
|
|
"\tRecv on socket = %d.\n"
|
|
"\tBytes left to receive = %d\n"
|
|
"\tTimeout = %d\n",
|
|
pMsg,
|
|
socket,
|
|
pMsg->BytesToReceive,
|
|
pMsg->Timeout
|
|
));
|
|
|
|
//
|
|
// verify socket, setup fd_set and select timeout
|
|
//
|
|
|
|
if ( socket == 0 || socket == INVALID_SOCKET )
|
|
{
|
|
return( ERROR_INVALID_PARAMETER );
|
|
}
|
|
|
|
FD_ZERO( &fdset );
|
|
FD_SET( socket, &fdset );
|
|
|
|
selectTimeout.tv_usec = 0;
|
|
selectTimeout.tv_sec = pMsg->Timeout;
|
|
|
|
//
|
|
// new message -- set to receive message length
|
|
// - reusing buffer
|
|
// - new buffer
|
|
//
|
|
// continuing receive of message
|
|
//
|
|
|
|
if ( !pMsg->pchRecv )
|
|
{
|
|
DNS_ASSERT( pMsg->fMessageComplete || pMsg->MessageLength == 0 );
|
|
|
|
pchrecv = (PCHAR) &pMsg->MessageLength;
|
|
recvLength = (INT) sizeof( WORD );
|
|
}
|
|
else
|
|
{
|
|
pchrecv = (PCHAR) pMsg->pchRecv;
|
|
recvLength = (INT) pMsg->BytesToReceive;
|
|
}
|
|
DNS_ASSERT( recvLength );
|
|
|
|
|
|
//
|
|
// loop until receive the entire message
|
|
//
|
|
|
|
while ( 1 )
|
|
{
|
|
//
|
|
// wait for stack to indicate packet reception
|
|
//
|
|
|
|
err = select( 0, &fdset, NULL, NULL, &selectTimeout );
|
|
if ( err <= 0 )
|
|
{
|
|
if ( err < 0 )
|
|
{
|
|
// select error
|
|
err = WSAGetLastError();
|
|
DNS_PRINT(( "ERROR: select() error = %p\n", err ));
|
|
return( err );
|
|
}
|
|
else
|
|
{
|
|
Trace_LogRecvEvent(
|
|
pMsg,
|
|
ERROR_TIMEOUT,
|
|
TRUE // TCP
|
|
);
|
|
|
|
DNS_PRINT(( "ERROR: timeout on response %p\n", pMsg ));
|
|
return( ERROR_TIMEOUT );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Only recv() exactly as much data as indicated.
|
|
// Another message could follow during zone transfer.
|
|
//
|
|
|
|
err = recv(
|
|
socket,
|
|
pchrecv,
|
|
recvLength,
|
|
0 );
|
|
|
|
DNSDBG( TCP, (
|
|
"\nRecv'd %d bytes on TCP socket %d\n",
|
|
err,
|
|
socket ));
|
|
|
|
//
|
|
// TCP FIN received -- error in the middle of a message.
|
|
//
|
|
|
|
if ( err == 0 )
|
|
{
|
|
goto FinReceived;
|
|
}
|
|
|
|
//
|
|
// recv error
|
|
// - perfectly reasonable if shutting down
|
|
// - otherwise actual recv() error
|
|
//
|
|
|
|
if ( err == SOCKET_ERROR )
|
|
{
|
|
goto SockError;
|
|
}
|
|
|
|
//
|
|
// update buffer params
|
|
//
|
|
|
|
recvLength -= err;
|
|
pchrecv += err;
|
|
|
|
DNS_ASSERT( recvLength >= 0 );
|
|
|
|
//
|
|
// received message or message length
|
|
//
|
|
|
|
if ( recvLength == 0 )
|
|
{
|
|
// done receiving message
|
|
|
|
if ( pchrecv > (PCHAR)&pMsg->MessageHead )
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// recv'd message length, setup to recv() message
|
|
// - byte flip length
|
|
// - continue reception with this length
|
|
//
|
|
|
|
DNS_ASSERT( pchrecv == (PCHAR)&pMsg->MessageHead );
|
|
|
|
messageLength = pMsg->MessageLength;
|
|
pMsg->MessageLength = messageLength = ntohs( messageLength );
|
|
if ( messageLength < sizeof(DNS_HEADER) )
|
|
{
|
|
DNS_PRINT((
|
|
"ERROR: Received TCP message with bad message"
|
|
" length %d.\n",
|
|
messageLength ));
|
|
|
|
goto BadTcpMessage;
|
|
}
|
|
recvLength = messageLength;
|
|
|
|
DNSDBG( TCP, (
|
|
"Received TCP message length %d, on socket %d,\n"
|
|
"\tfor msg at %p.\n",
|
|
messageLength,
|
|
socket,
|
|
pMsg ));
|
|
|
|
// starting recv of valid message length
|
|
|
|
if ( messageLength <= pMsg->BufferLength )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// note: currently do not realloc
|
|
|
|
goto BadTcpMessage;
|
|
#if 0
|
|
//
|
|
// DCR: allow TCP realloc
|
|
// - change call signature OR
|
|
// - return pMsg with ptr to realloced
|
|
// perhaps better to ignore and do 64K buffer all the time
|
|
//
|
|
// realloc, if existing message too small
|
|
//
|
|
|
|
pMsg = Dns_ReallocateTcpMessage( pMsg, messageLength );
|
|
if ( !pMsg )
|
|
{
|
|
return( GetLastError() );
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
//
|
|
// Message received
|
|
// recv ptr serves as flag, clear to start new message on reuse
|
|
//
|
|
|
|
pMsg->fMessageComplete = TRUE;
|
|
pMsg->pchRecv = NULL;
|
|
|
|
//
|
|
// return message information
|
|
// - flip count bytes
|
|
//
|
|
|
|
DNS_BYTE_FLIP_HEADER_COUNTS( &pMsg->MessageHead );
|
|
|
|
Trace_LogRecvEvent(
|
|
pMsg,
|
|
0,
|
|
TRUE // TCP
|
|
);
|
|
|
|
IF_DNSDBG( RECV )
|
|
{
|
|
DnsDbg_Message(
|
|
"Received TCP packet",
|
|
pMsg );
|
|
}
|
|
return( ERROR_SUCCESS );
|
|
|
|
|
|
SockError:
|
|
|
|
err = GetLastError();
|
|
|
|
#if 0
|
|
//
|
|
// note: want non-blocking sockets if doing full async
|
|
//
|
|
// WSAEWOULD block is NORMAL return for message not fully recv'd.
|
|
// - save state of message reception
|
|
//
|
|
// We use non-blocking sockets, so bad client (that fails to complete
|
|
// message) doesn't hang TCP receiver.
|
|
//
|
|
|
|
if ( err == WSAEWOULDBLOCK )
|
|
{
|
|
pMsg->pchRecv = pchrecv;
|
|
pMsg->BytesToReceive = recvLength;
|
|
|
|
DNSDBG( TCP, (
|
|
"Leave ReceiveTcpMessage() after WSAEWOULDBLOCK.\n"
|
|
"\tSocket=%d, Msg=%p\n"
|
|
"\tBytes left to receive = %d\n",
|
|
socket,
|
|
pMsg,
|
|
pMsg->BytesToReceive
|
|
));
|
|
goto CleanupConnection;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// cancelled connection
|
|
// -- perfectly legal, question is why
|
|
//
|
|
|
|
if ( pchrecv == (PCHAR) &pMsg->MessageLength
|
|
&&
|
|
( err == WSAESHUTDOWN ||
|
|
err == WSAECONNABORTED ||
|
|
err == WSAECONNRESET ) )
|
|
{
|
|
DNSDBG( TCP, (
|
|
"WARNING: Recv RESET (%d) on socket %d\n",
|
|
err,
|
|
socket ));
|
|
goto CleanupConnection;
|
|
}
|
|
|
|
// anything else is our problem
|
|
|
|
DNS_LOG_EVENT(
|
|
DNS_EVENT_RECV_CALL_FAILED,
|
|
0,
|
|
NULL,
|
|
err );
|
|
|
|
DNSDBG( ANY, (
|
|
"ERROR: recv() of TCP message failed.\n"
|
|
"\t%d bytes recvd\n"
|
|
"\t%d bytes left\n"
|
|
"\tGetLastError = 0x%08lx.\n",
|
|
pchrecv - (PCHAR)&pMsg->MessageLength,
|
|
recvLength,
|
|
err ));
|
|
DNS_ASSERT( FALSE );
|
|
|
|
goto CleanupConnection;
|
|
|
|
FinReceived:
|
|
|
|
//
|
|
// valid FIN -- if recv'd between messages (before message length)
|
|
//
|
|
|
|
DNSDBG( TCP, (
|
|
"ERROR: Recv TCP FIN (0 bytes) on socket %d\n",
|
|
socket,
|
|
recvLength ));
|
|
|
|
if ( !pMsg->MessageLength && pchrecv == (PCHAR)&pMsg->MessageLength )
|
|
{
|
|
err = DNS_ERROR_NO_PACKET;
|
|
goto CleanupConnection;
|
|
}
|
|
|
|
//
|
|
// FIN during message -- invalid message
|
|
//
|
|
|
|
DNSDBG( ANY, (
|
|
"ERROR: TCP message received has incorrect length.\n"
|
|
"\t%d bytes left when recv'd FIN.\n",
|
|
recvLength ));
|
|
goto BadTcpMessage;
|
|
|
|
|
|
BadTcpMessage:
|
|
{
|
|
PCHAR pchServer = MSG_REMOTE_IPADDR_STRING(pMsg);
|
|
|
|
DNS_LOG_EVENT(
|
|
DNS_EVENT_BAD_TCP_MESSAGE,
|
|
1,
|
|
& pchServer,
|
|
0 );
|
|
}
|
|
err = DNS_ERROR_BAD_PACKET;
|
|
|
|
CleanupConnection:
|
|
|
|
// note: don't actually close socket
|
|
// the socket is usually still referenced by the send message
|
|
// buffer and is closed when the calling function cleans it up
|
|
|
|
return err ? err : DNS_ERROR_BAD_PACKET;
|
|
}
|
|
|
|
|
|
|
|
DNS_STATUS
|
|
Send_AndRecvTcp(
|
|
IN OUT PSEND_BLOB pBlob
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends to and waits to recv from remote DNS.
|
|
|
|
INTERNAL public function.
|
|
|
|
Arguments:
|
|
|
|
pBlob -- send info
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful packet reception.
|
|
Error status on failure.
|
|
|
|
--*/
|
|
{
|
|
DNS_STATUS status = DNS_ERROR_NO_DNS_SERVERS;
|
|
DWORD rcode;
|
|
DWORD i;
|
|
PDNS_NETINFO pnetInfo;
|
|
PADDR_ARRAY pallocServerArray = NULL;
|
|
PADDR_ARRAY pservArray;
|
|
PIP4_ARRAY pserv4Array;
|
|
PDNS_MSG_BUF psendMsg;
|
|
PDNS_MSG_BUF precvMsg;
|
|
|
|
DNSDBG( SEND, (
|
|
"Enter Send_AndRecvTcp( %p )\n",
|
|
pBlob ));
|
|
|
|
//
|
|
// unpack
|
|
//
|
|
|
|
pnetInfo = pBlob->pNetInfo;
|
|
pservArray = pBlob->pServerList;
|
|
pserv4Array = pBlob->pServ4List;
|
|
psendMsg = pBlob->pSendMsg;
|
|
precvMsg = pBlob->Results.pMessage;
|
|
|
|
//
|
|
// verify params
|
|
//
|
|
|
|
if ( !psendMsg || !precvMsg || (!pnetInfo && !pservArray && !pserv4Array) )
|
|
{
|
|
return( ERROR_INVALID_PARAMETER );
|
|
}
|
|
|
|
//
|
|
// build server IP array?
|
|
//
|
|
// DCR: should use netinfo priorities in TCP also
|
|
// DCR: need TCP netinfo for IP6
|
|
// DCR: handle IP4 array -- here or ABOVE here
|
|
//
|
|
|
|
if ( !pservArray )
|
|
{
|
|
// FIX6: convert IP4 array here???
|
|
|
|
pallocServerArray = NetInfo_ConvertToAddrArray(
|
|
pnetInfo,
|
|
NULL, // all adapters
|
|
0 // no addr family
|
|
);
|
|
if ( !pallocServerArray )
|
|
{
|
|
return( DNS_ERROR_NO_MEMORY );
|
|
}
|
|
pservArray = pallocServerArray;
|
|
}
|
|
|
|
//
|
|
// init messages for TCP
|
|
//
|
|
|
|
DNS_ASSERT( psendMsg->Socket == 0 );
|
|
|
|
psendMsg->fTcp = TRUE;
|
|
psendMsg->Socket = 0;
|
|
SET_MESSAGE_FOR_TCP_RECV( precvMsg );
|
|
|
|
//
|
|
// loop sending until
|
|
// - receive successful response
|
|
// or
|
|
// - receive errors response from all servers
|
|
// or
|
|
// - reach final timeout on all servers
|
|
//
|
|
|
|
if ( precvMsg->Timeout == 0 )
|
|
{
|
|
precvMsg->Timeout = DEFAULT_TCP_TIMEOUT;
|
|
}
|
|
|
|
for( i=0; i<pservArray->AddrCount; i++ )
|
|
{
|
|
//
|
|
// close any previous connection
|
|
//
|
|
|
|
if ( psendMsg->Socket )
|
|
{
|
|
Socket_CloseMessageSockets( psendMsg );
|
|
Socket_ClearMessageSockets( precvMsg );
|
|
}
|
|
|
|
//
|
|
// connect and send to next server
|
|
//
|
|
|
|
status = Send_OpenTcpConnectionAndSend(
|
|
psendMsg,
|
|
&pservArray->AddrArray[i],
|
|
TRUE
|
|
);
|
|
if ( status != ERROR_SUCCESS )
|
|
{
|
|
continue;
|
|
}
|
|
DNS_ASSERT( psendMsg->Socket != INVALID_SOCKET && psendMsg->Socket != 0 );
|
|
|
|
//
|
|
// receive response
|
|
// - if successful receive, done
|
|
// - if timeout continue
|
|
// - other errors indicate some setup or system level problem
|
|
// note: Dns_RecvTcp will close and zero msg->socket on error!
|
|
//
|
|
|
|
Send_SetMessageForRecv( precvMsg, psendMsg );
|
|
|
|
status = Dns_RecvTcp( precvMsg );
|
|
|
|
//
|
|
// timed out (or error)
|
|
// - if end of timeout, quit
|
|
// - otherwise double timeout and resend
|
|
//
|
|
|
|
switch( status )
|
|
{
|
|
case ERROR_SUCCESS:
|
|
break;
|
|
|
|
case ERROR_TIMEOUT:
|
|
|
|
DNS_PRINT((
|
|
"ERROR: connected to server at %s\n"
|
|
"\tbut no response to packet at %p\n",
|
|
MSG_REMOTE_IPADDR_STRING( psendMsg ),
|
|
psendMsg
|
|
));
|
|
continue;
|
|
|
|
default:
|
|
|
|
DNS_PRINT((
|
|
"ERROR: connected to server at %s to send packet %p\n"
|
|
"\tbut error %d encountered on receive.\n",
|
|
MSG_REMOTE_IPADDR_STRING( psendMsg ),
|
|
psendMsg,
|
|
status
|
|
));
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// verify XID match
|
|
//
|
|
|
|
if ( precvMsg->MessageHead.Xid != psendMsg->MessageHead.Xid )
|
|
{
|
|
DNS_PRINT((
|
|
"ERROR: Incorrect XID in response. Ignoring.\n" ));
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// verify question match
|
|
// - this is "Maggs Bug" check
|
|
// - ASSERT here just to investigate issue locally
|
|
// and make sure check is not bogus
|
|
//
|
|
|
|
if ( !Dns_IsSamePacketQuestion(
|
|
precvMsg,
|
|
psendMsg ))
|
|
{
|
|
DNS_PRINT((
|
|
"ERROR: Bad question response from server %s!\n"
|
|
"\tXID match, but question does not match question sent!\n",
|
|
MSG_REMOTE_IPADDR_STRING( psendMsg ) ));
|
|
|
|
DNS_ASSERT( FALSE );
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// check response code
|
|
// - some move to next server, others terminal
|
|
//
|
|
// DCR_FIX1: bring TCP RCODE handling in-line with UDP
|
|
//
|
|
// DCR_FIX: save best TCP RCODE
|
|
// preserve RCODE (and message) for useless TCP response
|
|
// would need to then reset TIMEOUT to success at end
|
|
// or use these RCODEs as status returns
|
|
//
|
|
|
|
rcode = precvMsg->MessageHead.ResponseCode;
|
|
|
|
switch( rcode )
|
|
{
|
|
case DNS_RCODE_SERVER_FAILURE:
|
|
case DNS_RCODE_NOT_IMPLEMENTED:
|
|
case DNS_RCODE_REFUSED:
|
|
|
|
DNS_PRINT((
|
|
"WARNING: Servers have responded with failure.\n" ));
|
|
continue;
|
|
|
|
default:
|
|
|
|
break;
|
|
}
|
|
break;
|
|
|
|
} // end loop sending/recving UPDATEs
|
|
|
|
//
|
|
// close up final connection
|
|
// unless set to keep open for reuse
|
|
//
|
|
|
|
Socket_CloseMessageSockets( psendMsg );
|
|
Socket_ClearMessageSockets( precvMsg );
|
|
|
|
// if allocated adapter list free it
|
|
|
|
if ( pallocServerArray )
|
|
{
|
|
FREE_HEAP( pallocServerArray );
|
|
}
|
|
|
|
DNSDBG( SEND, (
|
|
"Leaving Send_AndRecvTcp()\n"
|
|
"\tstatus = %d\n",
|
|
status ));
|
|
|
|
return( status );
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
DNS_STATUS
|
|
Dns_AsyncRecv(
|
|
IN OUT PDNS_MSG_BUF pMsgRecv
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Drop recv on async socket.
|
|
|
|
Arguments:
|
|
|
|
pMsgRecv - message to receive; OPTIONAL, if NULL message buffer
|
|
is allocated;
|
|
in either case global pDnsAsyncRecvMsg points at buffer
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
Error status on failure.
|
|
|
|
--*/
|
|
{
|
|
WSABUF wsabuf;
|
|
DWORD bytesRecvd;
|
|
DWORD flags = 0;
|
|
|
|
IF_DNSDBG( RECV )
|
|
{
|
|
DNS_PRINT((
|
|
"Enter Dns_AsyncRecv( %p )\n",
|
|
pMsgRecv ));
|
|
}
|
|
|
|
//
|
|
// allocate buffer if none given
|
|
//
|
|
|
|
if ( !pMsgRecv )
|
|
{
|
|
pMsgRecv = Dns_AllocateMsgBuf( MAXWORD );
|
|
if ( !pMsgRecv )
|
|
{
|
|
return( GetLastError() ):
|
|
}
|
|
}
|
|
pDnsAsyncRecvMsg = pMsgRecv;
|
|
|
|
|
|
//
|
|
// reset i/o completion event
|
|
//
|
|
|
|
ResetEvent( hDnsSocketEvent );
|
|
DNS_ASSERT( hDnsSocketEvent == Dns_SocketOverlapped.hEvent );
|
|
|
|
//
|
|
// drop down recv
|
|
//
|
|
|
|
status = WSARecvFrom(
|
|
DnsSocket,
|
|
& wsabuf,
|
|
1,
|
|
& bytesRecvd, // dummy
|
|
& flags,
|
|
& pMsgRecv->RemoteAddress.Sockaddr,
|
|
& pMsgRecv->RemoteAddress.SockaddrLength,
|
|
& DnsSocketOverlapped,
|
|
NULL // no completion routine
|
|
);
|
|
|
|
|
|
return( ERROR_SUCCESS );
|
|
|
|
Failed:
|
|
|
|
return( status );
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
VOID
|
|
Dns_InitQueryTimeouts(
|
|
VOID
|
|
)
|
|
{
|
|
HKEY hKey = NULL;
|
|
DWORD status;
|
|
DWORD dwType;
|
|
DWORD ValueSize;
|
|
LPSTR lpTimeouts = NULL;
|
|
|
|
g_QueryTimeouts = QueryTimeouts;
|
|
g_QuickQueryTimeouts = QuickQueryTimeouts;
|
|
g_MulticastQueryTimeouts = MulticastQueryTimeouts;
|
|
|
|
status = RegOpenKeyExA(
|
|
HKEY_LOCAL_MACHINE,
|
|
NT_TCPIP_REG_LOCATION,
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
&hKey );
|
|
|
|
if ( status )
|
|
return;
|
|
|
|
if ( !hKey )
|
|
return;
|
|
|
|
status = RegQueryValueExA(
|
|
hKey,
|
|
DNS_QUERY_TIMEOUTS,
|
|
NULL,
|
|
&dwType,
|
|
NULL,
|
|
&ValueSize );
|
|
|
|
if ( !status )
|
|
{
|
|
if ( ValueSize == 0 )
|
|
{
|
|
goto GetQuickQueryTimeouts;
|
|
}
|
|
|
|
lpTimeouts = ALLOCATE_HEAP( ValueSize );
|
|
|
|
if ( lpTimeouts )
|
|
{
|
|
LPSTR StringPtr;
|
|
DWORD StringLen;
|
|
DWORD Timeout;
|
|
DWORD Count = 0;
|
|
|
|
status = RegQueryValueExA( hKey,
|
|
DNS_QUERY_TIMEOUTS,
|
|
NULL,
|
|
&dwType,
|
|
lpTimeouts,
|
|
&ValueSize );
|
|
|
|
if ( status ||
|
|
dwType != REG_MULTI_SZ )
|
|
{
|
|
FREE_HEAP( lpTimeouts );
|
|
goto GetQuickQueryTimeouts;
|
|
}
|
|
|
|
StringPtr = lpTimeouts;
|
|
|
|
while ( ( StringLen = strlen( StringPtr ) ) != 0 &&
|
|
Count < DNS_MAX_QUERY_TIMEOUTS )
|
|
{
|
|
Timeout = atoi( StringPtr );
|
|
|
|
if ( Timeout )
|
|
RegistryQueryTimeouts[Count++] = Timeout;
|
|
|
|
StringPtr += (StringLen + 1);
|
|
}
|
|
|
|
RegistryQueryTimeouts[Count] = 0;
|
|
g_QueryTimeouts = RegistryQueryTimeouts;
|
|
FREE_HEAP( lpTimeouts );
|
|
}
|
|
}
|
|
|
|
GetQuickQueryTimeouts:
|
|
|
|
status = RegQueryValueExA( hKey,
|
|
DNS_QUICK_QUERY_TIMEOUTS,
|
|
NULL,
|
|
&dwType,
|
|
NULL,
|
|
&ValueSize );
|
|
|
|
if ( !status )
|
|
{
|
|
if ( ValueSize == 0 )
|
|
{
|
|
goto GetMulticastTimeouts;
|
|
}
|
|
|
|
lpTimeouts = ALLOCATE_HEAP( ValueSize );
|
|
|
|
if ( lpTimeouts )
|
|
{
|
|
LPSTR StringPtr;
|
|
DWORD StringLen;
|
|
DWORD Timeout;
|
|
DWORD Count = 0;
|
|
|
|
status = RegQueryValueExA( hKey,
|
|
DNS_QUICK_QUERY_TIMEOUTS,
|
|
NULL,
|
|
&dwType,
|
|
lpTimeouts,
|
|
&ValueSize );
|
|
|
|
if ( status ||
|
|
dwType != REG_MULTI_SZ )
|
|
{
|
|
FREE_HEAP( lpTimeouts );
|
|
goto GetMulticastTimeouts;
|
|
}
|
|
|
|
StringPtr = lpTimeouts;
|
|
|
|
while ( ( StringLen = strlen( StringPtr ) ) != 0 &&
|
|
Count < DNS_MAX_QUERY_TIMEOUTS )
|
|
{
|
|
Timeout = atoi( StringPtr );
|
|
|
|
if ( Timeout )
|
|
RegistryQuickQueryTimeouts[Count++] = Timeout;
|
|
|
|
StringPtr += (StringLen + 1);
|
|
}
|
|
|
|
RegistryQuickQueryTimeouts[Count] = 0;
|
|
g_QuickQueryTimeouts = RegistryQuickQueryTimeouts;
|
|
FREE_HEAP( lpTimeouts );
|
|
}
|
|
}
|
|
|
|
GetMulticastTimeouts:
|
|
|
|
status = RegQueryValueExA( hKey,
|
|
DNS_MULTICAST_QUERY_TIMEOUTS,
|
|
NULL,
|
|
&dwType,
|
|
NULL,
|
|
&ValueSize );
|
|
|
|
if ( !status )
|
|
{
|
|
if ( ValueSize == 0 )
|
|
{
|
|
RegCloseKey( hKey );
|
|
return;
|
|
}
|
|
|
|
lpTimeouts = ALLOCATE_HEAP( ValueSize );
|
|
|
|
if ( lpTimeouts )
|
|
{
|
|
LPSTR StringPtr;
|
|
DWORD StringLen;
|
|
DWORD Timeout;
|
|
DWORD Count = 0;
|
|
|
|
status = RegQueryValueExA( hKey,
|
|
DNS_MULTICAST_QUERY_TIMEOUTS,
|
|
NULL,
|
|
&dwType,
|
|
lpTimeouts,
|
|
&ValueSize );
|
|
|
|
if ( status ||
|
|
dwType != REG_MULTI_SZ )
|
|
{
|
|
FREE_HEAP( lpTimeouts );
|
|
RegCloseKey( hKey );
|
|
return;
|
|
}
|
|
|
|
StringPtr = lpTimeouts;
|
|
|
|
while ( ( StringLen = strlen( StringPtr ) ) != 0 &&
|
|
Count < DNS_MAX_QUERY_TIMEOUTS )
|
|
{
|
|
Timeout = atoi( StringPtr );
|
|
|
|
if ( Timeout )
|
|
RegistryMulticastQueryTimeouts[Count++] = Timeout;
|
|
|
|
StringPtr += (StringLen + 1);
|
|
}
|
|
|
|
RegistryMulticastQueryTimeouts[Count] = 0;
|
|
g_MulticastQueryTimeouts = RegistryMulticastQueryTimeouts;
|
|
FREE_HEAP( lpTimeouts );
|
|
}
|
|
}
|
|
|
|
RegCloseKey( hKey );
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// OPT selection
|
|
//
|
|
// These routines track DNS server OPT awareness.
|
|
//
|
|
// The paradigm here is to default to sending OPTs, then track
|
|
// OPT non-awareness.
|
|
//
|
|
// DCR: RPC over OPT config info
|
|
// - either two lists (local and from-resolver in process)
|
|
// OR
|
|
// - RPC back OPT failures to resolver
|
|
// OR
|
|
// - flag network info blobs to RPC back to resolver
|
|
//
|
|
// security wise, prefer not to get info back
|
|
//
|
|
//
|
|
// DCR: OPT info in network info
|
|
// - then don't have to traverse locks
|
|
// - save is identical to current
|
|
// - could exclude OPT on any non-cache sends to
|
|
// handle problem of not saving OPT failures
|
|
//
|
|
|
|
//
|
|
// Global IP array of OPT-failed DNS servers
|
|
//
|
|
|
|
PADDR_ARRAY g_OptFailServerList = NULL;
|
|
|
|
// Allocation size for OptFail IP array.
|
|
// Ten servers nicely covers the typical case.
|
|
|
|
#define OPT_FAIL_LIST_SIZE 10
|
|
|
|
|
|
//
|
|
// Use global lock for OPT list
|
|
//
|
|
|
|
#define LOCK_OPT_LIST() LOCK_GENERAL()
|
|
#define UNLOCK_OPT_LIST() UNLOCK_GENERAL()
|
|
|
|
|
|
|
|
BOOL
|
|
Send_IsServerOptExclude(
|
|
IN PDNS_ADDR pAddr
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determine if particular server is not OPT aware.
|
|
|
|
Arguments:
|
|
|
|
pAddr -- IP address of DNS server
|
|
|
|
Return Value:
|
|
|
|
TRUE if server should NOT get OPT send.
|
|
FALSE if server should can send OPT
|
|
|
|
--*/
|
|
{
|
|
BOOL retval;
|
|
|
|
//
|
|
// zero IP -- meaning TCP connect to unknown
|
|
// => must exclude OPT to allow success, otherwise
|
|
// we can't retry non-OPT
|
|
//
|
|
|
|
if ( !pAddr || DnsAddr_IsEmpty(pAddr) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// no exclusions?
|
|
// - doing outside lock for perf once we get to
|
|
// the "fully-deployed" case
|
|
//
|
|
|
|
if ( !g_OptFailServerList )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// see if IP is in OPT list
|
|
// - only if found do we exclude
|
|
//
|
|
|
|
LOCK_OPT_LIST();
|
|
|
|
retval = FALSE;
|
|
|
|
if ( g_OptFailServerList
|
|
&&
|
|
AddrArray_ContainsAddr(
|
|
g_OptFailServerList,
|
|
pAddr ) )
|
|
{
|
|
retval = TRUE;
|
|
}
|
|
UNLOCK_OPT_LIST();
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
Send_SetServerOptExclude(
|
|
IN PDNS_ADDR pAddr
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set server for OPT exclusion.
|
|
|
|
Arguments:
|
|
|
|
IpAddress -- IP address of DNS server that failed OPT
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// screen zero IP (TCP connect to unknown IP)
|
|
//
|
|
|
|
if ( !pAddr || DnsAddr_IsEmpty(pAddr) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// put IP in OPT-fail list
|
|
// - create if doesn't exist
|
|
// - resize if won't fit
|
|
// note: add failure means "won't fit"
|
|
//
|
|
// note: only safe to reset global if allocation successful
|
|
// note: only one retry to protect alloc failure looping
|
|
//
|
|
|
|
LOCK_OPT_LIST();
|
|
|
|
if ( ! g_OptFailServerList
|
|
||
|
|
! AddrArray_AddAddr(
|
|
g_OptFailServerList,
|
|
pAddr
|
|
) )
|
|
{
|
|
PADDR_ARRAY pnewList;
|
|
|
|
pnewList = DnsAddrArray_CopyAndExpand(
|
|
g_OptFailServerList,
|
|
OPT_FAIL_LIST_SIZE,
|
|
TRUE // free current
|
|
);
|
|
if ( pnewList )
|
|
{
|
|
g_OptFailServerList = pnewList;
|
|
|
|
AddrArray_AddAddr(
|
|
g_OptFailServerList,
|
|
pAddr
|
|
);
|
|
}
|
|
}
|
|
|
|
UNLOCK_OPT_LIST();
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
Send_CleanupOptList(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dump OPT list for process detach.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
LOCK_OPT_LIST();
|
|
|
|
DnsAddrArray_Free( g_OptFailServerList );
|
|
g_OptFailServerList = NULL;
|
|
|
|
UNLOCK_OPT_LIST();
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Main send routine
|
|
//
|
|
|
|
DNS_STATUS
|
|
Send_AndRecv(
|
|
IN OUT PSEND_BLOB pBlob
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Send message, receive response.
|
|
|
|
Arguments:
|
|
|
|
pBlob -- send blob
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
Error code on failure.
|
|
|
|
--*/
|
|
{
|
|
PDNS_NETINFO pnetInfo;
|
|
PIP4_ARRAY pserv4Array;
|
|
PADDR_ARRAY pservArray;
|
|
PADDR_ARRAY pservArrayIn = NULL;
|
|
PADDR_ARRAY pservArrayAlloc = NULL;
|
|
DWORD flags;
|
|
PDNS_MSG_BUF psendMsg;
|
|
PDNS_MSG_BUF precvMsg;
|
|
PDNS_MSG_BUF psavedUdpResponse = NULL;
|
|
DNS_STATUS statusFromUdp = ERROR_SUCCESS;
|
|
DNS_STATUS status = ERROR_TIMEOUT;
|
|
DNS_STATUS parseStatus;
|
|
BOOL ftcp;
|
|
ADDR_ARRAY tempArray;
|
|
|
|
//
|
|
// unpack
|
|
//
|
|
|
|
pnetInfo = pBlob->pNetInfo;
|
|
pservArray = pBlob->pServerList;
|
|
pserv4Array = pBlob->pServ4List;
|
|
flags = pBlob->Flags;
|
|
psendMsg = pBlob->pSendMsg;
|
|
precvMsg = pBlob->Results.pMessage;
|
|
|
|
pservArrayIn = pservArray;
|
|
|
|
|
|
DNSDBG( QUERY, (
|
|
"Send_AndRecv( %p )\n",
|
|
pBlob ));
|
|
|
|
IF_DNSDBG( QUERY )
|
|
{
|
|
DnsDbg_SendBlob(
|
|
"Send_AndRecv()",
|
|
pBlob );
|
|
}
|
|
|
|
//
|
|
// response buf passed in?
|
|
// if not allocate one -- big enough for TCP
|
|
//
|
|
|
|
if ( !precvMsg )
|
|
{
|
|
precvMsg = pBlob->pRecvMsgBuf;
|
|
if ( !precvMsg )
|
|
{
|
|
precvMsg = Dns_AllocateMsgBuf( DNS_TCP_DEFAULT_PACKET_LENGTH );
|
|
if ( !precvMsg )
|
|
{
|
|
status = DNS_ERROR_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// send packet and get response
|
|
// - try UDP first unless TCP only
|
|
//
|
|
|
|
ftcp = ( flags & DNS_QUERY_USE_TCP_ONLY ) ||
|
|
( DNS_MESSAGE_CURRENT_OFFSET(psendMsg) >= DNS_RFC_MAX_UDP_PACKET_LENGTH );
|
|
|
|
if ( !ftcp )
|
|
{
|
|
if ( flags & DNS_QUERY_MULTICAST_ONLY )
|
|
{
|
|
//
|
|
// If the multicast query fails, then ERROR_TIMEOUT will
|
|
// be returned
|
|
//
|
|
goto DoMulticast;
|
|
}
|
|
|
|
if ( pserv4Array && !pservArray )
|
|
{
|
|
pservArrayAlloc = DnsAddrArray_CreateFromIp4Array( pserv4Array );
|
|
pservArray = pservArrayAlloc;
|
|
}
|
|
|
|
status = Send_AndRecvUdpWithParam(
|
|
psendMsg,
|
|
precvMsg,
|
|
flags,
|
|
pservArray,
|
|
pnetInfo );
|
|
|
|
statusFromUdp = status;
|
|
|
|
if ( status != ERROR_SUCCESS &&
|
|
status != DNS_ERROR_RCODE_NAME_ERROR &&
|
|
status != DNS_INFO_NO_RECORDS )
|
|
{
|
|
//
|
|
// DCR: this multicast ON_NAME_ERROR test is bogus
|
|
// this isn't NAME_ERROR this is pretty much any error
|
|
//
|
|
|
|
if ( pnetInfo &&
|
|
pnetInfo->InfoFlags & NINFO_FLAG_MULTICAST_ON_NAME_ERROR )
|
|
{
|
|
goto DoMulticast;
|
|
}
|
|
else
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
// if truncated response switch to TCP
|
|
|
|
if ( precvMsg->MessageHead.Truncation &&
|
|
! (flags & DNS_QUERY_ACCEPT_PARTIAL_UDP) )
|
|
{
|
|
ftcp = TRUE;
|
|
pservArrayIn = pservArray;
|
|
pservArray = &tempArray;
|
|
pBlob->pServerList = pservArray;
|
|
|
|
DnsAddrArray_InitSingleWithSockaddr(
|
|
pservArray,
|
|
&precvMsg->RemoteAddress.Sockaddr );
|
|
|
|
psavedUdpResponse = precvMsg;
|
|
precvMsg = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// TCP send
|
|
// - for TCP queries
|
|
// - or truncation on UDP unless accepting partial response
|
|
//
|
|
// DCR_FIX: this precvMsg Free is bad
|
|
// if message was passed in we shouldn't have it, we should
|
|
// just do our own thing and ignore this recv buffer somehow
|
|
// ideally that buffer action is at much higher level
|
|
//
|
|
|
|
if ( ftcp )
|
|
{
|
|
if ( precvMsg &&
|
|
precvMsg->BufferLength < DNS_TCP_DEFAULT_PACKET_LENGTH )
|
|
{
|
|
if ( precvMsg != pBlob->pRecvMsgBuf )
|
|
{
|
|
FREE_HEAP( precvMsg );
|
|
}
|
|
precvMsg = NULL;
|
|
}
|
|
if ( !precvMsg )
|
|
{
|
|
precvMsg = Dns_AllocateMsgBuf( DNS_TCP_DEFAULT_PACKET_LENGTH );
|
|
if ( !precvMsg )
|
|
{
|
|
status = DNS_ERROR_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
psendMsg->fTcp = TRUE;
|
|
precvMsg->fTcp = TRUE;
|
|
#if 0
|
|
if ( flags & DNS_QUERY_SOCKET_KEEPALIVE )
|
|
{
|
|
precvMsg->fSocketKeepalive = TRUE;
|
|
}
|
|
#endif
|
|
pBlob->Results.pMessage = precvMsg;
|
|
|
|
status = Send_AndRecvTcp( pBlob );
|
|
|
|
//
|
|
// if recursing following truncated UDP query, then
|
|
// must make sure we actually have better data
|
|
// - if successful, but RCODE is different and bad
|
|
// => use UDP response
|
|
// - if failed TCP => use UDP
|
|
// - successful with good RCODE => parse TCP response
|
|
//
|
|
|
|
if ( psavedUdpResponse )
|
|
{
|
|
if ( status == ERROR_SUCCESS )
|
|
{
|
|
DWORD rcode = precvMsg->MessageHead.ResponseCode;
|
|
|
|
if ( rcode == ERROR_SUCCESS ||
|
|
rcode == psavedUdpResponse->MessageHead.ResponseCode ||
|
|
( rcode != DNS_RCODE_SERVER_FAILURE &&
|
|
rcode != DNS_RCODE_FORMAT_ERROR &&
|
|
rcode != DNS_RCODE_REFUSED ) )
|
|
{
|
|
goto Parse;
|
|
}
|
|
}
|
|
|
|
// TCP failed or returned bum error code
|
|
|
|
FREE_HEAP( precvMsg );
|
|
precvMsg = psavedUdpResponse;
|
|
psavedUdpResponse = NULL;
|
|
}
|
|
|
|
// direct TCP query
|
|
// - cleanup if failed
|
|
|
|
else if ( status != ERROR_SUCCESS )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// DCR: this multicast test is bogus (too wide open)
|
|
// essentially ANY error sends us on to multicast
|
|
// even INFO_NO_RECORDS
|
|
//
|
|
// multicast test should be intelligent
|
|
// - adpater with no DNS servers, or NO_RESPONSE
|
|
// from any DNS server
|
|
// multicast test also has to be skipped for update
|
|
//
|
|
|
|
if ( status == ERROR_SUCCESS )
|
|
{
|
|
DWORD rcode = precvMsg->MessageHead.ResponseCode;
|
|
|
|
if ( rcode == ERROR_SUCCESS ||
|
|
( rcode != DNS_RCODE_SERVER_FAILURE &&
|
|
rcode != DNS_RCODE_FORMAT_ERROR &&
|
|
rcode != DNS_RCODE_REFUSED ) )
|
|
{
|
|
goto Parse;
|
|
}
|
|
}
|
|
|
|
//
|
|
// multicast?
|
|
//
|
|
|
|
DoMulticast:
|
|
|
|
if ( ( pnetInfo &&
|
|
pnetInfo->InfoFlags & NINFO_FLAG_ALLOW_MULTICAST )
|
|
||
|
|
( ( flags & DNS_QUERY_MULTICAST_ONLY ) &&
|
|
! pnetInfo ) )
|
|
{
|
|
if ( !psendMsg ||
|
|
( psendMsg &&
|
|
( psendMsg->MessageHead.Opcode == DNS_OPCODE_UPDATE ) ) )
|
|
{
|
|
if ( statusFromUdp )
|
|
{
|
|
status = statusFromUdp;
|
|
}
|
|
else
|
|
{
|
|
status = DNS_ERROR_NO_DNS_SERVERS;
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
status = Send_AndRecvMulticast(
|
|
psendMsg,
|
|
precvMsg,
|
|
pnetInfo );
|
|
|
|
if ( status != ERROR_SUCCESS &&
|
|
status != DNS_ERROR_RCODE_NAME_ERROR &&
|
|
status != DNS_INFO_NO_RECORDS )
|
|
{
|
|
if ( statusFromUdp )
|
|
{
|
|
status = statusFromUdp;
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// parse response (if desired)
|
|
//
|
|
// DCR: this is busted, should have one parsing function to handle
|
|
// taking fSaveRecords as param
|
|
// specifically need to tease out NO_RECORDS response even if
|
|
// not parsing records
|
|
//
|
|
|
|
Parse:
|
|
|
|
if ( pBlob->fSaveRecords )
|
|
{
|
|
parseStatus = Dns_ExtractRecordsFromMessage(
|
|
precvMsg,
|
|
//(flags & DNSQUERY_UNICODE_OUT),
|
|
TRUE, // unicode results
|
|
& pBlob->Results.pRecords );
|
|
|
|
if ( !(flags & DNS_QUERY_DONT_RESET_TTL_VALUES ) )
|
|
{
|
|
Dns_NormalizeAllRecordTtls( pBlob->Results.pRecords );
|
|
}
|
|
}
|
|
|
|
// not parsing -- just return RCODE as status
|
|
|
|
else
|
|
{
|
|
parseStatus = Dns_MapRcodeToStatus( precvMsg->MessageHead.ResponseCode );
|
|
}
|
|
|
|
//
|
|
// get "best" status
|
|
// - no-records response beats NAME_ERROR (or other error)
|
|
// dump bogus records from error response
|
|
//
|
|
// DCR: multi-adapter NXDOMAIN\no-records response broken
|
|
// note, here we'd give back a packet with NAME_ERROR
|
|
// or another error
|
|
//
|
|
|
|
if ( status != parseStatus )
|
|
{
|
|
// save previous NO_RECORDS response, from underlying query
|
|
// this trumps other errors (FORMERR, SERVFAIL, NXDOMAIN);
|
|
//
|
|
// note, that parsed message shouldn't be HIGHER level RCODE
|
|
// as these should beat out NO_RECORDS in original parsing
|
|
|
|
if ( status == DNS_INFO_NO_RECORDS &&
|
|
parseStatus != ERROR_SUCCESS )
|
|
{
|
|
ASSERT( precvMsg->MessageHead.ResponseCode <= DNS_RCODE_NAME_ERROR );
|
|
|
|
if ( pBlob->Results.pRecords )
|
|
{
|
|
Dns_RecordListFree( pBlob->Results.pRecords );
|
|
pBlob->Results.pRecords = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = parseStatus;
|
|
}
|
|
}
|
|
|
|
|
|
Cleanup:
|
|
|
|
// cleanup recv buffer?
|
|
//
|
|
// DCR: should have more definitive "have-response" test
|
|
|
|
if ( pBlob->fSaveResponse &&
|
|
(status == ERROR_SUCCESS || Dns_IsStatusRcode(status)) )
|
|
{
|
|
pBlob->Results.pMessage = precvMsg;
|
|
}
|
|
else
|
|
{
|
|
if ( precvMsg != pBlob->pRecvMsgBuf )
|
|
{
|
|
FREE_HEAP( precvMsg );
|
|
}
|
|
pBlob->Results.pMessage = NULL;
|
|
}
|
|
if ( psavedUdpResponse )
|
|
{
|
|
FREE_HEAP( psavedUdpResponse );
|
|
}
|
|
|
|
// set response status
|
|
|
|
pBlob->Results.Status = status;
|
|
|
|
// replace original server array, if created
|
|
// new in TCP fallover
|
|
|
|
pBlob->pServerList = pservArrayIn;
|
|
|
|
if ( pservArrayAlloc )
|
|
{
|
|
DnsAddrArray_Free( pservArrayAlloc );
|
|
}
|
|
|
|
DNSDBG( RECV, (
|
|
"Leaving Send_AndRecv(), status = %s (%d)\n",
|
|
Dns_StatusString(status),
|
|
status ));
|
|
|
|
return( status );
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// Obsolete exported crap
|
|
//
|
|
// May be here for ICS
|
|
//
|
|
|
|
DNS_STATUS
|
|
Dns_SendEx(
|
|
IN OUT PDNS_MSG_BUF pMsg,
|
|
IN IP4_ADDRESS IpAddr, OPTIONAL
|
|
IN BOOL fNoOpt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Send a DNS packet.
|
|
|
|
This is the generic send routine used for ANY send of a DNS message.
|
|
|
|
It assumes nothing about the message type, but does assume:
|
|
- pCurrent points at byte following end of desired data
|
|
- RR count bytes are in HOST byte order
|
|
|
|
Note: EXPORTED function Dns_SendEx(), remove when clear
|
|
now an IP4 shim
|
|
|
|
DCR: Remove Dns_SendEx() from export when ICS fixed
|
|
|
|
Arguments:
|
|
|
|
pMsg - message info for message to send
|
|
|
|
IpAddr - IP to send to; OPTIONAL, required only if UDP
|
|
and message sockaddr not set
|
|
|
|
fNoOpt - TRUE if OPT send is forbidden
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful.
|
|
FALSE on send error.
|
|
|
|
--*/
|
|
{
|
|
DNS_ADDR addr;
|
|
|
|
if ( IpAddr )
|
|
{
|
|
DnsAddr_BuildFromIp4(
|
|
&addr,
|
|
IpAddr,
|
|
0 );
|
|
}
|
|
|
|
return Send_MessagePrivate(
|
|
pMsg,
|
|
IpAddr ? &addr : NULL,
|
|
fNoOpt
|
|
);
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
Dns_InitializeMsgRemoteSockaddr(
|
|
IN OUT PDNS_MSG_BUF pMsg,
|
|
IN IP4_ADDRESS IpAddr
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize remote sockaddr.
|
|
|
|
Note: EXPORTED function -- IP4 shim
|
|
|
|
// DCR: EXPORTED may remove when clean
|
|
|
|
Arguments:
|
|
|
|
pMsg - message to send
|
|
|
|
IpAddr - IP4 address to send to
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
DNS_ADDR addr;
|
|
|
|
DnsAddr_BuildFromIp4(
|
|
&addr,
|
|
IpAddr,
|
|
0 );
|
|
|
|
Send_SetMsgRemoteSockaddr(
|
|
pMsg,
|
|
&addr );
|
|
}
|
|
|
|
|
|
|
|
DNS_STATUS
|
|
Dns_OpenTcpConnectionAndSend(
|
|
IN OUT PDNS_MSG_BUF pMsg,
|
|
IN IP4_ADDRESS IpAddr,
|
|
IN BOOL fBlocking
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Connect via TCP to desired server.
|
|
|
|
EXPORTED function! IP4 shim. Dns_OpenTcpConnectionAndSend() remove when clear
|
|
|
|
// DCR: EXPORTED may remove when clean
|
|
|
|
Arguments:
|
|
|
|
pMsg -- message info to set with connection socket
|
|
|
|
ipServer -- IP of DNS server to connect to
|
|
|
|
fBlocking -- blocking connection
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful.
|
|
FALSE on connect error.
|
|
|
|
--*/
|
|
{
|
|
DNS_ADDR addr;
|
|
|
|
DnsAddr_BuildFromIp4(
|
|
&addr,
|
|
IpAddr,
|
|
0 );
|
|
|
|
return Send_OpenTcpConnectionAndSend(
|
|
pMsg,
|
|
&addr,
|
|
fBlocking );
|
|
}
|
|
|
|
|
|
|
|
DNS_STATUS
|
|
Dns_SendAndRecvUdp(
|
|
IN OUT PDNS_MSG_BUF pMsgSend,
|
|
OUT PDNS_MSG_BUF pMsgRecv,
|
|
IN DWORD dwFlags,
|
|
IN PIP4_ARRAY pServ4List,
|
|
IN OUT PDNS_NETINFO pNetInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends to and waits to recv from remote DNS.
|
|
|
|
EXPORTED function! Dns_SendAndRecvUdp() Kill once clear.
|
|
|
|
Arguments:
|
|
|
|
pMsgSend - message to send
|
|
|
|
ppMsgRecv - and reuse
|
|
|
|
dwFlags -- query flags
|
|
|
|
pServ4List -- list of server to use (IP4); overrides adapter info
|
|
|
|
pNetInfo -- adapter list DNS server info
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful response.
|
|
Error status for "best RCODE" response if rcode.
|
|
ERROR_TIMEOUT on timeout.
|
|
Error status on send\recv failure.
|
|
|
|
--*/
|
|
{
|
|
DNS_STATUS status;
|
|
PADDR_ARRAY parray;
|
|
|
|
//
|
|
// convert 4 to 6
|
|
//
|
|
|
|
parray = DnsAddrArray_CreateFromIp4Array( pServ4List );
|
|
|
|
status = Send_AndRecvUdpWithParam(
|
|
pMsgSend,
|
|
pMsgRecv,
|
|
dwFlags,
|
|
parray,
|
|
pNetInfo );
|
|
|
|
DnsAddrArray_Free( parray );
|
|
|
|
return status;
|
|
}
|
|
|
|
|