Leaked source code of windows server 2003
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.
 
 
 
 
 
 

2077 lines
49 KiB

/*++
Copyright (c) 1997-2002 Microsoft Corporation
Module Name:
faz.c
Abstract:
Domain Name System (DNS) API
Find Authoritative Zone (FAZ) routines
Author:
Jim Gilroy (jamesg) January, 1997
Revision History:
--*/
#include "local.h"
//
// Max number of server's we'll ever bother to extract from packet
// (much more and you're out of UDP packet space anyway)
//
#define MAX_NAME_SERVER_COUNT (20)
//
// Private protos
//
BOOL
IsRootServerAddressIp4(
IN IP4_ADDRESS Ip
);
//
// Private utilities
//
PDNS_NETINFO
buildUpdateNetworkInfoFromFAZ(
IN PWSTR pszZone,
IN PWSTR pszPrimaryDns,
IN PDNS_RECORD pRecord,
IN BOOL fIp4,
IN BOOL fIp6
)
/*++
Routine Description:
Build new DNS server list from record list.
Arguments:
pszZone -- zone name
pszPrimaryDns -- DNS server name
pRecord -- record list from FAZ or other lookup that contain DNS server
host records
fIp4 -- running IP4
fIp6 -- running IP6
Return Value:
ERROR_SUCCESS if successful.
Error code on failure.
--*/
{
CHAR buffer[ MAX_NAME_SERVER_COUNT*sizeof(DNS_ADDR) + sizeof(DNS_ADDR_ARRAY) ];
PDNS_ADDR_ARRAY parray = (PDNS_ADDR_ARRAY)buffer;
BOOL fmatch = FALSE;
WORD wtype;
DNSDBG( TRACE, (
"buildUpdateNetworkInfoFromFAZ( %S )\n",
pszZone ));
//
// DNS hostname unknown, extract from SOA or NS records
//
if ( !pszPrimaryDns || !pRecord )
{
return( NULL );
}
//
// init IP array
//
DnsAddrArray_Init( parray, MAX_NAME_SERVER_COUNT );
//
// find IP addresses of primary DNS server
//
while ( pRecord )
{
// if not A record
// - we're done if already read records, otherwise continue
wtype = pRecord->wType;
if ( wtype != DNS_TYPE_AAAA && wtype != DNS_TYPE_A )
{
if ( parray->AddrCount != 0 )
{
break;
}
fmatch = FALSE;
pRecord = pRecord->pNext;
continue;
}
// if record has name, check it
// otherwise match is the same as previous record
if ( pRecord->pName )
{
fmatch = Dns_NameCompare_W(
pRecord->pName,
pszPrimaryDns );
}
if ( fmatch )
{
if ( wtype == DNS_TYPE_AAAA )
{
if ( fIp6 )
{
DnsAddrArray_AddIp6(
parray,
& pRecord->Data.AAAA.Ip6Address,
0, // no scope
DNSADDR_MATCH_ADDR
);
}
}
else if ( wtype == DNS_TYPE_A )
{
if ( fIp4 &&
!IsRootServerAddressIp4( pRecord->Data.A.IpAddress ) )
{
DnsAddrArray_AddIp4(
parray,
pRecord->Data.A.IpAddress,
DNSADDR_MATCH_ADDR
);
}
}
}
pRecord = pRecord->pNext;
continue;
}
if ( parray->AddrCount == 0 )
{
return( NULL );
}
//
// convert into UPDATE adapter list
//
return NetInfo_CreateForUpdate(
pszZone,
pszPrimaryDns,
parray,
0 );
}
BOOL
ValidateZoneNameForUpdate(
IN PWSTR pszZone
)
/*++
Routine Description:
Check if zone is valid for update.
Arguments:
pszZone -- zone name
Return Value:
TRUE -- zone is valid for update
FALSE -- zone is invalid, should NOT send update to this zone
--*/
{
PWSTR pzoneExclusions = NULL;
PWSTR pch;
PWSTR pnext;
DNS_STATUS status;
DWORD length;
BOOL returnVal = TRUE;
DWORD labelCount;
DNSDBG( TRACE, (
"ValidateZoneNameForUpdate( %S )\n",
pszZone ));
//
// never update the root
//
if ( !pszZone || *pszZone==L'.' )
{
return( FALSE );
}
//
// never update TLD
// - except config override in case someone
// gave themselves a single label domain name
//
if ( g_UpdateTopLevelDomains )
{
return( TRUE );
}
labelCount = Dns_NameLabelCountW( pszZone );
if ( labelCount > 2 )
{
return( TRUE );
}
if ( labelCount < 2 )
{
return( FALSE );
}
//
// screen out
// - "in-addr.arpa"
// - "ip6.arpa"
//
if ( Dns_NameCompare_W(
L"in-addr.arpa",
pszZone ) )
{
return( FALSE );
}
if ( Dns_NameCompare_W(
L"ip6.arpa",
pszZone ) )
{
return( FALSE );
}
return( TRUE );
#if 0
//
// DCR: "update zone allowed" list?
//
// note: this is complicated as need to test
// SECOND label because tough cases are
// "co.uk" -- difficult to test first label
//
//
// read exclusion list from registry
//
status = DnsRegGetValueEx(
NULL, // no session
NULL, // no adapter
NULL, // no adapter name
DnsRegUpdateZoneExclusions,
REGTYPE_UPDATE_ZONE_EXCLUSIONS,
DNSREG_FLAG_DUMP_EMPTY, // dump empty string
(PBYTE *) &pzoneExclusions
);
if ( status != ERROR_SUCCESS ||
!pzoneExclusions )
{
ASSERT( pzoneExclusions == NULL );
return( TRUE );
}
//
// check all exclusion zones
//
pch = pzoneExclusions;
while ( 1 )
{
// check for termination
// or find next string
length = wcslen( pch );
if ( length == 0 )
{
break;
}
pnext = pch + length + 1;
//
// check this string
//
DNSDBG( TRACE, (
"Update zone compare to %S\n",
pch ));
if ( Dns_NameCompare_W(
pch,
pszZone ) )
{
returnVal = FALSE;
break;
}
pch = pnext;
}
// if no match, zone is valid
FREE_HEAP( pzoneExclusions );
return( returnVal );
#endif
}
DNS_STATUS
Faz_Private(
IN PWSTR pszName,
IN DWORD dwFlags,
IN PADDR_ARRAY pServArray,
OUT PDNS_NETINFO * ppNetworkInfo
)
/*++
Routine Description:
Find name of authoritative zone.
Result of FAZ:
- zone name
- primary DNS server name
- primary DNS IP list
Arguments:
pszName -- name to find authoritative zone for
dwFlags -- flags to use for DnsQuery
pServArray -- servers to query, defaults used if NULL
ppNetworkInfo -- ptr to adapter list built for FAZ
Return Value:
ERROR_SUCCESS if successful.
Error code on failure.
--*/
{
DNS_STATUS status;
PDNS_RECORD precord = NULL;
PDNS_RECORD precordPrimary = NULL;
PDNS_RECORD precordSOA = NULL;
PWSTR pszdnsHost = NULL;
PDNS_NETINFO pnetInfo = NULL;
PWSTR pzoneName;
BOOL fip4;
BOOL fip6;
BOOL queriedA = FALSE;
BOOL queriedAAAA = FALSE;
DNSDBG( QUERY, (
"Faz_Private()\n"
"\tname %S\n"
"\tflags %08x\n"
"\tserver list %p\n"
"\tnetinfo addr %p\n",
pszName,
dwFlags,
pServArray,
ppNetworkInfo ));
//
// query until find name with SOA record -- the zone root
//
// note, MUST check that actually get SOA record
// - servers may return referral
// - lame server might return CNAME to name
//
pzoneName = pszName;
while ( pzoneName )
{
if ( precord )
{
Dns_RecordListFree( precord );
precord = NULL;
}
status = Query_Private(
pzoneName,
DNS_TYPE_SOA,
dwFlags |
DNS_QUERY_TREAT_AS_FQDN |
DNS_QUERY_ALLOW_EMPTY_AUTH_RESP,
pServArray,
& precord );
//
// find SOA and possibly primary name A
//
// test for ERROR_SUCCESS, AUTH_EMPTY or NAME_ERROR
// in all cases first record should be SOA
// ERROR_SUCCESS -- answer section
// NAME_ERROR or AUTH_EMPTY -- authority section
// all MAY also have additional record for SOA primary server
//
if ( status == ERROR_SUCCESS ||
status == DNS_INFO_NO_RECORDS ||
status == DNS_ERROR_RCODE_NAME_ERROR )
{
if ( precord && precord->wType == DNS_TYPE_SOA )
{
// received SOA
// devolve name to zone name
DNSDBG( QUERY, (
"FAZ found SOA (section %d) at zone %S\n",
precord->Flags.S.Section,
precord->pName ));
while( pzoneName &&
! Dns_NameCompare_W( pzoneName, precord->pName ) )
{
pzoneName = Dns_GetDomainNameW( pzoneName );
}
precordSOA = precord;
status = ERROR_SUCCESS;
break;
}
// this could be because server no-recurse or
// bum server (BIND) that CNAMEs even when type=SOA
// drop down to devolve name and continue
DNSDBG( ANY, (
"ERROR: response from FAZ query with no SOA records.\n" ));
}
//
// other errors besides
// - name error
// - no records
// indicate terminal problem
//
else
{
DNS_ASSERT( precord == NULL );
goto Cleanup;
}
//
// name error or empty response, continue with next higher domain
//
pzoneName = Dns_GetDomainNameW( pzoneName );
}
//
// if reached root or TLD -- no update
// - note currently returning SERVFAIL because of
// screwy netlogon logic
//
if ( !ValidateZoneNameForUpdate(pzoneName) )
{
//status = DNS_ERROR_INVALID_ZONE_OPERATION;
status = DNS_ERROR_RCODE_SERVER_FAILURE;
//status = DNS_ERROR_RCODE_REFUSED;
goto Cleanup;
}
//
// determine required protocol for update
//
// need to insure we get server address records for protocol that is
// reachable from this client;
//
// more specifically the ideal protocol for the update is a protocol
// that the target DNS server supports and that the client also supports
// ON THE ADAPTER that the server is reached through;
// this is close to being the protocol that the FAZ went over -- but
// if from the cache, we don't have (can't yet get) that info; and that
// is a sufficient but not necessary condition;
//
// furthermore we can't even make the global determination that we don't
// support IP4, because we can always open an IP4 socket (on loopback interface)
//
// solution:
// - IP4 only => trivial done
// - IP6 =>
// - try to build from FAZ, insisting on IP6
// -
//
// note, even here we're assuming
//
//
// get supported protocol info
//
Util_GetActiveProtocols(
& fip6,
& fip4 );
//
// have SOA record
//
// if primary server A record in the packet, use it
// otherwise query for primary DNS A record
//
DNS_ASSERT( precordSOA );
DNS_ASSERT( status == ERROR_SUCCESS );
pszdnsHost = precordSOA->Data.SOA.pNamePrimaryServer;
//
// check additional for primary A\AAAA record
// if found, build network info blob for update
// that points only to update server
//
pnetInfo = buildUpdateNetworkInfoFromFAZ(
pzoneName,
pszdnsHost,
precordSOA,
fip4,
fip6 );
if ( pnetInfo )
{
goto Cleanup;
}
//
// if no primary server A\AAAA record found -- must query
//
DNSDBG( QUERY, (
"WARNING: FAZ making additional query for primary!\n"
"\tPrimary (%S) address record should have been in additional section!\n",
pszdnsHost ));
while ( 1 )
{
WORD wtype;
//
// protocol order IP6 first
//
// this protects us in the IP6 only scenario from getting IP4
// DNS address when in fact we can't use it because of no IP4
// binding; the reverse issue is not much of a problem, the
// autoconfig IP6 address works and we probably won't be
// getting an IP6 address, unless IP6 DNS is desired
//
// DCR: update address\protocol problem
// ultimately should either
// A) verify received address is reachable
// if not direct from IpHlpApi, then find adapter and verify
// we have address of requeried protocol on the adapter
// B) get addresses for any protocols we have, and make sure
// send code uses them
//
if ( fip6 && !queriedAAAA )
{
wtype = DNS_TYPE_AAAA;
queriedAAAA = TRUE;
}
else if ( fip4 && !queriedA )
{
wtype = DNS_TYPE_A;
queriedA = TRUE;
}
else
{
DNSDBG( FAZ, (
"No more protocols for FAZ server address query!\n"
"\tserver = %S\n"
"\tqueried AAAA = %d\n"
"\tqueried A = %d\n",
pszdnsHost,
queriedAAAA,
queriedA ));
status = DNS_ERROR_RCODE_SERVER_FAILURE;
goto Cleanup;
}
status = Query_Private(
pszdnsHost,
wtype,
dwFlags |
DNS_QUERY_TREAT_AS_FQDN |
DNS_QUERY_ALLOW_EMPTY_AUTH_RESP,
pServArray,
& precordPrimary );
if ( status == ERROR_SUCCESS )
{
pnetInfo = buildUpdateNetworkInfoFromFAZ(
pzoneName,
pszdnsHost,
precordPrimary,
fip4,
fip6 );
if ( pnetInfo )
{
goto Cleanup;
}
}
DNSDBG( FAZ, (
"FAZ server address query failed to produce records!\n"
"\tserver = %S\n"
"\ttype = %d\n"
"\tstatus = %d\n"
"\tprecords = %p\n",
pszdnsHost,
wtype,
status,
precordPrimary ));
Dns_RecordListFree( precordPrimary );
precordPrimary = NULL;
continue;
}
Cleanup:
Dns_RecordListFree( precord );
Dns_RecordListFree( precordPrimary );
*ppNetworkInfo = pnetInfo;
DNSDBG( QUERY, (
"Leaving Faz_Private()\n"
"\tstatus = %d\n"
"\tzone = %S\n",
status,
pzoneName ));
return( status );
}
DNS_STATUS
DoQuickFAZ(
OUT PDNS_NETINFO * ppNetworkInfo,
IN PWSTR pszName,
IN PADDR_ARRAY aipServerList OPTIONAL
)
/*++
Routine Description:
FAZ to build network info from FAZ result
Result of FAZ:
- zone name
- primary DNS server name
- primary DNS IP list
This routine is cheap shell around real FAZ to handle
network failure issue, speeding things in net down
condition.
Arguments:
ppNetworkInfo -- addr to recv ptr to network info
pszName -- name for update
aipServerList -- IP array of DNS servers to contact
Return Value:
ERROR_SUCCESS if successful.
ErrorCode on failure
--*/
{
DNS_STATUS status;
DNSDBG( TRACE, ( "DoQuickFAZ( %S )\n", pszName ));
if ( IsKnownNetFailure() )
{
return GetLastError();
}
//
// call real FAZ
// - get results as adapter list struct
//
status = Faz_Private(
pszName,
aipServerList ? DNS_QUERY_BYPASS_CACHE : 0,
aipServerList,
ppNetworkInfo // build adapter list from results
);
//
// if unsuccessful, check if network failure
//
if ( status != ERROR_SUCCESS && !aipServerList )
{
if ( status == WSAEFAULT ||
status == WSAENOTSOCK ||
status == WSAENETDOWN ||
status == WSAENETUNREACH ||
status == WSAEPFNOSUPPORT ||
status == WSAEAFNOSUPPORT ||
status == WSAEHOSTDOWN ||
status == WSAEHOSTUNREACH ||
status == ERROR_TIMEOUT )
{
SetKnownNetFailure( status );
return status;
}
}
return status;
}
//
// Update network info preparation
//
DWORD
GetDnsServerListsForUpdate(
IN OUT PDNS_ADDR_ARRAY * DnsServerListArray,
IN DWORD ArrayLength,
IN DWORD Flags
)
/*++
Routine Description:
Get DNS server lists for update.
One DNS server list returned for each valid updateable adapter.
Arguments:
DnsServerListArray -- array to hold DNS server lists found
ArrayLength -- length of array
Flags -- update flags
Return Value:
Count of DNS server lists found.
--*/
{
PDNS_NETINFO pnetInfo;
DWORD iter1;
DWORD iter2;
DWORD countNets = 0;
// clear server list array
RtlZeroMemory(
DnsServerListArray,
sizeof(PADDR_ARRAY) * ArrayLength );
// build list from current netinfo
pnetInfo = GetNetworkInfo();
if ( ! pnetInfo )
{
return 0;
}
//
// check if update is disabled
// - update dependent on registration state
// - global registration state is OFF
// => then skip
//
if ( (Flags & DNS_UPDATE_SKIP_NO_UPDATE_ADAPTERS)
&&
! g_RegistrationEnabled )
{
return 0;
}
//
// build DNS server list for each updateable adapter
//
for ( iter1 = 0; iter1 < pnetInfo->AdapterCount; iter1++ )
{
PDNS_ADAPTER padapter;
DWORD serverCount;
PDNS_ADDR_ARRAY parray;
if ( iter1 >= ArrayLength )
{
break;
}
padapter = NetInfo_GetAdapterByIndex( pnetInfo, iter1 );
if ( !padapter )
{
continue;
}
// skip if no DNS servers
if ( !padapter->pDnsAddrs )
{
continue;
}
// skip "no-update" adapter?
// - if skip-disabled flag set for this update
// - and no-update flag on adapter
if ( (Flags & DNS_UPDATE_SKIP_NO_UPDATE_ADAPTERS) &&
!(padapter->InfoFlags & AINFO_FLAG_REGISTER_IP_ADDRESSES) )
{
continue;
}
//
// valid update adapter
// - create\save DNS server list
// - bump count of lists
//
// DCR: functionalize adapter DNS list to IP array
//
// DCR_PERF: collapse DNS server lists in netinfo BEFORE allocating them
// in other words bring this function into collapse and fix
//
parray = DnsAddrArray_CreateCopy( padapter->pDnsAddrs );
if ( ! parray )
{
goto Exit;
}
DnsServerListArray[countNets] = parray;
countNets++;
}
Exit:
// free network info
// return count of DNS server lists found
NetInfo_Free( pnetInfo );
return countNets;
}
DWORD
cleanDeadAdaptersFromArray(
IN OUT PADDR_ARRAY * IpArrayArray,
IN OUT PDNS_NETINFO * NetworkInfoArray, OPTIONAL
IN DWORD Count
)
/*++
Routine Description:
Cleanup and remove from array(s) adapter info, when an
adapter is determined to be dead, useless or duplicate for
the update.
Arguments:
IpArrayArray -- array of IP array pointers
NetworkInfoArray -- array of ptrs to network info structs
Count -- length arrays (current adapter count)
Return Value:
New adapter count.
--*/
{
register DWORD iter;
//
// remove useless adapters
// useless means no DNS server list
//
for ( iter = 0; iter < Count; iter++ )
{
PADDR_ARRAY parray = IpArrayArray[iter];
if ( !parray || parray->AddrCount==0 )
{
if ( parray )
{
DnsAddrArray_Free( parray );
IpArrayArray[ iter ] = NULL;
}
Count--;
IpArrayArray[ iter ] = IpArrayArray[ Count ];
IpArrayArray[ Count ] = NULL;
// if have corresponding NetworkInfo array, clean it in same fashion
if ( NetworkInfoArray )
{
if ( NetworkInfoArray[iter] )
{
NetInfo_Free( NetworkInfoArray[iter] );
NetworkInfoArray[iter] = NULL;
}
NetworkInfoArray[ iter ] = NetworkInfoArray[ Count ];
NetworkInfoArray[ Count ] = NULL;
}
}
}
// return count of cleaned list
return Count;
}
DWORD
eliminateDuplicateAdapterFromArrays(
IN OUT PADDR_ARRAY* IpArrayArray,
IN OUT PDNS_NETINFO * NetworkInfoArray,
IN OUT PDNS_RECORD * NsRecordArray,
IN DWORD Count,
IN DWORD DuplicateIndex
)
/*++
Routine Description:
Cleanup and remove from array(s) adapter info, when an
adapter is determined to be dead, useless or duplicate for
the update.
Arguments:
IpArrayArray -- array of IP array pointers
NetworkInfoArray -- array of ptrs to network info structs
NsRecordArray -- array of NS record lists for FAZed zone
Count -- length arrays (current adapter count)
DuplicateIndex -- index of duplicate
Return Value:
New adapter count.
--*/
{
ASSERT( DuplicateIndex < Count );
DNSDBG( TRACE, (
"eliminateDuplicateAdapterFromArrays( dup=%d, max=%d )\n",
DuplicateIndex,
Count ));
//
// free any info on duplicate adapter
//
FREE_HEAP( IpArrayArray[DuplicateIndex] );
NetInfo_Free( NetworkInfoArray[DuplicateIndex] );
Dns_RecordListFree( NsRecordArray[DuplicateIndex] );
//
// copy top entry to this spot
//
Count--;
if ( Count != DuplicateIndex )
{
IpArrayArray[DuplicateIndex] = IpArrayArray[Count];
NetworkInfoArray[DuplicateIndex] = NetworkInfoArray[Count];
NsRecordArray[DuplicateIndex] = NsRecordArray[Count];
}
return Count;
}
DWORD
combineDnsServerListsForTwoAdapters(
IN OUT PADDR_ARRAY* IpArrayArray,
IN DWORD Count,
IN DWORD Index1,
IN DWORD Index2
)
/*++
Routine Description:
Combine DNS server lists for two adapters.
Note, this unions the DNS server lists for the two
adapters and eliminates the higher indexed adapter
from the list.
Arguments:
IpArrayArray -- array of IP array pointers
Count -- length of pointer array
Index1 -- low index to union
Index2 -- high index to union
Return Value:
New adapter count.
--*/
{
PADDR_ARRAY punionArray = NULL;
DNSDBG( TRACE, (
"combineDnsServerListsForTwoAdapters( count=%d, i1=%d, i2=%d )\n",
Count,
Index1,
Index2 ));
ASSERT( Index1 < Count );
ASSERT( Index2 < Count );
ASSERT( Index1 < Index2 );
//
// union the arrays
//
// if unable to allocate union, then just use list in first array
// and dump second
//
DnsAddrArray_Union( IpArrayArray[Index1], IpArrayArray[Index2], &punionArray );
if ( punionArray )
{
FREE_HEAP( IpArrayArray[Index1] );
IpArrayArray[Index1] = punionArray;
}
FREE_HEAP( IpArrayArray[Index2] );
IpArrayArray[Index2] = NULL;
//
// swap deleted entry with last entry in list
//
Count--;
IpArrayArray[Index2] = IpArrayArray[ Count ];
return( Count );
}
DNS_STATUS
CollapseDnsServerListsForUpdate(
IN OUT PADDR_ARRAY* DnsServerListArray,
OUT PDNS_NETINFO * NetworkInfoArray,
IN OUT PDWORD pNetCount,
IN PWSTR pszUpdateName
)
/*++
Routine Description:
Builds update network info blob for each unique name space.
This essentially starts with DNS server list for each adapter
and progressively detects adapters pointing at same name space
until down minimum number of name spaces.
Arguments:
DnsServerListArray -- array of ptrs to DNS server lists for each adapter
NetworkInfoArray -- array to hold pointer to update network info for each
adapter on return contains ptr to network info for each unique name
space update should be sent to
dwNetCount -- starting count of individual adapters networks
pszUpdateName -- name to update
Return Value:
Count of unique name spaces to update.
NetworkInfoArray contains update network info blob for each name space.
--*/
{
PDNS_RECORD NsRecordArray[ UPDATE_ADAPTER_LIMIT ];
PADDR_ARRAY parray1;
DWORD iter1;
DWORD iter2;
DWORD maxCount = *pNetCount;
DNS_STATUS status = DNS_ERROR_NO_DNS_SERVERS;
DNSDBG( TRACE, (
"collapseDnsServerListsForUpdate( count=%d )\n"
"\tupdate name = %S\n",
maxCount,
pszUpdateName ));
//
// clean list of any useless adapters
//
maxCount = cleanDeadAdaptersFromArray(
DnsServerListArray,
NULL, // no network info yet
maxCount );
//
// if only one adapter -- nothing to compare
// - do FAZ to build update network info, if
// successful, we're done
//
if ( maxCount <= 1 )
{
if ( maxCount == 1 )
{
NetworkInfoArray[0] = NULL;
status = DoQuickFAZ(
&NetworkInfoArray[0],
pszUpdateName,
DnsServerListArray[0] );
if ( NetworkInfoArray[0] )
{
goto Done;
}
FREE_HEAP( DnsServerListArray[0] );
maxCount = 0;
goto Done;
}
goto Done;
}
//
// clear NetworkInfo
//
RtlZeroMemory(
NetworkInfoArray,
maxCount * sizeof(PVOID) );
//
// loop through combining adapters with shared DNS servers
//
// as we combine entries we shrink the list
//
for ( iter1 = 0; iter1 < maxCount; iter1++ )
{
parray1 = DnsServerListArray[ iter1 ];
for ( iter2=iter1+1; iter2 < maxCount; iter2++ )
{
if ( AddrArray_IsIntersection(
parray1,
DnsServerListArray[iter2] ) )
{
DNSDBG( UPDATE, (
"collapseDSLFU: whacking intersecting DNS server lists\n"
"\tadapters %d and %d (max =%d)\n",
iter1,
iter2,
maxCount ));
maxCount = combineDnsServerListsForTwoAdapters(
DnsServerListArray,
maxCount,
iter1,
iter2 );
iter2--;
parray1 = DnsServerListArray[ iter1 ];
}
}
}
DNSDBG( TRACE, (
"collapseDSLFU: count after dup server whack = %d\n",
maxCount ));
#if 0
// clean again, in case we missed something
maxCount = cleanDeadAdaptersFromArray(
DnsServerListArray,
NULL, // no network info yet
maxCount );
#endif
//
// FAZ remaining adapters
//
// save result NetworkInfo struct
// => for comparison to determine adapters that share DNS name space
// => to return to caller to do actual update
//
// if FAZ fails this adapter is useless for update -- dead issue
// adapter is removed and replaced by highest array entry
//
for ( iter1 = 0; iter1 < maxCount; iter1++ )
{
status = Faz_Private(
pszUpdateName,
DNS_QUERY_BYPASS_CACHE,
DnsServerListArray[ iter1 ],
& NetworkInfoArray[ iter1 ] );
if ( status != ERROR_SUCCESS )
{
FREE_HEAP( DnsServerListArray[ iter1 ] );
DnsServerListArray[ iter1 ] = NULL;
maxCount--;
DnsServerListArray[ iter1 ] = DnsServerListArray[ maxCount ];
iter1--;
continue;
}
}
#if 0
// clean out failed FAZ entries
maxCount = cleanDeadAdaptersFromArray(
DnsServerListArray,
NetworkInfoArray,
maxCount );
#endif
// if only able to FAZ one adapter -- we're done
// only point here is to skip a bunch of unnecessary
// stuff in the most typical case multi-adapter case
if ( maxCount <= 1 )
{
DNSDBG( TRACE, (
"collapseDSLFU: down to single FAZ adapter\n" ));
goto Done;
}
//
// compare FAZ results to see if adapters are in same name space
//
// do two passes
// - on first pass only compare based on FAZ results, if successful
// we eliminate duplicate adapter
//
// - on second pass, adapters that are still separate are compared;
// if they don't fail FAZ matches (which are retried) then NS queries
// are used to determine if separate nets;
// note that NS query results are saved, so NS query is order N, even
// though we are in N**2 loop
//
RtlZeroMemory(
NsRecordArray,
maxCount * sizeof(PVOID) );
for ( iter1=0; iter1 < maxCount; iter1++ )
{
for ( iter2=iter1+1; iter2 < maxCount; iter2++ )
{
if ( Faz_CompareTwoAdaptersForSameNameSpace(
DnsServerListArray[iter1],
NetworkInfoArray[iter1],
NULL, // no NS list
DnsServerListArray[iter2],
NetworkInfoArray[iter2],
NULL, // no NS list
FALSE // don't use NS queries
) )
{
DNSDBG( UPDATE, (
"collapseDSLFU: whacking same-FAZ adapters\n"
"\tadapters %d and %d (max =%d)\n",
iter1,
iter2,
maxCount ));
eliminateDuplicateAdapterFromArrays(
DnsServerListArray,
NetworkInfoArray,
NsRecordArray,
maxCount,
iter2 );
maxCount--;
iter2--;
}
}
}
DNSDBG( TRACE, (
"collapseDSLFU: count after dup FAZ whack = %d\n",
maxCount ));
// second pass using NS info
// if NS info is created, we save it to avoid requery
for ( iter1=0; iter1 < maxCount; iter1++ )
{
for ( iter2=iter1+1; iter2 < maxCount; iter2++ )
{
if ( Faz_CompareTwoAdaptersForSameNameSpace(
DnsServerListArray[iter1],
NetworkInfoArray[iter1],
& NsRecordArray[iter1],
DnsServerListArray[iter2],
NetworkInfoArray[iter2],
& NsRecordArray[iter2],
TRUE // follow up with NS queries
) )
{
DNSDBG( UPDATE, (
"collapseDSLFU: whacking same-zone-NS adapters\n"
"\tadapters %d and %d (max =%d)\n",
iter1,
iter2,
maxCount ));
eliminateDuplicateAdapterFromArrays(
DnsServerListArray,
NetworkInfoArray,
NsRecordArray,
maxCount,
iter2 );
maxCount--;
iter2--;
}
}
}
//
// kill off any NS records found
//
for ( iter1=0; iter1 < maxCount; iter1++ )
{
Dns_RecordListFree( NsRecordArray[iter1] );
}
Done:
//
// set count of remaining adapters (update DNS server lists)
//
// return status
// - success if have any update adapter
// - on failure bubble up FAZ error
//
DNSDBG( TRACE, (
"Leave CollapseDnsServerListsForUpdate( collapsed count=%d )\n",
maxCount ));
*pNetCount = maxCount;
if ( maxCount > 0 )
{
status = ERROR_SUCCESS;
}
return status;
}
BOOL
WINAPI
Faz_CompareTwoAdaptersForSameNameSpace(
IN PDNS_ADDR_ARRAY pDnsServerList1,
IN PDNS_NETINFO pNetInfo1,
IN OUT PDNS_RECORD * ppNsRecord1,
IN PDNS_ADDR_ARRAY pDnsServerList2,
IN PDNS_NETINFO pNetInfo2,
IN OUT PDNS_RECORD * ppNsRecord2,
IN BOOL bDoNsCheck
)
/*++
Routine Description:
Compare two adapters to see if in same name space for update.
Arguments:
pDnsServerList1 -- IP array of DNS servers for first adapter
pNetInfo1 -- update netinfo for first adapter
ppNsRecord1 -- addr of ptr to NS record list of update zone done on
first adapter; NULL if no NS check required; if
NS check required and *ppNsRecord1 is NULL, NS query
is made and results returned
pDnsServerList2 -- IP array of DNS servers for second adapter
pNetInfo2 -- update netinfo for second adapter
ppNsRecord2 -- addr of ptr to NS record list of update zone done on
second adapter; NULL if no NS check required; if
NS check required and *ppNsRecord2 is NULL, NS query
is made and results returned
bDoNsCheck -- include update-zone NS check compare; if NS overlap then
name spaces assumed to be the same
Return Value:
ERROR_SUCCESS if successful.
Error status on failure.
--*/
{
DNS_STATUS status = NO_ERROR;
BOOL fsame = FALSE;
PDNS_ADAPTER padapter1;
PDNS_ADAPTER padapter2;
PDNS_RECORD pns1 = NULL;
PDNS_RECORD pns2 = NULL;
PDNS_RECORD pnotUsed = NULL;
PWSTR pzoneName;
//
// done if bad params
//
if ( !pDnsServerList1 || !pDnsServerList2 )
{
return FALSE;
}
//
// validity check
// - note: could probably be just ASSERT()
//
if ( ! NetInfo_IsForUpdate(pNetInfo1) ||
! NetInfo_IsForUpdate(pNetInfo2) )
{
ASSERT( FALSE );
return( FALSE );
}
//
// compare FAZ results
//
// first compare zone names
// if FAZ returns different zone names, then clearly
// have disjoint name spaces
//
pzoneName = NetInfo_UpdateZoneName( pNetInfo1 );
if ( ! Dns_NameCompare_W(
pzoneName,
NetInfo_UpdateZoneName( pNetInfo2 ) ) )
{
return FALSE;
}
//
// check if pointing at same server:
// - if have same update DNS server -- have a match
// - if same server name -- have a match
//
padapter1 = NetInfo_GetAdapterByIndex( pNetInfo1, 0 );
padapter2 = NetInfo_GetAdapterByIndex( pNetInfo2, 0 );
if ( DnsAddrArray_IsEqual(
padapter1->pDnsAddrs,
padapter2->pDnsAddrs,
DNSADDR_MATCH_ADDR ) )
{
return TRUE;
}
else
{
fsame = Dns_NameCompare_W(
NetInfo_UpdateServerName( pNetInfo1 ),
NetInfo_UpdateServerName( pNetInfo2 ) );
}
//
// if matched or not doing NS check => then done
//
if ( fsame || !bDoNsCheck )
{
return( fsame );
}
//
// NS check
//
// if not pointing at same server, may be two multimaster primaries
//
// use NS queries to determine if NS lists for same servers are in
// fact a match
//
if ( ppNsRecord1 )
{
pns1 = *ppNsRecord1;
}
if ( !pns1 )
{
status = Query_Private(
pzoneName,
DNS_TYPE_NS,
DNS_QUERY_BYPASS_CACHE,
pDnsServerList1,
&pns1 );
if ( status != ERROR_SUCCESS )
{
goto Done;
}
pnotUsed = DnsRecordSetDetach( pns1 );
if ( pnotUsed )
{
Dns_RecordListFree( pnotUsed );
pnotUsed = NULL;
}
}
if ( ppNsRecord2 )
{
pns2 = *ppNsRecord2;
}
if ( !pns2 )
{
status = Query_Private(
pzoneName,
DNS_TYPE_NS,
DNS_QUERY_BYPASS_CACHE,
pDnsServerList2,
&pns2 );
if ( status != ERROR_SUCCESS )
{
goto Done;
}
pnotUsed = DnsRecordSetDetach( pns2 );
if ( pnotUsed )
{
Dns_RecordListFree( pnotUsed );
pnotUsed = NULL;
}
}
//
// if NS lists the same -- same namespace
//
fsame = Dns_RecordSetCompareForIntersection( pns1, pns2 );
Done:
//
// cleanup or return NS lists
//
// note, purpose of returning is so caller can avoid requerying
// NS if must make compare against multiple other adapters
//
if ( ppNsRecord1 )
{
*ppNsRecord1 = pns1;
}
else
{
Dns_RecordListFree( pns1 );
}
if ( ppNsRecord2 )
{
*ppNsRecord2 = pns2;
}
else
{
Dns_RecordListFree( pns2 );
}
return fsame;
}
BOOL
WINAPI
Faz_AreServerListsInSameNameSpace(
IN PWSTR pszDomainName,
IN PADDR_ARRAY pServerList1,
IN PADDR_ARRAY pServerList2
)
/*++
Routine Description:
Compare two adapters to see if in same name space for update.
Arguments:
pszDomainName -- domain name to update
pServerList1 -- IP array of DNS servers for first adapter
pServerList2 -- IP array of DNS servers for second adapter
Return Value:
TRUE -- if adapters are found to be on same net
FALSE -- otherwise (definitely NOT or unable to determine)
--*/
{
DNS_STATUS status;
BOOL fsame = FALSE;
PDNS_NETINFO pnetInfo1 = NULL;
PDNS_NETINFO pnetInfo2 = NULL;
DNSDBG( TRACE, (
"Faz_AreServerListsInSameNameSpace()\n" ));
// bad param screening
if ( !pServerList1 || !pServerList2 || !pszDomainName )
{
return FALSE;
}
//
// compare DNS server lists
// if any overlap, them effectively in same DNS namespace
//
if ( AddrArray_IsIntersection( pServerList1, pServerList2 ) )
{
return TRUE;
}
//
// if no DNS server overlap, must compare FAZ results
//
// note: FAZ failures interpreted as FALSE response
// required for callers in asyncreg.c
//
status = Faz_Private(
pszDomainName,
DNS_QUERY_BYPASS_CACHE,
pServerList1,
&pnetInfo1 );
if ( status != ERROR_SUCCESS )
{
goto Cleanup;
}
status = Faz_Private(
pszDomainName,
DNS_QUERY_BYPASS_CACHE,
pServerList2,
&pnetInfo2 );
if ( status != ERROR_SUCCESS )
{
goto Cleanup;
}
//
// call the comparison routine
//
fsame = Faz_CompareTwoAdaptersForSameNameSpace(
pServerList1,
pnetInfo1,
NULL, // no NS record list
pServerList2,
pnetInfo2,
NULL, // no NS record list
TRUE // follow up with NS queries
);
Cleanup:
NetInfo_Free( pnetInfo1 );
NetInfo_Free( pnetInfo2 );
return fsame;
}
BOOL
WINAPI
CompareMultiAdapterSOAQueries(
IN PWSTR pszDomainName,
IN PIP4_ARRAY pServerList1,
IN PIP4_ARRAY pServerList2
)
/*++
Routine Description:
Compare two adapters to see if in same name space for update.
Note, IP4 routine called by asyncreg.c code.
The working routine is Faz_CompareServerListsForSameNameSpace().
Arguments:
pszDomainName -- domain name to update
pServerList1 -- IP array of DNS servers for first adapter
pServerList2 -- IP array of DNS servers for second adapter
Return Value:
TRUE -- if adapters are found to be on same net
FALSE -- otherwise (definitely NOT or unable to determine)
--*/
{
PADDR_ARRAY parray1;
PADDR_ARRAY parray2;
BOOL bresult;
DNSDBG( TRACE, (
"CompareMultiAdapterSOAQueries()\n" ));
parray1 = DnsAddrArray_CreateFromIp4Array( pServerList1 );
parray2 = DnsAddrArray_CreateFromIp4Array( pServerList2 );
bresult = Faz_AreServerListsInSameNameSpace(
pszDomainName,
parray1,
parray2 );
DnsAddrArray_Free( parray1 );
DnsAddrArray_Free( parray2 );
return bresult;
}
//
// DCR: IP6 support for FAZ NS list address grab
//
IP4_ADDRESS
FindHostIpAddressInRecordList(
IN PDNS_RECORD pRecordList,
IN PWSTR pszHostName
)
/*++
Routine Description:
Find IP for hostname, if its A record is in list.
NOTE: This code was borrowed from \dns\dnslib\query.c! ;-)
Arguments:
pRecordList - incoming RR set
pszHostName - hostname to find
Return Value:
IP address matching hostname, if A record for hostname found.
Zero if not found.
--*/
{
register PDNS_RECORD prr = pRecordList;
//
// loop through all records until find IP matching hostname
//
while ( prr )
{
if ( prr->wType == DNS_TYPE_A &&
Dns_NameCompare_W(
prr->pName,
pszHostName ) )
{
return( prr->Data.A.IpAddress );
}
prr = prr->pNext;
}
return( 0 );
}
PADDR_ARRAY
GetNameServersListForDomain(
IN PWSTR pDomainName,
IN PADDR_ARRAY pServerList
)
/*++
Routine Description:
Get IPs for all DNS servers for zone.
Arguments:
pDomainName -- zone name
pServerList -- server list to query
Return Value:
IP array of IPs of DNS servers for zone.
NULL if error.
--*/
{
DNS_STATUS status = NO_ERROR;
PDNS_RECORD prrQuery = NULL;
PADDR_ARRAY pnsArray = NULL;
DWORD countAddr = 0;
DNSDBG( TRACE, (
"GetNameServersListForDomain()\n"
"\tdomain name %S\n"
"\tserver list %p\n",
pDomainName,
pServerList ));
status = Query_Private(
pDomainName,
DNS_TYPE_NS,
DNS_QUERY_BYPASS_CACHE,
pServerList,
&prrQuery );
if ( status == NO_ERROR )
{
PDNS_RECORD pTemp = prrQuery;
DWORD dwCount = 0;
while ( pTemp )
{
dwCount++;
pTemp = pTemp->pNext;
}
pnsArray = DnsAddrArray_Create( dwCount );
if ( pnsArray )
{
pTemp = prrQuery;
while ( pTemp )
{
if ( pTemp->wType == DNS_TYPE_NS )
{
IP4_ADDRESS ip = 0;
ip = FindHostIpAddressInRecordList(
pTemp,
pTemp->Data.NS.pNameHost );
if ( !ip )
{
PDNS_RECORD pARecord = NULL;
//
// Query again to get the server's address
//
status = Query_Private(
pTemp->Data.NS.pNameHost,
DNS_TYPE_A,
DNS_QUERY_BYPASS_CACHE,
pServerList,
&pARecord );
if ( status == NO_ERROR &&
pARecord )
{
ip = pARecord->Data.A.IpAddress;
Dns_RecordListFree( pARecord );
}
}
if ( ip )
{
DnsAddrArray_AddIp4(
pnsArray,
ip,
TRUE );
}
}
pTemp = pTemp->pNext;
}
}
}
if ( prrQuery )
{
Dns_RecordListFree( prrQuery );
}
return pnsArray;
}
//
// Root server screening
//
// Root servers as of .net 2003 ship:
// 198.41.0.4
// 128.9.0.107
// 192.33.4.12
// 128.8.10.90
// 192.203.230.10
// 192.5.5.241
// 192.112.36.4
// 128.63.2.53
// 192.36.148.17
// 192.58.128.30
// 193.0.14.129
// 198.32.64.12
// 202.12.27.33
//
IP4_ADDRESS g_RootServers4[] =
{
0x040029c6,
0x6b000980,
0x0c0421c0,
0x5a0a0880,
0x0ae6cbc0,
0xf10505c0,
0x042470c0,
0x35023f80,
0x119424c0,
0x1e803ac0,
0x810e00c1,
0x0c4020c6,
0x211b0cca,
0
};
BOOL
IsRootServerAddressIp4(
IN IP4_ADDRESS Ip
)
/*++
Routine Description:
Determine if address is root server address.
Arguments:
Ip -- IP to screen
Return Value:
TRUE if root server address.
FALSE otherwise.
--*/
{
DWORD iter;
IP4_ADDRESS rootIp;
//
// check against all root servers
//
iter = 0;
while ( (rootIp = g_RootServers4[iter++]) != 0 )
{
if ( rootIp == Ip )
{
return TRUE;
}
}
return FALSE;
}
//
// End of faz.c
//