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.
 
 
 
 
 
 

4562 lines
104 KiB

/*++
Copyright (c) 2000-2001 Microsoft Corporation
Module Name:
netinfo.c
Abstract:
Domain Name System (DNS) API
DNS network info routines.
Author:
Jim Gilroy (jamesg) March 2000
Revision History:
--*/
#include "local.h"
#include "registry.h" // Registry reading definitions
// Netinfo cache
//
// Do in process caching of netinfo for brief period for perf
// Currently cache for only 10s
// Locking currently just using general CS
//
PDNS_NETINFO g_pNetInfo = NULL;
#define NETINFO_CACHE_TIMEOUT (15) // 15 seconds
BOOL g_NetInfoCacheLockInitialized = FALSE;
CRITICAL_SECTION g_NetInfoCacheLock;
#define LOCK_NETINFO_CACHE() EnterCriticalSection( &g_NetInfoCacheLock );
#define UNLOCK_NETINFO_CACHE() LeaveCriticalSection( &g_NetInfoCacheLock );
//
// Netinfo build requires drop into other services
// which could
// a) be waiting
// b) have dependencies back on DNS
// so we protect with timed lock -- failing if not complete in a few seconds.
//
BOOL g_NetInfoBuildLockInitialized = FALSE;
TIMED_LOCK g_NetInfoBuildLock;
#define LOCK_NETINFO_BUILD() TimedLock_Enter( &g_NetInfoBuildLock, 5000 );
#define UNLOCK_NETINFO_BUILD() TimedLock_Leave( &g_NetInfoBuildLock );
//
// DNS_ADDR screening
//
// Use a user-defined field in DNS_ADDR as screening mask, in screening function.
// Mask is set in screening address.
#define DnsAddrFlagScreeningMask DnsAddrUser1Dword1
VOID
AdapterInfo_Free(
IN OUT PDNS_ADAPTER pAdapter,
IN BOOL fFreeAdapterStruct
)
/*++
Routine Description:
Free DNS_ADAPTER structure.
Arguments:
pAdapter -- pointer to adapter blob to free
Return Value:
None.
--*/
{
DNSDBG( TRACE, ( "AdapterInfo_Free( %p, %d )\n", pAdapter, fFreeAdapterStruct ));
if ( !pAdapter )
{
return;
}
if ( pAdapter->pszAdapterGuidName )
{
FREE_HEAP( pAdapter->pszAdapterGuidName );
}
if ( pAdapter->pszAdapterDomain )
{
FREE_HEAP( pAdapter->pszAdapterDomain );
}
if ( pAdapter->pLocalAddrs )
{
FREE_HEAP( pAdapter->pLocalAddrs );
}
if ( pAdapter->pDnsAddrs )
{
FREE_HEAP( pAdapter->pDnsAddrs );
}
if ( fFreeAdapterStruct )
{
FREE_HEAP( pAdapter );
}
}
VOID
AdapterInfo_Init(
OUT PDNS_ADAPTER pAdapter,
IN BOOL fZeroInit,
IN DWORD InfoFlags,
IN PWSTR pszGuidName,
IN PWSTR pszDomain,
IN PDNS_ADDR_ARRAY pLocalAddrs,
IN PDNS_ADDR_ARRAY pDnsAddrs
)
/*++
Routine Description:
Init adapter info blob.
This sets the blob DIRECTLY with these pointers,
no reallocation.
Arguments:
pAdapter -- adapter blob to fill in
fZeroInit -- clear to zero
InfoFlags -- flags
pszGuidName -- GUID name
pszAdapterDomain -- adapter domain name
pLocalAddrs -- local addrs
pDnsAddrs -- DNS server addrs
Return Value:
Ptr to adapter info, if successful
NULL on failure.
--*/
{
DNSDBG( TRACE, ( "AdapterInfo_Init()\n" ));
//
// setup adapter info blob
//
if ( fZeroInit )
{
RtlZeroMemory(
pAdapter,
sizeof( *pAdapter ) );
}
// names
pAdapter->pszAdapterGuidName = pszGuidName;
pAdapter->pszAdapterDomain = pszDomain;
pAdapter->pLocalAddrs = pLocalAddrs;
pAdapter->pDnsAddrs = pDnsAddrs;
// if no DNS servers -- set ignore flag
if ( fZeroInit && !pDnsAddrs )
{
InfoFlags |= AINFO_FLAG_IGNORE_ADAPTER;
}
pAdapter->InfoFlags = InfoFlags;
}
DNS_STATUS
AdapterInfo_Create(
OUT PDNS_ADAPTER pAdapter,
IN BOOL fZeroInit,
IN DWORD InfoFlags,
IN PWSTR pszGuidName,
IN PWSTR pszDomain,
IN PDNS_ADDR_ARRAY pLocalAddrs,
IN PDNS_ADDR_ARRAY pDnsAddrs
)
/*++
Routine Description:
Create adapter info blob.
This initializes blob with copies of names and addr arrays
provided.
Arguments:
pAdapter -- adapter blob to fill in
fZeroInit -- clear to zero
InfoFlags -- flags
pszGuidName -- GUID name
pszAdapterDomain -- adapter domain name
pLocalAddrs -- local addrs
pDnsAddrs -- DNS server addrs
Return Value:
Ptr to adapter info, if successful
NULL on failure.
--*/
{
PDNS_ADDR_ARRAY paddrs;
PWSTR pname;
DNSDBG( TRACE, ( "AdapterInfo_Create()\n" ));
//
// setup adapter info blob
//
if ( fZeroInit )
{
RtlZeroMemory(
pAdapter,
sizeof( *pAdapter ) );
}
// names
if ( pszGuidName )
{
pname = Dns_CreateStringCopy_W( pszGuidName );
if ( !pname )
{
goto Failed;
}
pAdapter->pszAdapterGuidName = pname;
}
if ( pszDomain )
{
pname = Dns_CreateStringCopy_W( pszDomain );
if ( !pname )
{
goto Failed;
}
pAdapter->pszAdapterDomain = pname;
}
// addresses
if ( pLocalAddrs )
{
paddrs = DnsAddrArray_CreateCopy( pLocalAddrs );
if ( !paddrs )
{
goto Failed;
}
pAdapter->pLocalAddrs = paddrs;
}
if ( pDnsAddrs )
{
paddrs = DnsAddrArray_CreateCopy( pDnsAddrs );
if ( !paddrs )
{
goto Failed;
}
pAdapter->pDnsAddrs = paddrs;
}
// if no DNS servers -- set ignore flag
if ( fZeroInit && !paddrs )
{
InfoFlags |= AINFO_FLAG_IGNORE_ADAPTER;
}
pAdapter->InfoFlags = InfoFlags;
return NO_ERROR;
Failed:
AdapterInfo_Free(
pAdapter,
FALSE // no structure free
);
return DNS_ERROR_NO_MEMORY;
}
DNS_STATUS
AdapterInfo_Copy(
OUT PDNS_ADAPTER pCopy,
IN PDNS_ADAPTER pAdapter
)
/*++
Routine Description:
Create copy of DNS adapter info.
Arguments:
pAdapter -- DNS adapter to copy
Return Value:
Ptr to DNS adapter info copy, if successful
NULL on failure.
--*/
{
DNS_STATUS status;
DNSDBG( TRACE, ( "AdapterInfo_Copy( %p )\n", pAdapter ));
if ( !pAdapter || !pCopy )
{
return ERROR_INVALID_PARAMETER;
}
//
// copy all fields
//
RtlCopyMemory(
pCopy,
pAdapter,
sizeof( *pAdapter ) );
pCopy->pszAdapterGuidName = NULL;
pCopy->pszAdapterDomain = NULL;
pCopy->pLocalAddrs = NULL;
pCopy->pDnsAddrs = NULL;
//
// do create copy
//
return AdapterInfo_Create(
pCopy,
FALSE, // no zero init
pAdapter->InfoFlags,
pAdapter->pszAdapterGuidName,
pAdapter->pszAdapterDomain,
pAdapter->pLocalAddrs,
pAdapter->pDnsAddrs );
}
DNS_STATUS
AdapterInfo_CreateFromIp4Array(
OUT PDNS_ADAPTER pAdapter,
IN PIP4_ARRAY pServerArray,
IN DWORD Flags,
IN PWSTR pszDomainName,
IN PWSTR pszGuidName
)
/*++
Routine Description:
Create copy of IP address array as a DNS Server list.
Arguments:
pIpArray -- IP address array to convert
Flags -- Flags that describe the adapter
pszDomainName -- The default domain name for the adapter
pszGuidName -- The registry GUID name for the adapter (if NT)
Return Value:
ERROR_SUCCESS if successful.
ErrorCode on error.
--*/
{
DNS_STATUS status;
PDNS_ADDR_ARRAY pdnsArray;
DWORD i;
DWORD count;
DNSDBG( TRACE, ( "AdapterInfo_CreateFromIp4Array()\n" ));
//
// get count of DNS servers
//
if ( !pServerArray )
{
count = 0;
}
else
{
count = pServerArray->AddrCount;
}
//
// copy DNS server IPs
//
pdnsArray = DnsAddrArray_CreateFromIp4Array( pServerArray );
if ( !pdnsArray )
{
status = DNS_ERROR_NO_MEMORY;
goto Done;
}
//
// build adapter info
//
status = AdapterInfo_Create(
pAdapter,
TRUE, // zero init
Flags,
pszDomainName,
pszGuidName,
NULL,
pdnsArray );
Done:
DnsAddrArray_Free( pdnsArray );
return status;
}
//
// Search list routines
//
PSEARCH_LIST
SearchList_Alloc(
IN DWORD MaxNameCount
)
/*++
Routine Description:
Create uninitialized search list.
Arguments:
NameCount -- count of search names list will hold
Return Value:
Ptr to uninitialized DNS search list, if successful
NULL on failure.
--*/
{
PSEARCH_LIST psearchList = NULL;
DWORD length;
DNSDBG( TRACE, ( "SearchList_Alloc()\n" ));
//
// note: specifically allowing alloc even if no name count
// or domain name as could have no PDN and still need
// search list referenced in query
//
#if 0
if ( MaxNameCount == 0 )
{
return NULL;
}
#endif
//
// allocate for max entries
//
length = sizeof(SEARCH_LIST)
- sizeof(SEARCH_NAME)
+ ( sizeof(SEARCH_NAME) * MaxNameCount );
psearchList = (PSEARCH_LIST) ALLOCATE_HEAP_ZERO( length );
if ( ! psearchList )
{
return NULL;
}
psearchList->MaxNameCount = MaxNameCount;
return psearchList;
}
VOID
SearchList_Free(
IN OUT PSEARCH_LIST pSearchList
)
/*++
Routine Description:
Free SEARCH_LIST structure.
Arguments:
pSearchList -- ptr to search list to free
Return Value:
None
--*/
{
DWORD i;
DNSDBG( TRACE, ( "SearchList_Free( %p )\n", pSearchList ));
//
// free all search names, then list itself
//
if ( pSearchList )
{
for ( i=0; i < pSearchList->NameCount; i++ )
{
PWSTR pname = pSearchList->SearchNameArray[i].pszName;
if ( pname )
{
FREE_HEAP( pname );
}
}
FREE_HEAP( pSearchList );
}
}
PSEARCH_LIST
SearchList_Copy(
IN PSEARCH_LIST pSearchList
)
/*++
Routine Description:
Create copy of search list.
Arguments:
pSearchList -- search list to copy
Return Value:
Ptr to DNS Search list copy, if successful
NULL on failure.
--*/
{
PSEARCH_LIST pcopy;
DWORD i;
DNSDBG( TRACE, ( "SearchList_Copy()\n" ));
if ( ! pSearchList )
{
return NULL;
}
//
// create DNS Search list of desired size
//
// since we don't add and delete from search list once
// created, size copy only for actual name count
//
pcopy = SearchList_Alloc( pSearchList->NameCount );
if ( ! pcopy )
{
return NULL;
}
for ( i=0; i < pSearchList->NameCount; i++ )
{
PWSTR pname = pSearchList->SearchNameArray[i].pszName;
if ( pname )
{
pname = Dns_CreateStringCopy_W( pname );
if ( pname )
{
pcopy->SearchNameArray[i].pszName = pname;
pcopy->SearchNameArray[i].Flags = pSearchList->SearchNameArray[i].Flags;
pcopy->NameCount++;
}
}
}
return pcopy;
}
BOOL
SearchList_ContainsName(
IN PSEARCH_LIST pSearchList,
IN PWSTR pszName
)
/*++
Routine Description:
Check if name is in search list.
Arguments:
pSearchList -- ptr to search list being built
pszName -- name to check
Return Value:
TRUE if name is in search list.
FALSE otherwise.
--*/
{
DWORD count = pSearchList->NameCount;
//
// check every search list entry for this name
//
while ( count-- )
{
if ( Dns_NameCompare_W(
pSearchList->SearchNameArray[ count ].pszName,
pszName ) )
{
return( TRUE );
}
}
return( FALSE );
}
VOID
SearchList_AddName(
IN OUT PSEARCH_LIST pSearchList,
IN PWSTR pszName,
IN DWORD Flag
)
/*++
Routine Description:
Add name to search list.
Arguments:
pSearchList -- ptr to search list being built
pszName -- name to add to search list
Flag -- flag value
Return Value:
None. Name is added to search list, unless memory alloc failure.
--*/
{
DWORD count = pSearchList->NameCount;
PWSTR pallocName;
DNSDBG( TRACE, ( "Search_AddName()\n" ));
//
// ignore name is already in list
// ignore if at list max
//
if ( SearchList_ContainsName(
pSearchList,
pszName )
||
count >= pSearchList->MaxNameCount )
{
return;
}
// copy name and put in list
pallocName = Dns_CreateStringCopy_W( pszName );
if ( !pallocName )
{
return;
}
pSearchList->SearchNameArray[count].pszName = pallocName;
//
// set flag -- but first flag always zero (normal timeouts)
// this protects against no PDN situation where use adapter
// name as PDN;
if ( count == 0 )
{
Flag = 0;
}
pSearchList->SearchNameArray[count].Flags = Flag;
pSearchList->NameCount = ++count;
}
DNS_STATUS
SearchList_Parse(
IN OUT PSEARCH_LIST pSearchList,
IN PWSTR pszList
)
/*++
Routine Description:
Parse registry search list string into SEARCH_LIST structure.
Arguments:
pSearchList -- search list array
pszList -- registry list of search names;
names are comma or white space separated
Return Value:
ERROR_SUCCESS if successful.
Error code on failure.
--*/
{
register PWCHAR pch = pszList;
WCHAR ch;
PWCHAR pnameStart;
PWSTR pname;
DWORD countNames = pSearchList->NameCount;
DNSDBG( NETINFO, (
"SearchList_Parse( %p, %S )\n",
pSearchList,
pszList ));
//
// extract each domain name string in buffer,
// and add to search list array
//
while( ch = *pch && countNames < MAX_SEARCH_LIST_ENTRIES )
{
// skip leading whitespace, find start of domain name string
while( ch == ' ' || ch == '\t' || ch == ',' )
{
ch = *++pch;
}
if ( ch == 0 )
{
break;
}
pnameStart = pch;
//
// find end of string and NULL terminate
//
ch = *pch;
while( ch != L' ' && ch != L'\t' && ch != L'\0' && ch != L',' )
{
ch = *++pch;
}
*pch = L'\0';
//
// end of buffer?
//
if ( pch == pnameStart )
{
break;
}
//
// whack any trailing dot on name
//
pch--;
if ( *pch == L'.' )
{
*pch = L'\0';
}
pch++;
//
// make copy of the name
//
pname = Dns_CreateStringCopy_W( pnameStart );
if ( pname )
{
pSearchList->SearchNameArray[ countNames ].pszName = pname;
pSearchList->SearchNameArray[ countNames ].Flags = 0;
countNames++;
}
// if more continue
if ( ch != 0 )
{
pch++;
continue;
}
break;
}
// reset name count
pSearchList->NameCount = countNames;
return( ERROR_SUCCESS );
}
PSEARCH_LIST
SearchList_Build(
IN PWSTR pszPrimaryDomainName,
IN PREG_SESSION pRegSession,
IN HKEY hKey,
IN OUT PDNS_NETINFO pNetInfo,
IN BOOL fUseDomainNameDevolution
)
/*++
Routine Description:
Build search list.
Arguments:
pszPrimaryDomainName -- primary domain name
pRegSession -- registry session
hKey -- registry key
Return Value:
Ptr to search list.
NULL on error or no search list.
--*/
{
PSEARCH_LIST ptempList;
PWSTR pregList = NULL;
DWORD status;
DNSDBG( TRACE, ( "Search_ListBuild()\n" ));
ASSERT( pRegSession || hKey );
//
// create search list using PDN
//
ptempList = SearchList_Alloc( MAX_SEARCH_LIST_ENTRIES );
if ( !ptempList )
{
return( NULL );
}
//
// read search list from registry
//
status = Reg_GetValue(
pRegSession,
hKey,
RegIdSearchList,
REGTYPE_SEARCH_LIST,
(PBYTE*) &pregList
);
if ( status == ERROR_SUCCESS )
{
ASSERT( pregList != NULL );
SearchList_Parse(
ptempList,
pregList );
FREE_HEAP( pregList );
}
else if ( status == DNS_ERROR_NO_MEMORY )
{
FREE_HEAP( ptempList );
return NULL;
}
//
// if no registry search list -- build one
//
// DCR: eliminate autobuilt search list
//
if ( ! ptempList->NameCount )
{
//
// use PDN in first search list slot
//
if ( pszPrimaryDomainName )
{
SearchList_AddName(
ptempList,
pszPrimaryDomainName,
0 );
}
//
// add devolved PDN if have NameDevolution
//
// note, we devolve only down to second level
// domain name "microsoft.com" NOT "com";
// to avoid being fooled by name like "com."
// also check that last dot is not terminal
//
if ( pszPrimaryDomainName && fUseDomainNameDevolution )
{
PWSTR pname = pszPrimaryDomainName;
while ( pname )
{
PWSTR pnext = wcschr( pname, '.' );
if ( !pnext )
{
break;
}
pnext++;
if ( !*pnext )
{
//DNS_ASSERT( FALSE );
break;
}
// add name, but not on first pass
// - already have PDN in first slot
if ( pname != pszPrimaryDomainName )
{
SearchList_AddName(
ptempList,
pname,
DNS_QUERY_USE_QUICK_TIMEOUTS );
}
pname = pnext;
}
}
// indicate this is dummy search list
if ( pNetInfo )
{
pNetInfo->InfoFlags |= NINFO_FLAG_DUMMY_SEARCH_LIST;
}
}
return ptempList;
}
PWSTR
SearchList_GetNextName(
IN OUT PSEARCH_LIST pSearchList,
IN BOOL fReset,
OUT PDWORD pdwSuffixFlags OPTIONAL
)
/*++
Routine Description:
Gets the next name from the search list.
Arguments:
pSearchList -- search list
fReset -- TRUE to reset to beginning of search list
pdwSuffixFlags -- flags associate with using this suffix
Return Value:
Ptr to the next search name. Note, this is a pointer
to a name in the search list NOT an allocation. Search
list structure must stay valid during use.
NULL when out of search names.
--*/
{
DWORD flag = 0;
PWSTR pname = NULL;
DWORD index;
DNSDBG( TRACE, ( "SearchList_GetNextName()\n" ));
// no list
if ( !pSearchList )
{
goto Done;
}
//
// reset?
//
if ( fReset )
{
pSearchList->CurrentNameIndex = 0;
}
//
// if valid name -- retrieve it
//
index = pSearchList->CurrentNameIndex;
if ( index < pSearchList->NameCount )
{
pname = pSearchList->SearchNameArray[index].pszName;
flag = pSearchList->SearchNameArray[index].Flags;
pSearchList->CurrentNameIndex = ++index;
}
Done:
if ( pdwSuffixFlags )
{
*pdwSuffixFlags = flag;
}
return pname;
}
//
// Net info routines
//
PDNS_NETINFO
NetInfo_Alloc(
IN DWORD AdapterCount
)
/*++
Routine Description:
Allocate network info.
Arguments:
AdapterCount -- count of net adapters info will hold
Return Value:
Ptr to uninitialized DNS network info, if successful
NULL on failure.
--*/
{
PDNS_NETINFO pnetInfo;
DWORD length;
DNSDBG( TRACE, ( "NetInfo_Alloc()\n" ));
//
// alloc
// - zero to avoid garbage on early free
//
length = sizeof(DNS_NETINFO)
- sizeof(DNS_ADAPTER)
+ (sizeof(DNS_ADAPTER) * AdapterCount);
pnetInfo = (PDNS_NETINFO) ALLOCATE_HEAP_ZERO( length );
if ( ! pnetInfo )
{
return NULL;
}
pnetInfo->MaxAdapterCount = AdapterCount;
return( pnetInfo );
}
VOID
NetInfo_Free(
IN OUT PDNS_NETINFO pNetInfo
)
/*++
Routine Description:
Free DNS_NETINFO structure.
Arguments:
pNetInfo -- ptr to netinfo to free
Return Value:
None
--*/
{
DWORD i;
DNSDBG( TRACE, ( "NetInfo_Free( %p )\n", pNetInfo ));
if ( ! pNetInfo )
{
return;
}
IF_DNSDBG( OFF )
{
DnsDbg_NetworkInfo(
"Network Info before free: ",
pNetInfo );
}
//
// free
// - search list
// - domain name
// - all the adapter info blobs
//
SearchList_Free( pNetInfo->pSearchList );
if ( pNetInfo->pszDomainName )
{
FREE_HEAP( pNetInfo->pszDomainName );
}
if ( pNetInfo->pszHostName )
{
FREE_HEAP( pNetInfo->pszHostName );
}
for ( i=0; i < pNetInfo->AdapterCount; i++ )
{
AdapterInfo_Free(
NetInfo_GetAdapterByIndex( pNetInfo, i ),
FALSE // no structure free
);
}
FREE_HEAP( pNetInfo );
}
PDNS_NETINFO
NetInfo_Copy(
IN PDNS_NETINFO pNetInfo
)
/*++
Routine Description:
Create copy of DNS Network info.
Arguments:
pNetInfo -- DNS Network info to copy
Return Value:
Ptr to DNS Network info copy, if successful
NULL on failure.
--*/
{
PDNS_NETINFO pcopy;
PDNS_ADAPTER padapter;
DWORD adapterCount;
DNS_STATUS status;
DWORD iter;
DNSDBG( TRACE, ( "NetInfo_Copy( %p )\n", pNetInfo ));
if ( ! pNetInfo )
{
return NULL;
}
IF_DNSDBG( OFF )
{
DnsDbg_NetworkInfo(
"Netinfo to copy: ",
pNetInfo );
}
//
// create network info struct of desired size
//
pcopy = NetInfo_Alloc( pNetInfo->AdapterCount );
if ( ! pcopy )
{
return NULL;
}
//
// copy flat fields
// - must reset MaxAdapterCount to actual allocation
// - AdapterCount reset below
//
RtlCopyMemory(
pcopy,
pNetInfo,
(PBYTE) &pcopy->AdapterArray[0] - (PBYTE)pcopy );
pcopy->MaxAdapterCount = pNetInfo->AdapterCount;
pcopy->AdapterCount = 0;
//
// copy subcomponents
// - domain name
// - search list
//
pcopy->pszDomainName = Dns_CreateStringCopy_W( pNetInfo->pszDomainName );
pcopy->pszHostName = Dns_CreateStringCopy_W( pNetInfo->pszHostName );
pcopy->pSearchList = SearchList_Copy( pNetInfo->pSearchList );
if ( (!pcopy->pszDomainName && pNetInfo->pszDomainName) ||
(!pcopy->pszHostName && pNetInfo->pszHostName) ||
(!pcopy->pSearchList && pNetInfo->pSearchList) )
{
status = DNS_ERROR_NO_MEMORY;
goto Failed;
}
//
// copy adapter info
//
adapterCount = 0;
for ( iter=0; iter<pNetInfo->AdapterCount; iter++ )
{
padapter = NetInfo_GetAdapterByIndex( pNetInfo, iter );
status = AdapterInfo_Copy(
&pcopy->AdapterArray[ adapterCount ],
padapter
);
if ( status == NO_ERROR )
{
pcopy->AdapterCount = ++adapterCount;
continue;
}
goto Failed;
}
IF_DNSDBG( OFF )
{
DnsDbg_NetworkInfo(
"Netinfo copy: ",
pcopy );
}
return pcopy;
Failed:
NetInfo_Free( pcopy );
SetLastError( status );
return NULL;
}
VOID
NetInfo_Clean(
IN OUT PDNS_NETINFO pNetInfo,
IN DWORD ClearLevel
)
/*++
Routine Description:
Clean network info.
Removes all query specific info and restores to
state that is "fresh" for next query.
Arguments:
pNetInfo -- DNS network info
ClearLevel -- level of runtime flag cleaning
Return Value:
None
--*/
{
PDNS_ADAPTER padapter;
DWORD iter;
DNSDBG( TRACE, (
"Enter NetInfo_Clean( %p, %08x )\n",
pNetInfo,
ClearLevel ));
IF_DNSDBG( NETINFO2 )
{
DnsDbg_NetworkInfo(
"Cleaning network info:",
pNetInfo
);
}
//
// clean up info
// - clear status fields
// - clear RunFlags
// - clear temp bits on InfoFlags
//
// note, runtime flags are wiped depending on level
// specified in call
// - all (includes disabled\timedout adapter info)
// - query (all query info)
// - name (all info for single name query)
//
// finally we set NETINFO_PREPARED flag so that we can
// can check for and do this initialization in the send
// code if not previously done;
//
// in the standard query path we can
// - do this init
// - disallow adapters based on query name
// - send without the info getting wiped
//
// in other send paths
// - send checks that NETINFO_PREPARED is not set
// - does basic init
//
pNetInfo->ReturnFlags &= ClearLevel;
pNetInfo->ReturnFlags |= RUN_FLAG_NETINFO_PREPARED;
for ( iter=0; iter<pNetInfo->AdapterCount; iter++ )
{
DWORD j;
PDNS_ADDR_ARRAY pserverArray;
padapter = NetInfo_GetAdapterByIndex( pNetInfo, iter );
pserverArray = padapter->pDnsAddrs;
if ( !pserverArray )
{
continue;
}
padapter->Status = 0;
padapter->RunFlags &= ClearLevel;
// clear server status fields
for ( j=0; j<pserverArray->AddrCount; j++ )
{
pserverArray->AddrArray[j].Flags = SRVFLAG_NEW;
pserverArray->AddrArray[j].Status = SRVSTATUS_NEW;
}
}
}
VOID
NetInfo_ResetServerPriorities(
IN OUT PDNS_NETINFO pNetInfo,
IN BOOL fLocalDnsOnly
)
/*++
Routine Description:
Resets the DNS server priority values for the DNS servers.
Arguments:
pNetInfo -- pointer to a DNS network info structure.
fLocalDnsOnly - TRUE to reset priority ONLY on local DNS servers
Note that this requires that the network info contain the IP address
list for each adapter so that the IP address list can be compared
to the DNS server list.
Return Value:
Nothing
--*/
{
PDNS_ADAPTER padapter;
DWORD iter;
DNSDBG( TRACE, ( "NetInfo_ResetServerPriorities( %p )\n", pNetInfo ));
if ( !pNetInfo )
{
return;
}
//
// reset priorities on server
// when
// - not do local only OR
// - server IP matches one of adapter IPs
//
// FIX6: local DNS check needs IP6 fixups
// DCR: encapsulate as "NetInfo_IsLocalAddress
for ( iter=0; iter<pNetInfo->AdapterCount; iter++ )
{
DWORD j;
PDNS_ADDR_ARRAY pserverArray;
padapter = NetInfo_GetAdapterByIndex( pNetInfo, iter );
pserverArray = padapter->pDnsAddrs;
if ( !pserverArray )
{
continue;
}
for ( j=0; j < pserverArray->AddrCount; j++ )
{
PDNS_ADDR pserver = &pserverArray->AddrArray[j];
// loopback goes first
// - we plumb it in for specific AD scenarios
// - the cost of failure is low (should just generate
// ICMP -- CONNRESET -- if server not running)
if ( DnsAddr_IsLoopback(pserver, 0) )
{
pserver->Priority = SRVPRI_LOOPBACK;
continue;
}
if ( !fLocalDnsOnly )
{
if ( DnsAddr_IsIp6DefaultDns( pserver ) )
{
pserver->Priority = SRVPRI_IP6_DEFAULT;
continue;
}
else
{
pserver->Priority = SRVPRI_DEFAULT;
continue;
}
}
// pull this into local test
if ( LocalIp_IsAddrLocal(
pserver,
NULL, // no local array
pNetInfo // use netinfo to screen local addrs
) )
{
pserver->Priority = SRVPRI_DEFAULT;
continue;
}
}
}
}
PDNS_ADAPTER
NetInfo_GetNextAdapter(
IN OUT PDNS_NETINFO pNetInfo
)
/*++
Routine Description:
Get next adapter.
Note, this must be preceeded by a call to macro
NetInfo_StartAdapterLoop()
Arguments:
pNetInfo -- netinfo to get adapter from
note, internal index is incremented
Return Value:
Ptr to next DNS_ADAPTER
NULL when out of adapters.
--*/
{
DWORD index;
PDNS_ADAPTER padapter = NULL;
//
// get next adapter if still in range
//
index = pNetInfo->AdapterIndex;
if ( index < pNetInfo->AdapterCount )
{
padapter = &pNetInfo->AdapterArray[ index++ ];
pNetInfo->AdapterIndex = index;
}
return padapter;
}
PDNS_ADAPTER
NetInfo_GetAdapterByName(
IN PDNS_NETINFO pNetInfo,
IN PWSTR pwsAdapterName
)
/*++
Routine Description:
Find adapter in netinfo by name.
Arguments:
pNetInfo -- DNS net adapter list to convert
pAdapterName -- adapter name
Return Value:
Ptr to adapter, if adapter name found.
NULL on failure.
--*/
{
PDNS_ADAPTER padapterFound = NULL;
PDNS_ADAPTER padapter;
DWORD iter;
DNSDBG( TRACE, ( "NetInfo_GetAdapterByName( %p )\n", pNetInfo ));
if ( !pNetInfo || !pwsAdapterName )
{
SetLastError( ERROR_INVALID_PARAMETER );
return NULL;
}
//
// find matching adapter
//
for ( iter=0; iter<pNetInfo->AdapterCount; iter++ )
{
padapter = NetInfo_GetAdapterByIndex( pNetInfo, iter );
if ( wcscmp( padapter->pszAdapterGuidName, pwsAdapterName ) == 0 )
{
padapterFound = padapter;
break;
}
}
return( padapterFound );
}
PDNS_ADDR_ARRAY
NetInfo_ConvertToAddrArray(
IN PDNS_NETINFO pNetInfo,
IN PWSTR pwsAdapterName,
IN DWORD Family OPTIONAL
)
/*++
Routine Description:
Create IP array of DNS servers from network info.
Arguments:
pNetInfo -- DNS net adapter list to convert
pwsAdapterName -- specific adapter
Family -- required specific address family
Return Value:
Ptr to IP array, if successful
NULL on failure.
--*/
{
PDNS_ADDR_ARRAY parray = NULL;
DWORD countServers = 0;
PDNS_ADAPTER padapter;
PDNS_ADAPTER padapterSingle = NULL;
DNS_STATUS status = NO_ERROR;
DWORD iter;
DNSDBG( TRACE, ( "NetInfo_ConvertToAddrArray( %p )\n", pNetInfo ));
//
// get count
//
if ( ! pNetInfo )
{
return NULL;
}
if ( pwsAdapterName )
{
padapterSingle = NetInfo_GetAdapterByName( pNetInfo, pwsAdapterName );
if ( !padapterSingle )
{
goto Done;
}
countServers = DnsAddrArray_GetFamilyCount(
padapterSingle->pDnsAddrs,
Family );
}
else
{
for ( iter=0; iter<pNetInfo->AdapterCount; iter++ )
{
padapter = NetInfo_GetAdapterByIndex( pNetInfo, iter );
countServers += DnsAddrArray_GetFamilyCount(
padapter->pDnsAddrs,
Family );
}
}
//
// allocate required array
//
parray = DnsAddrArray_Create( countServers );
if ( !parray )
{
status = DNS_ERROR_NO_MEMORY;
goto Done;
}
DNS_ASSERT( parray->MaxCount == countServers );
//
// read all servers into IP array
//
for ( iter=0; iter<pNetInfo->AdapterCount; iter++ )
{
padapter = NetInfo_GetAdapterByIndex( pNetInfo, iter );
if ( !padapterSingle ||
padapterSingle == padapter )
{
status = DnsAddrArray_AppendArrayEx(
parray,
padapter->pDnsAddrs,
0, // append all
Family, // family screen
0, // no dup screen
NULL, // no other screening
NULL // no other screening
);
DNS_ASSERT( status == NO_ERROR );
}
}
Done:
if ( status != NO_ERROR )
{
SetLastError( status );
}
return( parray );
}
PDNS_NETINFO
NetInfo_CreateForUpdate(
IN PWSTR pszZone,
IN PWSTR pszServerName,
IN PDNS_ADDR_ARRAY pServerArray,
IN DWORD dwFlags
)
/*++
Routine Description:
Create network info suitable for update.
Arguments:
pszZone -- target zone name
pszServerName -- target server name
pServerArray -- IP array with target server IP
dwFlags -- flags
Return Value:
Ptr to resulting update compatible network info.
NULL on failure.
--*/
{
PDNS_ADAPTER padapter;
PDNS_NETINFO pnetInfo;
DNSDBG( TRACE, ( "NetInfo_CreateForUpdate()\n" ));
//
// allocate
//
pnetInfo = NetInfo_Alloc( 1 );
if ( !pnetInfo )
{
return NULL;
}
//
// save zone name
//
if ( pszZone )
{
pnetInfo->pszDomainName = Dns_CreateStringCopy_W( pszZone );
if ( !pnetInfo->pszDomainName )
{
goto Fail;
}
}
//
// convert IP array and server name to server list
//
if ( NO_ERROR != AdapterInfo_Create(
&pnetInfo->AdapterArray[0],
TRUE, // zero init
dwFlags,
NULL, // no GUID
pszServerName, // use as domain name
NULL, // no local addrs
pServerArray
) )
{
goto Fail;
}
pnetInfo->AdapterCount = 1;
IF_DNSDBG( NETINFO2 )
{
DnsDbg_NetworkInfo(
"Update network info is: ",
pnetInfo );
}
return pnetInfo;
Fail:
DNSDBG( TRACE, ( "Failed NetInfo_CreateForUpdate()!\n" ));
NetInfo_Free( pnetInfo );
return NULL;
}
#if 0
PDNS_NETINFO
NetInfo_CreateForUpdateIp4(
IN PWSTR pszZone,
IN PWSTR pszServerName,
IN PIP4_ARRAY pServ4Array,
IN DWORD dwFlags
)
/*++
Routine Description:
Create network info suitable for update -- IP4 version.
DCR: Used only by Dns_UpdateLib() once killed, kill this.
Arguments:
pszZone -- target zone name
pszServerName -- target server name
pServ4Array -- IP$ array with target server IP
dwFlags -- flags
Return Value:
Ptr to resulting update compatible network info.
NULL on failure.
--*/
{
PADDR_ARRAY parray;
PDNS_NETINFO pnetInfo;
//
// convert 4 to 6, then call real routine
//
parray = DnsAddrArray_CreateFromIp4Array( pServ4Array );
pnetInfo = NetInfo_CreateForUpdate(
pszZone,
pszServerName,
parray,
dwFlags );
DnsAddrArray_Free( parray );
return pnetInfo;
}
#endif
PWSTR
NetInfo_UpdateZoneName(
IN PDNS_NETINFO pNetInfo
)
/*++
Routine Description:
Retrieve update zone name.
Arguments:
pNetInfo -- blob to check
Return Value:
Ptr to update zone name.
NULL on error.
--*/
{
return pNetInfo->pszDomainName;
}
PWSTR
NetInfo_UpdateServerName(
IN PDNS_NETINFO pNetInfo
)
/*++
Routine Description:
Retrieve update servere name.
Arguments:
pNetInfo -- blob to check
Return Value:
Ptr to update zone name.
NULL on error.
--*/
{
return pNetInfo->AdapterArray[0].pszAdapterDomain;
}
BOOL
NetInfo_IsForUpdate(
IN PDNS_NETINFO pNetInfo
)
/*++
Routine Description:
Check if network info blob if "update capable".
This means whether it is the result of a FAZ and
can be used to send updates.
Arguments:
pNetInfo -- blob to check
Return Value:
TRUE if update network info.
FALSE otherwise.
--*/
{
DNSDBG( TRACE, ( "NetInfo_IsForUpdate()\n" ));
return ( pNetInfo &&
pNetInfo->pszDomainName &&
pNetInfo->AdapterCount == 1 );
}
PDNS_NETINFO
NetInfo_CreateFromAddrArray(
IN PADDR_ARRAY pDnsServers,
IN PDNS_ADDR pServerIp,
IN BOOL fSearchInfo,
IN PDNS_NETINFO pNetInfo OPTIONAL
)
/*++
Routine Description:
Create network info given DNS server list.
Arguments:
pDnsServers -- IP array of DNS servers
ServerIp -- single IP in list
fSearchInfo -- TRUE if need search info
pNetInfo -- current network info blob to copy search info
from; this field is only relevant if fSearchInfo is TRUE
Return Value:
Ptr to resulting network info.
NULL on failure.
--*/
{
PDNS_NETINFO pnetInfo;
ADDR_ARRAY ipArray;
PADDR_ARRAY parray = pDnsServers;
PSEARCH_LIST psearchList;
PWSTR pdomainName;
DWORD flags = 0;
//
// DCR: eliminate search list form this routine
// i believe this routine is only used for query of
// FQDNs (usually in update) and doesn't require
// any default search info
//
// DCR: possibly combine with "BuildForUpdate" routine
// where search info included tacks this on
//
//
// if given single IP, ONLY use it
//
if ( pServerIp )
{
DnsAddrArray_InitSingleWithAddr(
& ipArray,
pServerIp );
parray = &ipArray;
}
//
// convert server IPs into network info blob
// - simply use update function above to avoid duplicate code
//
pnetInfo = NetInfo_CreateForUpdate(
NULL, // no zone
NULL, // no server name
parray,
0 // no flags
);
if ( !pnetInfo )
{
return( NULL );
}
//
// get search list and primary domain info
// - copy from passed in network info
// OR
// - cut directly out of new netinfo
//
if ( fSearchInfo )
{
if ( pNetInfo )
{
flags = pNetInfo->InfoFlags;
psearchList = SearchList_Copy( pNetInfo->pSearchList );
pdomainName = Dns_CreateStringCopy_W( pNetInfo->pszDomainName );
}
else
{
PDNS_NETINFO ptempNetInfo = GetNetworkInfo();
if ( ptempNetInfo )
{
flags = ptempNetInfo->InfoFlags;
psearchList = ptempNetInfo->pSearchList;
pdomainName = ptempNetInfo->pszDomainName;
ptempNetInfo->pSearchList = NULL;
ptempNetInfo->pszDomainName = NULL;
NetInfo_Free( ptempNetInfo );
}
else
{
psearchList = NULL;
pdomainName = NULL;
}
}
// plug search info into new netinfo blob
pnetInfo->pSearchList = psearchList;
pnetInfo->pszDomainName = pdomainName;
pnetInfo->InfoFlags |= (flags & NINFO_FLAG_DUMMY_SEARCH_LIST);
}
return( pnetInfo );
}
#if 0
PDNS_NETINFO
NetInfo_CreateFromIp4Array(
IN PIP4_ARRAY pDnsServers,
IN IP4_ADDRESS ServerIp,
IN BOOL fSearchInfo,
IN PDNS_NETINFO pNetInfo OPTIONAL
)
/*++
Routine Description:
Create network info given DNS server list.
Used only in Glenn update routines -- kill when they are deleted.
Arguments:
pDnsServers -- IP array of DNS servers
ServerIp -- single IP in list
fSearchInfo -- TRUE if need search info
pNetInfo -- current network info blob to copy search info
from; this field is only relevant if fSearchInfo is TRUE
Return Value:
Ptr to resulting network info.
NULL on failure.
--*/
{
PDNS_NETINFO pnetInfo;
IP4_ARRAY ipArray;
PIP4_ARRAY parray = pDnsServers;
PSEARCH_LIST psearchList;
PWSTR pdomainName;
DWORD flags = 0;
//
// DCR: eliminate search list form this routine
// i believe this routine is only used for query of
// FQDNs (usually in update) and doesn't require
// any default search info
//
// DCR: possibly combine with "BuildForUpdate" routine
// where search info included tacks this on
//
//
// if given single IP, ONLY use it
//
if ( ServerIp )
{
ipArray.AddrCount = 1;
ipArray.AddrArray[0] = ServerIp;
parray = &ipArray;
}
//
// convert server IPs into network info blob
// - simply use update function above to avoid duplicate code
//
pnetInfo = NetInfo_CreateForUpdateIp4(
NULL, // no zone
NULL, // no server name
parray,
0 // no flags
);
if ( !pnetInfo )
{
return( NULL );
}
//
// get search list and primary domain info
// - copy from passed in network info
// OR
// - cut directly out of new netinfo
//
if ( fSearchInfo )
{
if ( pNetInfo )
{
flags = pNetInfo->InfoFlags;
psearchList = SearchList_Copy( pNetInfo->pSearchList );
pdomainName = Dns_CreateStringCopy_W( pNetInfo->pszDomainName );
}
else
{
PDNS_NETINFO ptempNetInfo = GetNetworkInfo();
if ( ptempNetInfo )
{
flags = ptempNetInfo->InfoFlags;
psearchList = ptempNetInfo->pSearchList;
pdomainName = ptempNetInfo->pszDomainName;
ptempNetInfo->pSearchList = NULL;
ptempNetInfo->pszDomainName = NULL;
NetInfo_Free( ptempNetInfo );
}
else
{
psearchList = NULL;
pdomainName = NULL;
}
}
// plug search info into new netinfo blob
pnetInfo->pSearchList = psearchList;
pnetInfo->pszDomainName = pdomainName;
pnetInfo->InfoFlags |= (flags & NINFO_FLAG_DUMMY_SEARCH_LIST);
}
return( pnetInfo );
}
#endif
//
// NetInfo building utilities
//
// DNS server reachability routines
//
// These are used to build netinfo that has unreachable DNS
// servers screened out of the list.
//
BOOL
IsReachableDnsServer(
IN PDNS_NETINFO pNetInfo,
IN PDNS_ADAPTER pAdapter,
IN IP4_ADDRESS Ip4Addr
)
/*++
Routine Description:
Determine if DNS server is reachable.
Arguments:
pNetInfo -- network info blob
pAdapter -- struct with list of DNS servers
Ip4Addr -- DNS server address to test for reachability
Return Value:
TRUE if DNS server is reachable.
FALSE otherwise.
--*/
{
DWORD interfaceIndex;
DNS_STATUS status;
DNSDBG( NETINFO, (
"Enter IsReachableDnsServer( %p, %p, %08x )\n",
pNetInfo,
pAdapter,
Ip4Addr ));
DNS_ASSERT( pNetInfo && pAdapter );
//
// DCR: should do reachablity once on netinfo build
//
// DCR: reachability test can be smarter
// - reachable if same subnet as adapter IP
// question: multiple IPs?
// - reachable if same subnet as previous reachable IP
// question: can tell if same subnet?
//
// DCR: reachability on multi-homed connected
// - if send on another interface, does that interface
// "seem" to be connected
// probably see if
// - same subnet as this inteface
// question: multiple IPs
// - or share DNS servers in common
// question: just let server go, this doesn't work if
// the name is not the same
//
//
// if only one interface, assume reachability
//
if ( pNetInfo->AdapterCount <= 1 )
{
DNSDBG( SEND, (
"One interface, assume reachability!\n" ));
return( TRUE );
}
//
// check if server IP is reachable on its interface
//
status = IpHelp_GetBestInterface(
Ip4Addr,
& interfaceIndex );
if ( status != NO_ERROR )
{
DNS_ASSERT( FALSE );
DNSDBG( ANY, (
"GetBestInterface() failed! %d\n",
status ));
return( TRUE );
}
if ( pAdapter->InterfaceIndex != interfaceIndex )
{
DNSDBG( NETINFO, (
"IP %s on interface %d is unreachable!\n"
"\tsend would be on interface %d\n",
IP_STRING( Ip4Addr ),
pAdapter->InterfaceIndex,
interfaceIndex ));
return( FALSE );
}
return( TRUE );
}
BOOL
IsDnsReachableOnAlternateInterface(
IN PDNS_NETINFO pNetInfo,
IN DWORD InterfaceIndex,
IN PDNS_ADDR pAddr
)
/*++
Routine Description:
Determine if IP address is reachable on adapter.
This function determines whether DNS IP can be reached
on the interface that the stack indicates, when that
interface is NOT the one containing the DNS server.
We need this so we catch the multi-homed CONNECTED cases
where a DNS server is still reachable even though the
interface the stack will send on is NOT the interface for
the DNS server.
Arguments:
pNetInfo -- network info blob
Interface -- interface stack will send to IP on
pAddr -- DNS server address to test for reachability
Return Value:
TRUE if DNS server is reachable.
FALSE otherwise.
--*/
{
PDNS_ADAPTER padapter = NULL;
PDNS_ADDR_ARRAY pserverArray;
DWORD i;
PIP4_ARRAY pipArray;
PIP4_ARRAY psubnetArray;
DWORD ipCount;
IP4_ADDRESS ip4;
DNSDBG( NETINFO, (
"Enter IsDnsReachableOnAlternateInterface( %p, %d, %08x )\n",
pNetInfo,
InterfaceIndex,
pAddr ));
//
// find DNS adapter for interface
//
for( i=0; i<pNetInfo->AdapterCount; i++ )
{
padapter = NetInfo_GetAdapterByIndex( pNetInfo, i );
if ( padapter->InterfaceIndex != InterfaceIndex )
{
padapter = NULL;
continue;
}
break;
}
if ( !padapter )
{
DNSDBG( ANY, (
"WARNING: indicated send inteface %d NOT in netinfo!\n",
InterfaceIndex ));
return FALSE;
}
//
// success conditions:
// 1) DNS IP matches IP of DNS server for send interface
// 2) DNS IP is on subnet of IP of send interface
// 3) DNS IP4 is same default class subnet of send interface
//
// if either of these is TRUE then either
// - there is misconfiguration (not our problem)
// OR
// - there's a somewhat unlikely condition of a default network address
// being subnetted in such a way that it appears on two adapters for
// the machine but is not connected and routeable
// OR
// - these interfaces are connected and we can safely send on them
//
//
// #3 is the issue of multiple adapters in the same (corporate) name space
// example:
// adapter 1 -- default gateway
// IP 157.59.1.1
// DNS 158.10.1.1
//
// adapter 2
// IP 157.59.7.9
// DNS 159.65.7.8 -- send interface adapter 1
//
// adapter 3
// IP 159.57.12.3
// DNS 157.59.134.7 -- send interface adapter 1
//
// adapter 4
// IP 196.12.13.3
// DNS 200.59.73.2
//
// From GetBestInterface, adapter 1, (default gateway) is given as send interface
// for adapter 2 and 3's DNS servers.
//
// For adapter #2, it's DNS is NOT in adapter1's list, but it's IP shares the same
// class B network as adapter 1. It is unlikely that the subnetting is such that
// it's DNS is not reachable through adapter 1.
//
// For adapter #3, it's DNS is NOT in adapter1's list, but it's DNS is on the same
// class B network as adapter 1's interface. Again it's extremely unlikely it is
// not reachable.
//
// For adapter #4, however, it's plain that there's no connection. Neither it's IP
// nor DNS share default network with adapter 1. So send -- which will go out -- adapter
// #1 has a high likelyhood of being to a disjoint network and being unreturnable.
//
//
if ( DnsAddrArray_ContainsAddr(
padapter->pDnsAddrs,
pAddr,
DNSADDR_MATCH_ADDR ) )
{
DNSDBG( NETINFO, (
"DNS server %p also DNS server on send interface %d\n",
pAddr,
InterfaceIndex ));
return( TRUE );
}
//
// DCR: should do subnet matching on IPs
// if DNS server is for one interface with IP on same subnet as IP
// of the interface you'll send on, then it should be kosher
//
//
// test for subnet match for IP4 addrs
//
// FIX6: subnet matching fixup for new subnet info
// DCR: encapsulate subnet matching -- local subnet test
//
// FIX6: local subnet matching on IP6
//
#if SUB4NET
ip4 = DnsAddr_GetIp4( pAddr );
if ( ip4 != BAD_IP4_ADDR )
{
pipArray = padapter->pIp4Addrs;
psubnetArray = padapter->pIp4SubnetMasks;
if ( !pipArray ||
!psubnetArray ||
(ipCount = pipArray->AddrCount) != psubnetArray->AddrCount )
{
DNSDBG( ANY, ( "WARNING: missing or invalid interface IP\\subnet info!\n" ));
DNS_ASSERT( FALSE );
return( FALSE );
}
for ( i=0; i<ipCount; i++ )
{
IP4_ADDRESS subnet = psubnetArray->AddrArray[i];
if ( (subnet & ip4) == (subnet & pipArray->AddrArray[i]) )
{
DNSDBG( NETINFO, (
"DNS server %08x on subnet of IP for send interface %d\n"
"\tip = %08x, subnet = %08x\n",
ip4,
InterfaceIndex,
pipArray->AddrArray[i],
subnet ));
return( TRUE );
}
}
}
#endif
return( FALSE );
}
DNS_STATUS
StrikeOutUnreachableDnsServers(
IN OUT PDNS_NETINFO pNetInfo
)
/*++
Routine Description:
Eliminate unreachable DNS servers from the list.
Arguments:
pNetInfo -- DNS netinfo to fix up
Return Value:
ERROR_SUCCESS if successful
--*/
{
DNS_STATUS status;
DWORD validServers;
PDNS_ADAPTER padapter;
PDNS_ADDR_ARRAY pserverArray;
DWORD adapterIfIndex;
DWORD serverIfIndex;
DWORD i;
DWORD j;
DNSDBG( NETINFO, (
"Enter StrikeOutUnreachableDnsServers( %p )\n",
pNetInfo ));
DNS_ASSERT( pNetInfo );
//
// if only one interface, assume reachability
//
if ( pNetInfo->AdapterCount <= 1 )
{
DNSDBG( NETINFO, (
"One interface, assume reachability!\n" ));
return( TRUE );
}
//
// loop through adapters
//
for( i=0; i<pNetInfo->AdapterCount; i++ )
{
BOOL found4;
BOOL found6;
BOOL found6NonDefault;
padapter = NetInfo_GetAdapterByIndex( pNetInfo, i );
// ignore this adapter because there are no DNS
// servers configured?
if ( padapter->InfoFlags & AINFO_FLAG_IGNORE_ADAPTER )
{
continue;
}
//
// test all adapter's DNS servers for reachability
//
// note: currently save no server specific reachability,
// so if any server reachable, proceed;
// also if iphelp fails just assume reachability and proceed,
// better timeouts then not reaching server we can reach
//
adapterIfIndex = padapter->InterfaceIndex;
validServers = 0;
//
// FIX6: need GetBestInteface for IP6
//
found4 = FALSE;
found6 = FALSE;
found6NonDefault = FALSE;
pserverArray = padapter->pDnsAddrs;
for ( j=0; j<pserverArray->AddrCount; j++ )
{
PDNS_ADDR paddr = &pserverArray->AddrArray[j];
IP4_ADDRESS ip4;
ip4 = DnsAddr_GetIp4( paddr );
//
// IP6
//
if ( ip4 == BAD_IP4_ADDR )
{
found6 = TRUE;
if ( !DnsAddr_IsIp6DefaultDns( paddr ) )
{
found6NonDefault = TRUE;
}
continue;
}
//
// IP4 server
//
found4 = TRUE;
serverIfIndex = 0; // prefix happiness
status = IpHelp_GetBestInterface(
ip4,
& serverIfIndex );
if ( status == ERROR_NETWORK_UNREACHABLE )
{
DNSDBG( NETINFO, (
"GetBestInterface() NETWORK_UNREACH for server %s\n",
IP_STRING(ip4) ));
continue;
}
if ( status != NO_ERROR )
{
DNSDBG( ANY, (
"GetBestInterface() failed! %d\n",
status ));
//DNS_ASSERT( FALSE );
validServers++;
break;
//continue;
}
// server is reachable
// - queried on its adapter?
// - reachable through loopback
//
// DCR: tag unreachable servers individually
if ( serverIfIndex == adapterIfIndex ||
serverIfIndex == 1 )
{
validServers++;
break;
//continue;
}
// server can be reached on query interface
if ( IsDnsReachableOnAlternateInterface(
pNetInfo,
serverIfIndex,
paddr ) )
{
validServers++;
break;
//continue;
}
}
//
// mark adapter if no reachable servers found
//
// => if no servers or IP4 tested and failed, ignore the adapter
// => if only IP6 default server, mark, we'll use but not
// continue on adapter after NAME_ERROR
// =>
//
// - any IP6 will be considered "found" (until get explicit test)
// BUT if we test IP4 on that interface, then it's status wins
//
// DCR: alternative to ignoring unreachable
// - tag as unreachable
// - don't send to it on first pass
// - don't continue name error on unreachable
// (it would count as "heard from" when send.c routine
// works back through)
if ( validServers == 0 )
{
if ( found4 || !found6 )
{
padapter->InfoFlags |= (AINFO_FLAG_IGNORE_ADAPTER |
AINFO_FLAG_SERVERS_UNREACHABLE);
DNSDBG( NETINFO, (
"No reachable servers on interface %d\n"
"\tthis adapter (%p) ignored in DNS list!\n",
adapterIfIndex,
padapter ));
}
else if ( !found6NonDefault )
{
padapter->InfoFlags |= AINFO_FLAG_SERVERS_IP6_DEFAULT;
DNSDBG( NETINFO, (
"Only IP6 default servers on interface %d\n"
"\twill not continue on this adapter after response.\n",
adapterIfIndex,
padapter ));
}
}
}
return ERROR_SUCCESS;
}
//
// Network info caching\state routines
//
BOOL
InitNetworkInfo(
VOID
)
/*++
Routine Description:
Initialize network info.
Arguments:
None
Return Value:
None
--*/
{
//
// standard up-n-running path -- allows cheap runtime check
//
if ( g_NetInfoCacheLockInitialized &&
g_NetInfoBuildLockInitialized )
{
return TRUE;
}
//
// if netinfo not initialzied
//
LOCK_GENERAL();
g_pNetInfo = NULL;
if ( !g_NetInfoCacheLockInitialized )
{
g_NetInfoCacheLockInitialized =
( RtlInitializeCriticalSection( &g_NetInfoCacheLock ) == NO_ERROR );
}
if ( !g_NetInfoBuildLockInitialized )
{
g_NetInfoBuildLockInitialized =
( TimedLock_Initialize( &g_NetInfoBuildLock, 5000 ) == NO_ERROR );
}
UNLOCK_GENERAL();
return ( g_NetInfoCacheLockInitialized && g_NetInfoBuildLockInitialized );
}
VOID
CleanupNetworkInfo(
VOID
)
/*++
Routine Description:
Initialize network info.
Arguments:
None
Return Value:
None
--*/
{
LOCK_GENERAL();
NetInfo_MarkDirty();
if ( g_NetInfoBuildLockInitialized )
{
TimedLock_Cleanup( &g_NetInfoBuildLock );
}
if ( g_NetInfoCacheLockInitialized )
{
DeleteCriticalSection( &g_NetInfoCacheLock );
}
UNLOCK_GENERAL();
}
//
// Read config from resolver
//
PDNS_NETINFO
UpdateDnsConfig(
VOID
)
/*++
Routine Description:
Update DNS configuration.
This includes entire config
- flat registry DWORD\BOOL globals
- netinfo list
Arguments:
None
Return Value:
Ptr to network info blob.
--*/
{
DNS_STATUS status = ERROR_SUCCESS;
PDNS_NETINFO pnetworkInfo = NULL;
PDNS_GLOBALS_BLOB pglobalsBlob = NULL;
DNSDBG( TRACE, ( "UpdateDnsConfig()\n" ));
// DCR_CLEANUP: RPC TryExcept should be in RPC client library
RpcTryExcept
{
R_ResolverGetConfig(
NULL, // default handle
g_ConfigCookie,
& pnetworkInfo,
& pglobalsBlob
);
}
RpcExcept( DNS_RPC_EXCEPTION_FILTER )
{
status = RpcExceptionCode();
}
RpcEndExcept
if ( status != ERROR_SUCCESS )
{
DNSDBG( RPC, (
"R_ResolverGetConfig() RPC failed status = %d\n",
status ));
return NULL;
}
//
// DCR: save other config info here
// - flat memcpy of DWORD globals
// - save off cookie (perhaps include as one of them
// - save global copy of pnetworkInfo?
// (the idea being that we just copy it if
// RPC cookie is valid)
//
// - maybe return flags?
// memcpy is cheap but if more expensive config
// then could alert what needs update?
//
//
// DCR: once move, single "update global network info"
// then call it here to save global copy
// but global copy doesn't do much until RPC fails
// unless using cookie
//
// QUESTION: not sure about forcing global build here
// q: is this to be "read config" all
// or just "update config" and then individual
// routines for various pieces of config can
// determine what to do?
//
// note, doing eveything is fine if going to always
// read entire registry on cache failure; if so
// reasonable to push here
//
// if cache-on required for "real time" config, then
// should protect registry DWORD read with reasonable time
// (say read every five\ten\fifteen minutes?)
//
// perhaps NO read here, but have DWORD reg read update
// routine that called before registry reread when
// building adapter list in registry; then skip this
// step in cache
//
//
// copy in config
//
if ( pglobalsBlob )
{
RtlCopyMemory(
& DnsGlobals,
pglobalsBlob,
sizeof(DnsGlobals) );
MIDL_user_free( pglobalsBlob );
}
IF_DNSDBG( RPC )
{
DnsDbg_NetworkInfo(
"Network Info returned from cache:",
pnetworkInfo );
}
return pnetworkInfo;
}
//
// Public netinfo routine
//
PDNS_NETINFO
NetInfo_Get(
IN DWORD Flag,
IN DWORD AcceptLocalCacheTime OPTIONAL
)
/*++
Routine Description:
Read DNS network info from registry.
This is in process, limited caching version.
Note, this is macro'd as GetNetworkInfo() with parameters
NetInfo_Get( FALSE, TRUE ) throughout dnsapi code.
Arguments:
Flag -- flag; read order and IP
NIFLAG_GET_LOCAL_ADDRS
NIFLAG_FORCE_REGISTRY_READ
NIFLAG_READ_RESOLVER_FIRST
NIFLAG_READ_RESOLVER
NIFLAG_READ_PROCESS_CACHE
AcceptLocalCacheTime -- acceptable cache time on in process copy
Return Value:
ERROR_SUCCESS if successful.
Error code on failure.
--*/
{
PDNS_NETINFO pnetInfo = NULL;
PDNS_NETINFO poldNetInfo = NULL;
BOOL fcacheLock = FALSE;
BOOL fbuildLock = FALSE;
BOOL fcacheable = TRUE;
DNSDBG( NETINFO, (
"NetInfo_Get( %08x, %d )\n",
Flag,
AcceptLocalCacheTime ));
//
// init netinfo locks\caching
//
if ( !InitNetworkInfo() )
{
return NULL;
}
//
// get netinfo from one of several sources
// try
// - very recent local cached copy
// - RPC copy from resolver
// - build new
//
// note the locking model
// two SEPARATE locks
// - cache lock, for very quick, very local access
// to cached copy
// - build lock, for remote process access to cached (resolver)
// or newly built netinfo
//
// the locking hierarchy
// - build lock
// - cache lock (maybe taken inside build lock)
//
//
// Locking implementation note:
//
// The reason for the two locks is that when calling down for netinfo
// build it is possible to have a circular dependency.
//
// Here's the deadlock scenario if we have a single lock handling
// build and caching:
// - call into resolver and down to iphlpapi
// - iphlpapi RPC's into MPR (router service)
// - RtrMgr calls GetHostByName() which ends up in a
// iphlpapi!GetBestInterface call which in turn calls
// Mprapi!IsRouterRunning (which is an RPC to mprdim).
// - Mprdim is blocked on a CS which is held by a thread waiting
// for a demand-dial disconnect to complete - this is completely
// independent of 1.
// - Demand-dial disconnect is waiting for ppp to finish graceful
// termination.
// - PPP is waiting for dns to return from DnsSetConfigDword
// - DnsSetConfigDword, sets, alerts the cache, then calls
// NetInfo_MarkDirty()
// - NetInfo_MarkDirty() is waiting on CS to access the netinfo global.
//
// Now, this could be avoided by changing MarkDirty() to safely set some
// dirty bit (interlock). The build function would have to check the bit
// and go down again if it was set.
//
// However, there'd still be a chance that the call down to iphlpapi, could
// depend under some odd circumstance on some service that came back through
// the resolver. And the bottom line is that the real distinction is not
// between caching and marking cache dirty. It's between completely local
// cache get\set\clear activity, which can be safely overloaded on the general CS,
// AND the longer time, multi-service dependent building operation. So
// separate CS for both is correct.
//
if ( !(Flag & NIFLAG_FORCE_REGISTRY_READ)
&&
!g_DnsTestMode )
{
//
// RPC to resolver
//
if ( Flag & NIFLAG_READ_RESOLVER_FIRST )
{
// DCR: this could present "cookie" of existing netinfo
// and only get new if "cookie" is old, though the
// cost of that versus local copy seems small since
// still must do RPC and allocations -- only the copy
// for RPC on the resolver side is saved
fbuildLock = LOCK_NETINFO_BUILD();
if ( !fbuildLock )
{
goto Unlock;
}
pnetInfo = UpdateDnsConfig();
if ( pnetInfo )
{
DNSDBG( TRACE, (
"Netinfo read from resolver.\n" ));
goto CacheCopy;
}
}
//
// use in-process cached copy?
//
if ( Flag & NIFLAG_READ_PROCESS_CACHE )
{
DWORD timeout = NETINFO_CACHE_TIMEOUT;
LOCK_NETINFO_CACHE();
fcacheLock = TRUE;
if ( AcceptLocalCacheTime )
{
timeout = AcceptLocalCacheTime;
}
// check if valid copy cached in process
if ( g_pNetInfo &&
(g_pNetInfo->TimeStamp + timeout > Dns_GetCurrentTimeInSeconds()) )
{
pnetInfo = NetInfo_Copy( g_pNetInfo );
if ( pnetInfo )
{
DNSDBG( TRACE, (
"Netinfo found in process cache.\n" ));
goto Unlock;
}
}
UNLOCK_NETINFO_CACHE();
fcacheLock = FALSE;
}
//
// last chance on resolver
//
if ( !fbuildLock && (Flag & NIFLAG_READ_RESOLVER) )
{
fbuildLock = LOCK_NETINFO_BUILD();
if ( !fbuildLock )
{
goto Unlock;
}
pnetInfo = UpdateDnsConfig();
if ( pnetInfo )
{
DNSDBG( TRACE, (
"Netinfo read from resolver.\n" ));
goto CacheCopy;
}
}
}
//
// build fresh network info
//
DNS_ASSERT( !fcacheLock );
if ( !fbuildLock )
{
fbuildLock = LOCK_NETINFO_BUILD();
if ( !fbuildLock )
{
goto Unlock;
}
}
fcacheable = (Flag & NIFLAG_GET_LOCAL_ADDRS);
pnetInfo = NetInfo_Build( fcacheable );
if ( !pnetInfo )
{
goto Unlock;
}
CacheCopy:
//
// update cached copy
// - but not if built without local IPs;
// resolver copy always contains local IPs
//
if ( fcacheable )
{
if ( !fcacheLock )
{
LOCK_NETINFO_CACHE();
fcacheLock = TRUE;
}
poldNetInfo = g_pNetInfo;
g_pNetInfo = NetInfo_Copy( pnetInfo );
}
Unlock:
if ( fcacheLock )
{
UNLOCK_NETINFO_CACHE();
}
if ( fbuildLock )
{
UNLOCK_NETINFO_BUILD();
}
NetInfo_Free( poldNetInfo );
DNSDBG( TRACE, (
"Leave: NetInfo_Get( %p )\n",
pnetInfo ));
return( pnetInfo );
}
VOID
NetInfo_MarkDirty(
VOID
)
/*++
Routine Description:
Mark netinfo dirty so force reread.
Arguments:
None
Return Value:
None
--*/
{
PDNS_NETINFO pold;
DNSDBG( NETINFO, ( "NetInfo_MarkDirty()\n" ));
//
// init netinfo locks\caching
//
if ( !InitNetworkInfo() )
{
return;
}
//
// dump global network info to force reread
//
// since the resolve is always notified by DnsSetDwordConfig()
// BEFORE entering this function, the resolve should always be
// providing before we are in this function; all we need to do
// is insure that cached copy is dumped
//
LOCK_NETINFO_CACHE();
pold = g_pNetInfo;
g_pNetInfo = NULL;
UNLOCK_NETINFO_CACHE();
NetInfo_Free( pold );
}
PDNS_NETINFO
NetInfo_Build(
IN BOOL fGetIpAddrs
)
/*++
Routine Description:
Build network info blob from registry.
This is the FULL recreate function.
Arguments:
fGetIpAddrs -- TRUE to include local IP addrs for each adapter
(currently ignored -- always get all the info)
Return Value:
ERROR_SUCCESS if successful.
Error code on failure.
--*/
{
REG_SESSION regSession;
PREG_SESSION pregSession = NULL;
PDNS_NETINFO pnetInfo = NULL;
PDNS_ADAPTER pdnsAdapter = NULL;
DNS_STATUS status = NO_ERROR;
DWORD count;
DWORD createdAdapterCount = 0;
BOOL fuseIp;
PIP_ADAPTER_ADDRESSES padapterList = NULL;
PIP_ADAPTER_ADDRESSES padapter = NULL;
DWORD value;
PREG_GLOBAL_INFO pregInfo = NULL;
REG_GLOBAL_INFO regInfo;
REG_ADAPTER_INFO regAdapterInfo;
DWORD flag;
BOOL fhaveDnsServers = FALSE;
DNSDBG( TRACE, ( "\n\n\nNetInfo_Build()\n\n" ));
//
// open the registry
//
pregSession = &regSession;
status = Reg_OpenSession(
pregSession,
0,
0 );
if ( status != ERROR_SUCCESS )
{
status = DNS_ERROR_NO_DNS_SERVERS;
goto Cleanup;
}
//
// read global registry info
//
pregInfo = &regInfo;
status = Reg_ReadGlobalInfo(
pregSession,
pregInfo
);
if ( status != ERROR_SUCCESS )
{
status = DNS_ERROR_NO_DNS_SERVERS;
goto Cleanup;
}
//
// get adapter\address info from IP help
//
// note: always getting IP addresses
// - for multi-adapter need for routing
// - need for local lookups
// (might as well just include)
//
// DCR: could skip include when RPCing to client for
// query\update that does not require
//
flag = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST;
#if 0
if ( !fGetIpAddrs )
{
flag |= GAA_FLAG_SKIP_UNICAST;
}
#endif
padapterList = IpHelp_GetAdaptersAddresses(
PF_UNSPEC,
flag );
if ( !padapterList )
{
status = GetLastError();
DNS_ASSERT( status != NO_ERROR );
goto Cleanup;
}
// count up the active adapters
padapter = padapterList;
count = 0;
while ( padapter )
{
count++;
padapter = padapter->Next;
}
//
// allocate net info blob
// allocate DNS server IP array
//
pnetInfo = NetInfo_Alloc( count );
if ( !pnetInfo )
{
status = DNS_ERROR_NO_MEMORY;
goto Cleanup;
}
//
// loop through adapters -- build network info for each
//
padapter = padapterList;
while ( padapter )
{
DWORD adapterFlags = 0;
PWSTR pnameAdapter = NULL;
PWSTR padapterDomainName = NULL;
PDNS_ADDR_ARRAY pserverArray = NULL;
PDNS_ADDR_ARRAY plocalArray = NULL;
//
// read adapter registry info
//
// DCR: can skip adapter domain name read
// it's in IP help adapter, just need policy override
// DCR: can skip DDNS read, and register read
// again, except for policy overrides
//
// DCR: could just have an "ApplyPolicyOverridesToAdapterInfo()" sort
// of function and get the rest from
//
pnameAdapter = Dns_StringCopyAllocate(
padapter->AdapterName,
0,
DnsCharSetAnsi,
DnsCharSetUnicode );
if ( !pnameAdapter )
{
status = DNS_ERROR_NO_MEMORY;
goto Cleanup;
}
status = Reg_ReadAdapterInfo(
pnameAdapter,
pregSession,
& regInfo, // policy adapter info
& regAdapterInfo // receives reg info read
);
if ( status != NO_ERROR )
{
status = Reg_DefaultAdapterInfo(
& regAdapterInfo,
& regInfo,
padapter );
if ( status != NO_ERROR )
{
DNS_ASSERT( FALSE );
goto Skip;
}
}
// translate results into flags
if ( regAdapterInfo.fRegistrationEnabled )
{
adapterFlags |= AINFO_FLAG_REGISTER_IP_ADDRESSES;
}
if ( regAdapterInfo.fRegisterAdapterName )
{
adapterFlags |= AINFO_FLAG_REGISTER_DOMAIN_NAME;
}
// use domain name?
// - if disable on per adapter basis, then it's dead
if ( regAdapterInfo.fQueryAdapterName )
{
padapterDomainName = regAdapterInfo.pszAdapterDomainName;
regAdapterInfo.pszAdapterDomainName = NULL;
}
// DCR: could get DDNS and registration for adapter
// set flag on DHCP adapters
if ( padapter->Flags & IP_ADAPTER_DHCP_ENABLED )
{
adapterFlags |= AINFO_FLAG_IS_DHCP_CFG_ADAPTER;
}
//
// get adapter's IP addresses
//
fuseIp = fGetIpAddrs;
if ( fuseIp )
{
status = IpHelp_ReadAddrsFromList(
padapter->FirstUnicastAddress,
TRUE, // unicast addrs
0, // no screening
0, // no screening
& plocalArray, // local addrs
NULL, // IP6 only
NULL, // IP4 only
NULL, // no IP6 count
NULL // no IP4 count
);
if ( status != NO_ERROR )
{
goto Cleanup;
}
}
#if 0
//
// get per-adapter information from the iphlpapi.dll.
// -- autonet
//
// FIX6: do we need autonet info?
pserverArray = NULL;
status = IpHelp_GetPerAdapterInfo(
padapter->Index,
& pperAdapterInfo );
if ( status == NO_ERROR )
{
if ( pperAdapterInfo->AutoconfigEnabled &&
pperAdapterInfo->AutoconfigActive )
{
adapterFlags |= AINFO_FLAG_IS_AUTONET_ADAPTER;
}
}
#endif
//
// build DNS list
//
if ( padapter->FirstDnsServerAddress )
{
status = IpHelp_ReadAddrsFromList(
padapter->FirstDnsServerAddress,
FALSE, // not unicast addrs
0, // no screening
0, // no screening
& pserverArray, // get combined list
NULL, // no IP6 only
NULL, // no IP4 only
NULL, // no IP6 count
NULL // no IP4 count
);
if ( status != NO_ERROR )
{
goto Cleanup;
}
fhaveDnsServers = TRUE;
}
else
{
#if 0
//
// note: this feature doesn't work very well
// it kicks in when cable unplugged and get into auto-net
// scenario ... and then can bring in an unconfigured
// DNS server and give us long timeouts
//
// DCR: pointing to local DNS server
// a good approach would be to point to local DNS on ALL
// adapters when we fail to find ANY DNS servers at all
//
//
// if no DNS servers found -- use loopback if on DNS server
//
if ( g_IsDnsServer )
{
pserverArray = DnsAddrArray_Create( 1 );
if ( !pserverArray )
{
goto Skip;
}
DnsAddrArray_InitSingleWithIp4(
pserverArray,
DNS_NET_ORDER_LOOPBACK );
}
#endif
}
//
// build adapter info
//
// optionally add IP and subnet list; note this is
// direct add of data (not alloc\copy) so clear pointers
// after to skip free
//
// DCR: no failure case on adapter create failure???
//
// DCR: when do we need non-server adapters? for mcast?
//
// DCR: we could create Adapter name in unicode (above) then
// just copy it in;
// DCR: could preserve adapter domain name in blob, and NULL
// out the string in regAdapterInfo
//
if ( pserverArray || plocalArray )
{
PDNS_ADAPTER pnewAdapter = &pnetInfo->AdapterArray[ createdAdapterCount ];
AdapterInfo_Init(
pnewAdapter,
TRUE, // zero init
adapterFlags,
pnameAdapter,
padapterDomainName,
plocalArray,
pserverArray
);
pnewAdapter->InterfaceIndex = padapter->IfIndex;
pnetInfo->AdapterCount = ++createdAdapterCount;
pnameAdapter = NULL;
padapterDomainName = NULL;
plocalArray = NULL;
pserverArray = NULL;
}
Skip:
//
// cleanup adapter specific data
//
// note: no free of pserverArray, it IS the
// ptempArray buffer that we free at the end
//
Reg_FreeAdapterInfo(
&regAdapterInfo,
FALSE // don't free blob, it is on stack
);
if ( pnameAdapter );
{
FREE_HEAP( pnameAdapter );
}
if ( padapterDomainName );
{
FREE_HEAP( padapterDomainName );
}
if ( pserverArray );
{
DnsAddrArray_Free( pserverArray );
}
if ( plocalArray )
{
DnsAddrArray_Free( plocalArray );
}
// get next adapter
// reset status, so failure on the last adapter is not
// seen as global failure
padapter = padapter->Next;
status = ERROR_SUCCESS;
}
//
// no DNS servers?
// - use loopback if we are on MS DNS
// - otherwise note netinfo useless for lookup
//
// when self-pointing:
// - setup all adapters so we preserve adapter domain names for lookup
// - mark adapters as auto-loopback; send code will then avoid continuing
// query on other adapters
//
// note, i specifically choose this approach rather than configuring on any
// serverless adapter even if other adapters have DNS servers
// this avoids two problems:
// - server is poorly configured but CAN answer and fast local resolution
// blocks resolution through real DNS servers
// - network edge scenarios where DNS may be out-facing, but DNS client
// resolution may be intentionally desired to be only internal (admin network)
// in both cases i don't want to "pop" local DNS into the mix when it is unintended.
// when it is intended the easy workaround is to configure it explicitly
//
if ( !fhaveDnsServers )
{
if ( g_IsDnsServer )
{
DWORD i;
for ( i=0; i<pnetInfo->AdapterCount; i++ )
{
PDNS_ADAPTER padapt = NetInfo_GetAdapterByIndex( pnetInfo, i );
PDNS_ADDR_ARRAY pserverArray = NULL;
pserverArray = DnsAddrArray_Create( 1 );
if ( !pserverArray )
{
status = DNS_ERROR_NO_MEMORY;
goto Cleanup;
}
DnsAddrArray_InitSingleWithIp4(
pserverArray,
DNS_NET_ORDER_LOOPBACK );
padapt->pDnsAddrs = pserverArray;
padapt->InfoFlags &= ~AINFO_FLAG_IGNORE_ADAPTER;
padapt->InfoFlags |= AINFO_FLAG_SERVERS_AUTO_LOOPBACK;
}
}
else
{
pnetInfo->InfoFlags |= NINFO_FLAG_NO_DNS_SERVERS;
}
}
//
// eliminate unreachable DNS servers
//
if ( g_ScreenUnreachableServers )
{
StrikeOutUnreachableDnsServers( pnetInfo );
}
//
// build search list for network info
// - skip if no active adapters found
//
// DCR: shouldn't build search list?
//
// DCR: only build if actually read search list
//
if ( pnetInfo->AdapterCount )
{
pnetInfo->pSearchList = SearchList_Build(
regInfo.pszPrimaryDomainName,
pregSession,
NULL, // no explicit key
pnetInfo,
regInfo.fUseNameDevolution
);
if ( !pnetInfo->pSearchList )
{
status = DNS_ERROR_NO_MEMORY;
goto Cleanup;
}
}
//
// host and domain name info
//
pnetInfo->pszDomainName = Dns_CreateStringCopy_W( regInfo.pszPrimaryDomainName );
pnetInfo->pszHostName = Dns_CreateStringCopy_W( regInfo.pszHostName );
// timestamp
pnetInfo->TimeStamp = Dns_GetCurrentTimeInSeconds();
//
// set default server priorities
//
NetInfo_ResetServerPriorities( pnetInfo, FALSE );
Cleanup:
// free allocated reg info
Reg_FreeGlobalInfo(
pregInfo,
FALSE // don't free blob, it is on stack
);
if ( padapterList )
{
FREE_HEAP( padapterList );
}
if ( pnetInfo &&
pnetInfo->AdapterCount == 0 )
{
status = DNS_ERROR_NO_DNS_SERVERS;
}
// close registry session
Reg_CloseSession( pregSession );
if ( status != ERROR_SUCCESS )
{
NetInfo_Free( pnetInfo );
DNSDBG( TRACE, (
"Leave: NetInfo_Build()\n"
"\tstatus = %d\n",
status ));
SetLastError( status );
return( NULL );
}
IF_DNSDBG( NETINFO2 )
{
DnsDbg_NetworkInfo(
"New Netinfo:",
pnetInfo );
}
DNSDBG( TRACE, (
"\nLeave: NetInfo_Build()\n\n\n"
"\treturning fresh built network info (%p)\n",
pnetInfo ));
return( pnetInfo );
}
//
// Local address list
//
DWORD
netinfo_AddrFlagForConfigFlag(
IN DWORD ConfigFlag
)
/*++
Routine Description:
Build the DNS_ADDR flag for a given config flag.
Arguments:
ConfigFlag -- config flag we're given.
Return Value:
Flag in DNS_ADDR. Note this covers only the bits in DNSADDR_FLAG_TYPE_MASK
not the entire flag.
--*/
{
DWORD flag = 0;
if ( ConfigFlag & DNS_CONFIG_FLAG_ADDR_PUBLIC )
{
flag |= DNSADDR_FLAG_PUBLIC;
}
if ( ConfigFlag & DNS_CONFIG_FLAG_ADDR_CLUSTER )
{
flag |= DNSADDR_FLAG_TRANSIENT;
}
return flag;
}
BOOL
netinfo_LocalAddrScreen(
IN PDNS_ADDR pAddr,
IN PDNS_ADDR pScreenAddr
)
/*++
Routine Description:
Check DNS_ADDR against screening critera for local addr build.
Arguments:
pAddr -- address to screen
pScreenAddr -- screening info
Return Value:
TRUE if local addr passes screen.
FALSE otherwise.
--*/
{
DWORD family = DnsAddr_Family( pScreenAddr );
DWORD flags;
// screen family
if ( family &&
family != DnsAddr_Family(pAddr) )
{
return FALSE;
}
// screen flags
// - exact match on address type flag bits
return ( (pAddr->Flags & pScreenAddr->DnsAddrFlagScreeningMask)
== pScreenAddr->Flags);
}
DNS_STATUS
netinfo_ReadLocalAddrs(
IN OUT PDNS_ADDR_ARRAY pAddrArray,
IN PDNS_NETINFO pNetInfo,
IN PDNS_ADAPTER pSingleAdapter, OPTIONAL
IN OUT PDNS_ADDR pScreenAddr,
IN DWORD AddrFlags,
IN DWORD AddrMask,
IN DWORD ReadCount
)
/*++
Routine Description:
Create IP array of DNS servers from network info.
Arguments:
pAddrArray -- local address array being built
pNetInfo -- DNS net adapter list to convert
pSingleAdapter -- just do this one adapter
pScreenAddr -- address screening blob;
note: there's no true OUT info, but the screen addr
is altered to match AddrFlags
AddrFlags -- addr flag we're interested in
ReadCount -- count to read
1 -- just one
MAXDWORD -- all
0 -- all on second pass
Return Value:
NO_ERROR if successful.
Otherwise error code from add.
--*/
{
PDNS_ADDR_ARRAY parray = NULL;
PDNS_ADAPTER padapter;
DNS_STATUS status = NO_ERROR;
DWORD dupFlag = 0;
DWORD screenMask;
DWORD iter;
DNSDBG( TRACE, (
"netinfo_ReadLocalAddrs( %p, %p, %p, %08x, %08x, %d )\n",
pNetInfo,
pSingleAdapter,
pScreenAddr,
AddrFlags,
AddrMask,
ReadCount ));
//
// get DNS_ADDR flag for the address type we're reading
//
// note we have the classic and\or problem
// the addresses are denoted by two flags (from iphelp):
// DNSADDR_FLAG_PUBLIC
// DNSADDR_FLAG_TRANSIENT
//
// but we need both flags and mask to determine all the possible gatherings
// we want to do
//
// right now the DNS_CONFIG_FLAG_X are ORd together to get UNIONS of addresses
// we are willing to accept, but we go through the list multiple times to build
// the list favoring the public over private, non-cluster over cluster
//
// so currently when say you want DNS_CONFIG_PUBLIC you mean public and not-cluster;
// ditto for private; on the other hand when you say DNS_CONFIG_CLUSTER you are
// asking for all cluster (though we could screen on whether PUBLIC, PRIVATE both or
// neither were specified
//
pScreenAddr->Flags = netinfo_AddrFlagForConfigFlag( AddrFlags );
screenMask = DNSADDR_FLAG_TYPE_MASK;
if ( AddrMask != 0 )
{
screenMask = netinfo_AddrFlagForConfigFlag( AddrMask );
}
pScreenAddr->DnsAddrFlagScreeningMask = screenMask;
//
// read count
// = 0 means second pass on list
// -> read all, but do full duplicate screen to skip the
// addresses read on the first pass
if ( ReadCount == 0 )
{
ReadCount = MAXDWORD;
dupFlag = DNSADDR_MATCH_ALL;
}
//
// loop through all adapters
//
for ( iter=0; iter<pNetInfo->AdapterCount; iter++ )
{
padapter = NetInfo_GetAdapterByIndex( pNetInfo, iter );
if ( !pSingleAdapter ||
pSingleAdapter == padapter )
{
status = DnsAddrArray_AppendArrayEx(
pAddrArray,
padapter->pLocalAddrs,
ReadCount, // read address count
0, // family check handled by screening
dupFlag,
netinfo_LocalAddrScreen,
pScreenAddr
);
}
//DNS_ASSERT( status == NO_ERROR );
}
return status;
}
PADDR_ARRAY
NetInfo_CreateLocalAddrArray(
IN PDNS_NETINFO pNetInfo,
IN PWSTR pwsAdapterName, OPTIONAL
IN PDNS_ADAPTER pAdapter, OPTIONAL
IN DWORD Family, OPTIONAL
IN DWORD AddrFlags OPTIONAL
)
/*++
Routine Description:
Create IP array of DNS servers from network info.
Arguments:
pNetInfo -- DNS net adapter list to convert
pwsAdapterName -- specific adapter; NULL for all adapters
pAdapter -- specific adapter; NULL for all adapters
Family -- required specific address family; 0 for any family
AddrFlags -- address selection flags
DNS_CONFIG_FLAG_INCLUDE_CLUSTER
AddrFlagsMask -- mask on selecting flags
Return Value:
Ptr to IP array, if successful
NULL on failure.
--*/
{
PADDR_ARRAY parray = NULL;
DWORD iter;
DWORD countAddrs = 0;
PDNS_ADAPTER padapter;
PDNS_ADAPTER padapterSingle = NULL;
DNS_STATUS status = NO_ERROR;
DNS_ADDR screenAddr;
DNSDBG( TRACE, (
"NetInfo_CreateLocalAddrArray( %p, %S, %p, %d, %08x )\n",
pNetInfo,
pwsAdapterName,
pAdapter,
Family,
AddrFlags ));
//
// get count
//
if ( ! pNetInfo )
{
return NULL;
}
padapterSingle = pAdapter;
if ( pwsAdapterName && !padapterSingle )
{
padapterSingle = NetInfo_GetAdapterByName( pNetInfo, pwsAdapterName );
if ( !padapterSingle )
{
goto Done;
}
}
//
// setup screening addr
//
// if not address flag -- get all types
//
if ( AddrFlags == 0 )
{
AddrFlags = (DWORD)(-1);
}
RtlZeroMemory( &screenAddr, sizeof(screenAddr) );
screenAddr.Sockaddr.sa_family = (WORD)Family;
//
// count addrs
//
// DCR: could count with based on addr info
//
for ( iter=0; iter<pNetInfo->AdapterCount; iter++ )
{
padapter = NetInfo_GetAdapterByIndex( pNetInfo, iter );
if ( !padapterSingle ||
padapterSingle == padapter )
{
countAddrs += DnsAddrArray_GetFamilyCount(
padapter->pLocalAddrs,
Family );
}
}
//
// allocate required array
//
parray = DnsAddrArray_Create( countAddrs );
if ( !parray )
{
status = DNS_ERROR_NO_MEMORY;
goto Done;
}
DNS_ASSERT( parray->MaxCount == countAddrs );
//
// read addrs "in order"
//
// historically gethostbyname() presented addrs
// - one from each adapter
// - then the rest from all adapters
//
// we preserve this, plus order by type
// - public DNS_ELIGIBLE first
// - private (autonet, IP6 local scope stuff)
// - cluster\transient last
//
//
// public (DNS_ELIGIBLE) addrs
// - screen on all flags, specifically don't pick up public cluster addrs
//
if ( AddrFlags & DNS_CONFIG_FLAG_ADDR_PUBLIC )
{
// read first "public" addr of each (or single) adapter
status = netinfo_ReadLocalAddrs(
parray,
pNetInfo,
padapterSingle,
& screenAddr,
DNS_CONFIG_FLAG_ADDR_PUBLIC,
0, // exact match on all flags
1 // read only one address
);
// read the rest of "public" addrs
status = netinfo_ReadLocalAddrs(
parray,
pNetInfo,
padapterSingle,
& screenAddr,
DNS_CONFIG_FLAG_ADDR_PUBLIC,
0, // exact match on all flags
0 // read the rest
);
}
//
// private (non-DNS-publish) addrs (autonet, IP6 local, sitelocal, etc.)
// - screen on all flags, specifically don't pick up private cluster addrs
//
if ( AddrFlags & DNS_CONFIG_FLAG_ADDR_PRIVATE )
{
status = netinfo_ReadLocalAddrs(
parray,
pNetInfo,
padapterSingle,
& screenAddr,
DNS_CONFIG_FLAG_ADDR_PRIVATE,
0, // exact match on all flags
MAXDWORD // read all addrs
);
}
//
// cluster at end
// - only screen on cluster flag as public flag may
// also be set\clear
//
if ( AddrFlags & DNS_CONFIG_FLAG_ADDR_CLUSTER )
{
status = netinfo_ReadLocalAddrs(
parray,
pNetInfo,
padapterSingle,
& screenAddr,
DNS_CONFIG_FLAG_ADDR_CLUSTER,
DNS_CONFIG_FLAG_ADDR_CLUSTER, // any cluster match
MAXDWORD // read all addrs
);
}
Done:
if ( status != NO_ERROR )
{
SetLastError( status );
}
return( parray );
}
//
// Local address list presentation
//
PDNS_ADDR_ARRAY
NetInfo_GetLocalAddrArray(
IN PDNS_NETINFO pNetInfo,
IN PWSTR pwsAdapterName, OPTIONAL
IN DWORD AddrFamily, OPTIONAL
IN DWORD AddrFlags, OPTIONAL
IN BOOL fForce
)
/*++
Routine Description:
Get local addrs as array.
This is a combination NetInfo_Get\ConvertToLocalAddrArray routine.
It's purpose is to simplify getting local address info, while avoiding
costly NetInfo rebuilds where they are unnecessary.
Arguments:
pNetInfo -- existing netinfo to use
pwsAdapterName -- specific adapter name; NULL for all adapters
AddrFamily -- specific address family; 0 for all
AddrFlags -- flags to indicate addrs to consider
DNS_CONFIG_FLAG_INCLUDE_CLUSTER
fForce -- force reread from registry
Return Value:
ERROR_SUCCESS if successful.
Error code on failure.
--*/
{
PDNS_NETINFO pnetInfo = NULL;
PADDR_ARRAY parray = NULL;
DNS_STATUS status = NO_ERROR;
DNSDBG( TRACE, (
"NetInfo_GetLocalAddrArray()\n"
"\tpnetinfo = %p\n"
"\tadapter = %S\n"
"\tfamily = %d\n"
"\tflags = %08x\n"
"\tforce = %d\n",
pNetInfo,
pwsAdapterName,
AddrFamily,
AddrFlags,
fForce
));
//
// get network info to make list from
// - if force, full reread
// - otherwise gethostbyname() scenario
// - accept local caching for very short interval just for perf
// - accept resolver
//
// DCR: force first gethostbyname() call to resolver\registry?
// have to define "first", in a way that's different from netinfo()
// in last second
//
pnetInfo = pNetInfo;
if ( !pnetInfo )
{
DWORD getFlag = NIFLAG_GET_LOCAL_ADDRS;
DWORD timeout;
if ( fForce )
{
getFlag |= NIFLAG_FORCE_REGISTRY_READ;
timeout = 0;
}
else
{
getFlag |= NIFLAG_READ_RESOLVER;
getFlag |= NIFLAG_READ_PROCESS_CACHE;
timeout = 1;
}
pnetInfo = NetInfo_Get(
getFlag,
timeout
);
if ( !pnetInfo )
{
status = DNS_ERROR_NO_TCPIP;
goto Done;
}
}
//
// cluster filter info
// -- check environment variable
//
// DCR: once RnR no longer using myhostent() for gethostbyname()
// then can remove
if ( g_IsServer &&
(AddrFlags & DNS_CONFIG_FLAG_READ_CLUSTER_ENVAR) &&
!(AddrFlags & DNS_CONFIG_FLAG_ADDR_CLUSTER) )
{
ENVAR_DWORD_INFO filterInfo;
Reg_ReadDwordEnvar(
RegIdFilterClusterIp,
&filterInfo );
if ( !filterInfo.fFound ||
!filterInfo.Value )
{
AddrFlags |= DNS_CONFIG_FLAG_ADDR_CLUSTER;
}
}
//
// convert network info to IP4_ARRAY
//
parray = NetInfo_CreateLocalAddrArray(
pnetInfo,
pwsAdapterName,
NULL, // no specific adapter ptr
AddrFamily,
AddrFlags
);
if ( !parray )
{
status = GetLastError();
goto Done;
}
// if no IPs found, return
if ( parray->AddrCount == 0 )
{
DNS_PRINT((
"NetInfo_GetLocalAddrArray() failed: no addrs found\n"
"\tstatus = %d\n" ));
status = DNS_ERROR_NO_TCPIP;
goto Done;
}
IF_DNSDBG( NETINFO )
{
DNS_PRINT(( "Leaving Netinfo_GetLocalAddrArray()\n" ));
DnsDbg_DnsAddrArray(
"Local addr list",
"server",
parray );
}
Done:
// free netinfo built here
if ( pnetInfo != pNetInfo )
{
NetInfo_Free( pnetInfo );
}
if ( status != NO_ERROR )
{
FREE_HEAP( parray );
parray = NULL;
SetLastError( status );
}
return( parray );
}
PIP4_ARRAY
NetInfo_GetLocalAddrArrayIp4(
IN PWSTR pwsAdapterName, OPTIONAL
IN DWORD Flags,
IN BOOL fForce
)
/*++
Routine Description:
Get DNS server list as IP array.
Arguments:
pwsAdapterName -- specific adapter name; NULL for all adapters
Flags -- flags to indicate addrs to consider
DNS_CONFIG_FLAG_X
fForce -- force reread from registry
Return Value:
ERROR_SUCCESS if successful.
Error code on failure.
--*/
{
PADDR_ARRAY parray;
PIP4_ARRAY parray4 = NULL;
DNS_STATUS status = NO_ERROR;
DNSDBG( TRACE, ( "NetInfo_GetLocalAddrArrayIp4()\n" ));
//
// get DNS server list
//
parray = NetInfo_GetLocalAddrArray(
NULL, // no existing netinfo
pwsAdapterName,
AF_INET,
Flags,
fForce );
if ( !parray )
{
goto Done;
}
//
// convert array to IP4 array
//
parray4 = DnsAddrArray_CreateIp4Array( parray );
if ( !parray4 )
{
status = DNS_ERROR_NO_MEMORY;
goto Done;
}
DNS_ASSERT( parray4->AddrCount > 0 );
Done:
DnsAddrArray_Free( parray );
if ( status != NO_ERROR )
{
SetLastError( status );
}
return( parray4 );
}
//
// End netinfo.c
//