WindowsXP-SP1/ds/dns/dnsapi/faz.c

2130 lines
51 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1997-2001 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)
#if 1
//
// Original FAZ routines -- now obsolete
//
PDNS_NETINFO
Dns_BuildUpdateNetworkInfoFromFAZ(
IN LPSTR pszZone,
IN LPSTR pszPrimaryDns,
IN PDNS_RECORD pRecord
)
/*++
Routine Description:
Build new DNS network info from record list.
Arguments:
paipServers -- addr to receive ptr to zone's DNS server list
pRR - incoming RR set
Return Value:
ERROR_SUCCESS if successful.
Error code on failure.
--*/
{
INT countIp = 0;
IP_ADDRESS arrayIp[ MAX_NAME_SERVER_COUNT + 1];
PIP_ARRAY piparray = (PIP_ARRAY)arrayIp;
BOOL fmatch = FALSE;
DNS_ASSERT( &piparray->AddrCount == &arrayIp[0] );
//
// DNS hostname unknown, extract from SOA or NS records
//
if ( !pszPrimaryDns )
{
return( NULL );
}
//
// find IP addresses of primary DNS server
// - skip first entry, so array can mimic PIP_ARRAY structure
//
while ( pRecord )
{
// if not A record
// - we're done if already read records, otherwise continue
if ( pRecord->wType != DNS_TYPE_A )
{
if ( countIp != 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_UTF8(
pRecord->pName,
pszPrimaryDns );
}
if ( fmatch )
{
arrayIp[ ++countIp ] = pRecord->Data.A.IpAddress;
}
pRecord = pRecord->pNext;
continue;
}
if ( countIp == 0 )
{
return( NULL );
}
piparray->AddrCount = countIp;
//
// convert into UPDATE adapter list
//
return NetInfo_CreateForUpdate(
pszZone,
pszPrimaryDns,
piparray,
0 );
}
DNS_STATUS
Dns_FindAuthoritativeZoneLib(
IN PDNS_NAME pszName,
IN DWORD dwFlags,
IN PIP_ARRAY aipQueryServers,
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
Unicode string if dwFlags has DNSQUERY_UNICODE_NAME set.
ANSI string otherwise.
dwFlags -- flags to use for DnsQuery
aipQueryServers -- servers to query, defaults used if NULL
ppNetworkInfo -- ptr to network info built for FAZ
Return Value:
ERROR_SUCCESS if successful.
Error code on failure.
--*/
{
DNS_STATUS status;
PDNS_RECORD precord = NULL;
PDNS_RECORD precordPrimary = NULL;
LPSTR pszdnsHost = NULL;
PDNS_NETINFO pNetworkInfo = NULL;
DNSDBG( QUERY, (
"Dns_FindAuthoritativeZoneLib()\n"
"\tname %s\n"
"\tflags %08x\n",
pszName,
dwFlags ));
//
// 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
//
while ( pszName )
{
if ( precord )
{
Dns_RecordListFree( precord );
precord = NULL;
}
status = QueryDirectEx(
NULL, // no return message buffer
& precord,
NULL, // no header
0, // no header counts
(LPSTR) pszName,
DNS_TYPE_SOA,
NULL, // no input records
dwFlags,
aipQueryServers,
NULL // no network info
);
if ( status == ERROR_SUCCESS ||
status == DNS_ERROR_RCODE_NAME_ERROR ||
status == DNS_INFO_NO_RECORDS )
{
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( pszName &&
! Dns_NameCompare_UTF8( pszName, precord->pName ) )
{
pszName = Dns_GetDomainName( pszName );
}
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
//
pszName = Dns_GetDomainName( pszName );
}
//
// if reached root, we're dead
//
if ( !pszName )
{
if ( status == ERROR_SUCCESS )
{
DNS_ASSERT( FALSE );
status = DNS_ERROR_RCODE_SERVER_FAILURE;
}
goto Cleanup;
}
//
// have SOA record
//
// if primary server A record in the packet, use it
// otherwise query for primary DNS A record
//
DNS_ASSERT( precord->wType == DNS_TYPE_SOA );
DNS_ASSERT( status == ERROR_SUCCESS );
pszdnsHost = (LPSTR) precord->Data.SOA.pNamePrimaryServer;
pNetworkInfo = Dns_BuildUpdateNetworkInfoFromFAZ(
pszName,
pszdnsHost,
precord );
if ( !pNetworkInfo )
{
status = QueryDirectEx(
NULL, // no return message buffer
& precordPrimary,
NULL, // no header
0, // no header counts
pszdnsHost,
DNS_TYPE_A,
NULL, // no input records
dwFlags,
aipQueryServers,
NULL // no network info
);
if ( status != ERROR_SUCCESS || precordPrimary == NULL )
{
DNS_PRINT(( "ERROR: no response to primary A query\n" ));
TEST_ASSERT( FALSE );
status = DNS_ERROR_RCODE_SERVER_FAILURE;
goto Cleanup;
}
pNetworkInfo = Dns_BuildUpdateNetworkInfoFromFAZ(
pszName,
pszdnsHost,
precordPrimary );
if ( !pNetworkInfo )
{
status = DNS_ERROR_RCODE_SERVER_FAILURE;
}
}
Cleanup:
if ( precord )
{
Dns_RecordListFree( precord );
}
if ( precordPrimary )
{
Dns_RecordListFree( precordPrimary );
}
*ppNetworkInfo = pNetworkInfo;
IF_DNSDBG( QUERY )
{
DNS_PRINT((
"Leaving Dns_FindAuthoritativeZoneLib()\n"
"\tstatus = %d\n"
"\tzone = %s\n",
status,
pszName ));
}
return( status );
}
#endif
//
// Private utilities
//
PDNS_NETINFO
buildUpdateNetworkInfoFromFAZ(
IN LPSTR pszZone,
IN LPSTR pszPrimaryDns,
IN PDNS_RECORD pRecord
)
/*++
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
Return Value:
ERROR_SUCCESS if successful.
Error code on failure.
--*/
{
INT countIp = 0;
IP_ADDRESS arrayIp[ MAX_NAME_SERVER_COUNT + 1];
PIP_ARRAY piparray = (PIP_ARRAY)arrayIp;
BOOL fmatch = FALSE;
DNS_ASSERT( &piparray->AddrCount == &arrayIp[0] );
DNSDBG( TRACE, (
"buildUpdateNetworkInfoFromFAZ( %s )\n",
pszZone ));
//
// DNS hostname unknown, extract from SOA or NS records
//
if ( !pszPrimaryDns )
{
return( NULL );
}
//
// find IP addresses of primary DNS server
// - skip first entry, so array can mimic PIP_ARRAY structure
//
while ( pRecord )
{
// if not A record
// - we're done if already read records, otherwise continue
if ( pRecord->wType != DNS_TYPE_A )
{
if ( countIp != 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_UTF8(
pRecord->pName,
pszPrimaryDns );
}
if ( fmatch &&
countIp < MAX_NAME_SERVER_COUNT )
{
arrayIp[ ++countIp ] = pRecord->Data.A.IpAddress;
}
pRecord = pRecord->pNext;
continue;
}
if ( countIp == 0 )
{
return( NULL );
}
piparray->AddrCount = countIp;
//
// convert into UPDATE adapter list
//
return NetInfo_CreateForUpdate(
pszZone,
pszPrimaryDns,
piparray,
0 );
}
BOOL
ValidateZoneNameForUpdate(
IN PSTR 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
--*/
{
PSTR pzoneExclusions = NULL;
PSTR pch;
PSTR pnext;
DNS_STATUS status;
DWORD length;
BOOL returnVal = TRUE;
DWORD labelCount;
DNSDBG( TRACE, (
"ValidateZoneNameForUpdate( %s )\n",
pszZone ));
//
// never update the root
//
if ( !pszZone || *pszZone=='.' )
{
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_NameLabelCount( pszZone );
if ( labelCount > 2 )
{
return( TRUE );
}
if ( labelCount < 2 )
{
return( FALSE );
}
//
// screen out
// - "in-addr.arpa"
// - "ip6.int"
//
if ( Dns_NameCompare_UTF8(
"in-addr.arpa",
pszZone ) )
{
return( FALSE );
}
if ( Dns_NameCompare_UTF8(
"ip6.int",
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
//
// note: UTF8 compare mediocre perfwise
// if critically would keep exclusions in
// cannonical form so don't go to unicode
// for case-insensitive compare
//
pch = pzoneExclusions;
while ( 1 )
{
// check for termination
// or find next string
length = strlen( pch );
if ( length == 0 )
{
break;
}
pnext = pch + length + 1;
//
// check this string
//
DNSDBG( TRACE, (
"Update zone compare to %s\n",
pch ));
if ( Dns_NameCompare_UTF8(
pch,
pszZone ) )
{
returnVal = FALSE;
break;
}
pch = pnext;
}
// if no match, zone is valid
FREE_HEAP( pzoneExclusions );
return( returnVal );
#endif
}
DNS_STATUS
DnsFindAuthoritativeZone(
IN PDNS_NAME pszName,
IN DWORD dwFlags,
IN PIP_ARRAY aipQueryServers,
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
aipQueryServers -- 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;
LPSTR pszdnsHost = NULL;
PDNS_NETINFO pnetInfo = NULL;
PSTR pzoneName;
DNSDBG( QUERY, (
"DnsFindAuthoritativeZone()\n"
"\tname %s\n"
"\tflags %08x\n",
pszName,
dwFlags ));
//
// 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 = DnsQuery_UTF8(
(LPSTR) pzoneName,
DNS_TYPE_SOA,
dwFlags |
DNS_QUERY_TREAT_AS_FQDN |
DNS_QUERY_ALLOW_EMPTY_AUTH_RESP,
aipQueryServers,
& precord,
NULL );
//
// 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_UTF8( pzoneName, precord->pName ) )
{
pzoneName = DnsGetDomainName( 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 = DnsGetDomainName( 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;
}
//
// 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 = (LPSTR) precordSOA->Data.SOA.pNamePrimaryServer;
//
// check additional for primary A record
// if found, build network info blob for update
// that points only to update server
//
pnetInfo = buildUpdateNetworkInfoFromFAZ(
pzoneName,
pszdnsHost,
precordSOA );
//
// if no primary server A record found -- must query
//
if ( !pnetInfo )
{
DNSDBG( QUERY, (
"WARNING: FAZ making additional query for primary!\n"
"\tPrimary (%s) A record should have been in additional section!\n",
pszdnsHost ));
status = DnsQuery_UTF8(
pszdnsHost,
DNS_TYPE_A,
dwFlags |
DNS_QUERY_TREAT_AS_FQDN |
DNS_QUERY_ALLOW_EMPTY_AUTH_RESP,
aipQueryServers,
& precordPrimary,
NULL );
if ( status != ERROR_SUCCESS || precordPrimary == NULL )
{
DNS_PRINT(( "ERROR: no response to primary A query\n" ));
TEST_ASSERT( FALSE );
status = DNS_ERROR_RCODE_SERVER_FAILURE;
goto Cleanup;
}
pnetInfo = buildUpdateNetworkInfoFromFAZ(
pzoneName,
pszdnsHost,
precordPrimary );
if ( !pnetInfo )
{
status = DNS_ERROR_RCODE_SERVER_FAILURE;
}
}
Cleanup:
Dns_RecordListFree( precord );
Dns_RecordListFree( precordPrimary );
*ppNetworkInfo = pnetInfo;
IF_DNSDBG( QUERY )
{
DNS_PRINT((
"Leaving DnsFindAuthoritativeZone()\n"
"\tstatus = %d\n"
"\tzone = %s\n",
status,
pzoneName ));
}
return( status );
}
DNS_STATUS
DoQuickFAZ(
OUT PDNS_NETINFO * ppNetworkInfo,
IN LPSTR pszName,
IN PIP_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 = DnsFindAuthoritativeZone(
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 PIP_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(PIP_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;
PIP_ARRAY pdnsArray;
if ( iter1 >= ArrayLength )
{
break;
}
padapter = pnetInfo->AdapterArray[iter1];
if ( !padapter )
{
continue;
}
// skip if no DNS servers
serverCount = padapter->ServerCount;
if ( serverCount== 0 )
{
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 & DNS_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
//
pdnsArray = Dns_CreateIpArray( serverCount );
if ( ! pdnsArray )
{
goto Exit;
}
for ( iter2 = 0; iter2 < serverCount; iter2++ )
{
pdnsArray->AddrArray[iter2] = padapter->ServerArray[iter2].IpAddress;
}
DnsServerListArray[countNets] = pdnsArray;
countNets++;
}
Exit:
// free network info
// return count of DNS server lists found
NetInfo_Free( pnetInfo );
return countNets;
}
DWORD
cleanDeadAdaptersFromArray(
IN OUT PIP_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++ )
{
PIP_ARRAY parray = IpArrayArray[iter];
if ( !parray || parray->AddrCount==0 )
{
if ( parray )
{
FREE_HEAP( 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 PIP_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 PIP_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.
--*/
{
PIP_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
//
Dns_UnionOfIpArrays( 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 PIP_ARRAY * DnsServerListArray,
OUT PDNS_NETINFO * NetworkInfoArray,
IN OUT PDWORD pNetCount,
IN LPSTR 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 ];
PIP_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 ( Dns_IsIntersectionOfIpArrays( 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 = DnsFindAuthoritativeZone(
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 ( 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 ( 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
CompareTwoAdaptersForSameNameSpace(
IN PIP_ARRAY pDnsServerList1,
IN PDNS_NETINFO pNetworkInfo1,
IN OUT PDNS_RECORD * ppNsRecord1,
IN PIP_ARRAY pDnsServerList2,
IN PDNS_NETINFO pNetworkInfo2,
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
pNetworkInfo1 -- 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
pNetworkInfo2 -- 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 = pNetworkInfo1->AdapterArray[0];
PDNS_ADAPTER padapter2 = pNetworkInfo2->AdapterArray[0];
PDNS_RECORD pns1 = NULL;
PDNS_RECORD pns2 = NULL;
PDNS_RECORD pnotUsed = NULL;
PSTR pzoneName;
//
// done if bad params
//
if ( !pDnsServerList1 || !pDnsServerList2 )
{
return FALSE;
}
//
// validity check
// - note: could probably be just ASSERT()
//
if ( ! NetInfo_IsForUpdate(pNetworkInfo1) ||
! NetInfo_IsForUpdate(pNetworkInfo2) )
{
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( pNetworkInfo1 );
if ( ! Dns_NameCompare_UTF8(
pzoneName,
NetInfo_UpdateZoneName( pNetworkInfo2 ) ) )
{
return FALSE;
}
//
// check if pointing at same server:
// - if have same update DNS server -- have a match
// - if same server name -- have a match
//
if ( padapter1->ServerCount == 1 &&
padapter2->ServerCount == 1 &&
padapter1->ServerArray[0].IpAddress == padapter2->ServerArray[0].IpAddress )
{
return TRUE;
}
else
{
fsame = Dns_NameCompare_UTF8(
NetInfo_UpdateServerName( pNetworkInfo1 ),
NetInfo_UpdateServerName( pNetworkInfo2 ) );
}
//
// 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 = DnsQuery_UTF8(
pzoneName,
DNS_TYPE_NS,
DNS_QUERY_BYPASS_CACHE,
pDnsServerList1,
&pns1,
NULL );
if ( status != ERROR_SUCCESS )
{
goto Done;
}
pnotUsed = DnsRecordSetDetach( pns1 );
if ( pnotUsed )
{
Dns_RecordListFree( pnotUsed );
pnotUsed = NULL;
}
}
if ( ppNsRecord2 )
{
pns2 = *ppNsRecord2;
}
if ( !pns2 )
{
status = DnsQuery_UTF8(
pzoneName,
DNS_TYPE_NS,
DNS_QUERY_BYPASS_CACHE,
pDnsServerList2,
&pns2,
NULL );
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
CompareMultiAdapterSOAQueries(
IN LPSTR pszDomainName,
IN PIP_ARRAY pDnsServerList1,
IN PIP_ARRAY pDnsServerList2
)
/*++
Routine Description:
Compare two adapters to see if in same name space for update.
Arguments:
pszDomainName -- domain name to update
pDnsServerList1 -- IP array of DNS servers for first adapter
pDnsServerList2 -- 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, (
"CompareMultiAdapterSOAQueries()\n" ));
// bad param screening
if ( !pDnsServerList1 || !pDnsServerList2 || !pszDomainName )
{
return FALSE;
}
//
// compare DNS server lists
// if any overlap, them effectively in same DNS namespace
//
if ( Dns_IsIntersectionOfIpArrays( pDnsServerList1, pDnsServerList2 ) )
{
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 = DnsFindAuthoritativeZone(
pszDomainName,
DNS_QUERY_BYPASS_CACHE,
pDnsServerList1,
&pnetInfo1 );
if ( status != ERROR_SUCCESS )
{
goto Cleanup;
}
status = DnsFindAuthoritativeZone(
pszDomainName,
DNS_QUERY_BYPASS_CACHE,
pDnsServerList2,
&pnetInfo2 );
if ( status != ERROR_SUCCESS )
{
goto Cleanup;
}
//
// call the comparison routine
//
fsame = CompareTwoAdaptersForSameNameSpace(
pDnsServerList1,
pnetInfo1,
NULL, // no NS record list
pDnsServerList2,
pnetInfo2,
NULL, // no NS record list
TRUE // follow up with NS queries
);
Cleanup:
NetInfo_Free( pnetInfo1 );
NetInfo_Free( pnetInfo2 );
return fsame;
}
IP_ADDRESS
FindHostIpAddressInRecordList(
IN PDNS_RECORD pRecordList,
IN LPSTR 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 (in UTF8)
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_UTF8(
prr->pName,
pszHostName ) )
{
return( prr->Data.A.IpAddress );
}
prr = prr->pNext;
}
return( 0 );
}
PIP_ARRAY
GetNameServersListForDomain(
IN LPSTR pDomainName,
IN PIP_ARRAY aipServers
)
/*++
Routine Description:
Get IPs for all DNS servers for zone.
Arguments:
pDomainName -- zone name
aipServers -- 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;
PIP_ARRAY aipNSList = NULL;
DWORD countAddr = 0;
status = DnsQuery_UTF8(
pDomainName,
DNS_TYPE_NS,
DNS_QUERY_BYPASS_CACHE,
aipServers,
&prrQuery,
NULL );
if ( status == NO_ERROR )
{
PDNS_RECORD pTemp = prrQuery;
DWORD dwCount = 0;
while ( pTemp )
{
dwCount++;
pTemp = pTemp->pNext;
}
aipNSList = Dns_CreateIpArray( dwCount );
if ( aipNSList )
{
pTemp = prrQuery;
while ( pTemp )
{
if ( pTemp->wType == DNS_TYPE_NS )
{
IP_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 = DnsQuery_UTF8( pTemp->Data.NS.pNameHost,
DNS_TYPE_A,
DNS_QUERY_BYPASS_CACHE,
aipServers,
&pARecord,
NULL );
if ( status == NO_ERROR &&
pARecord )
{
ip = pARecord->Data.A.IpAddress;
Dns_RecordListFree( pARecord );
}
}
if ( ip &&
!Dns_IsAddressInIpArray( aipNSList, ip ) )
{
Dns_AddIpToIpArray( aipNSList, ip );
countAddr++;
}
}
pTemp = pTemp->pNext;
}
}
}
if ( prrQuery )
{
Dns_RecordListFree( prrQuery );
}
if ( aipNSList )
{
aipNSList->AddrCount = countAddr;
}
return aipNSList;
}
//
// End of faz.c
//