/*++ 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 // // Registry info // #define DNS_REG_READ_BUF_SIZE (1000) #define LOCALHOST "127.0.0.1" // // 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 (10) // 10 seconds #define LOCK_NETINFO() LOCK_GENERAL() #define UNLOCK_NETINFO() UNLOCK_GENERAL() // // Adapter info routines // DWORD AdapterInfo_SizeForServerCount( IN DWORD ServerCount ) /*++ Routine Description: Size in bytes of adapter info for given server count. Arguments: ServerCount -- max count of servers adapter will hold Return Value: Size in bytes of ADAPTER_INFO blob. --*/ { return sizeof(DNS_ADAPTER) - sizeof(DNS_SERVER_INFO) + (sizeof(DNS_SERVER_INFO) * ServerCount); } PDNS_ADAPTER AdapterInfo_Alloc( IN DWORD ServerCount ) /*++ Routine Description: Create uninitialized DNS Server list. Arguments: ServerCount -- count of servers list will hold Return Value: Ptr to uninitialized adapter, if successful NULL on failure. --*/ { DNSDBG( TRACE, ( "AdapterInfo_Alloc()\n" )); // // allocate blob for adapter info // return (PDNS_ADAPTER) ALLOCATE_HEAP_ZERO( AdapterInfo_SizeForServerCount( ServerCount ) ); } VOID AdapterInfo_Free( IN OUT PDNS_ADAPTER pAdapter ) /*++ Routine Description: Free DNS_ADAPTER structure. Arguments: pAdapter -- pointer to adapter blob to free Return Value: None. --*/ { DNSDBG( TRACE, ( "AdapterInfo_Free( %p )\n", pAdapter )); if ( !pAdapter ) { return; } if ( pAdapter->pszAdapterGuidName ) { FREE_HEAP( pAdapter->pszAdapterGuidName ); } if ( pAdapter->pszAdapterDomain ) { FREE_HEAP( pAdapter->pszAdapterDomain ); } if ( pAdapter->pAdapterIPAddresses ) { FREE_HEAP( pAdapter->pAdapterIPAddresses ); } if ( pAdapter->pAdapterIPSubnetMasks ) { FREE_HEAP( pAdapter->pAdapterIPSubnetMasks ); } FREE_HEAP( pAdapter ); } PDNS_ADAPTER AdapterInfo_Copy( 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. --*/ { PDNS_ADAPTER pcopy; DNSDBG( TRACE, ( "AdapterInfo_Copy( %p )\n", pAdapter )); if ( ! pAdapter ) { return NULL; } pcopy = AdapterInfo_Alloc( pAdapter->ServerCount ); if ( ! pcopy ) { return NULL; } // // copy the whole blob // - reset MaxServerCount to actual allocation RtlCopyMemory( pcopy, pAdapter, AdapterInfo_SizeForServerCount( pAdapter->ServerCount ) ); pcopy->MaxServerCount = pAdapter->ServerCount; // fixup allocated subfields pcopy->pszAdapterGuidName = Dns_CreateStringCopy( pAdapter->pszAdapterGuidName, 0 ); pcopy->pszAdapterDomain = Dns_CreateStringCopy( pAdapter->pszAdapterDomain, 0 ); pcopy->pAdapterIPAddresses = Dns_CreateIpArrayCopy( pAdapter->pAdapterIPAddresses ); pcopy->pAdapterIPSubnetMasks = Dns_CreateIpArrayCopy( pAdapter->pAdapterIPSubnetMasks ); return( pcopy ); } PDNS_ADAPTER AdapterInfo_Create( IN DWORD ServerCount, IN DWORD dwFlags, IN PSTR pszDomain, IN PSTR pszGuidName ) /*++ Routine Description: Create uninitialized DNS Server list. Arguments: ServerCount -- count of servers list will hold dwFlags -- flags pszAdapterDomain -- the names of the domain associated with this interface pszGuidName -- GUID name Return Value: Ptr to adapter info, if successful NULL on failure. --*/ { PDNS_ADAPTER padapter = NULL; DNSDBG( TRACE, ( "AdapterInfo_Create()\n" )); // // allocate blob for adapter info // padapter = AdapterInfo_Alloc( ServerCount ); if ( ! padapter ) { return NULL; } DNS_ASSERT( padapter->RunFlags == 0 ); DNS_ASSERT( padapter->ServerCount == 0 ); padapter->pszAdapterGuidName = Dns_CreateStringCopy( pszGuidName, 0 ); padapter->pszAdapterDomain = Dns_CreateStringCopy( pszDomain, 0 ); padapter->MaxServerCount = ServerCount; padapter->InfoFlags = dwFlags; return padapter; } PDNS_ADAPTER AdapterInfo_CreateFromIpArray( IN PIP_ARRAY pServerArray, IN DWORD dwFlags, IN PSTR pszDomainName, IN PSTR pszGuidName ) /*++ Routine Description: Create copy of IP address array as a DNS Server list. Arguments: pIpArray -- IP address array to convert dwFlags -- 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: Ptr to DNS Server list copy, if successful NULL on failure. --*/ { PDNS_ADAPTER padapter; DWORD i; DWORD count; DNSDBG( TRACE, ( "AdapterInfo_CreateFromIpArray()\n" )); // // get count of DNS servers // if ( !pServerArray ) { count = 0; } else { count = pServerArray->AddrCount; } // // create adapter with server list of required size // padapter = AdapterInfo_Create( count, dwFlags, pszDomainName, pszGuidName ); if ( !padapter ) { return NULL; } // // copy DNS server IPs and clear other fields // // DCR_QUESTION: are fields already zero'd? // for ( i=0; i < count; i++ ) { padapter->ServerArray[i].IpAddress = pServerArray->AddrArray[i]; padapter->ServerArray[i].Status = NO_ERROR; padapter->ServerArray[i].Priority = 0; } padapter->ServerCount = count; return padapter; } // // Search list routines // PSEARCH_LIST SearchList_Alloc( IN DWORD MaxNameCount, IN PSTR pszName ) /*++ Routine Description: Create uninitialized search list. Arguments: NameCount -- count of search names list will hold pszName -- primary domain name Return Value: Ptr to uninitialized DNS search list, if successful NULL on failure. --*/ { PSEARCH_LIST psearchList = NULL; DWORD length; DNSDBG( TRACE, ( "SearchList_Create()\n" )); if ( MaxNameCount == 0 && !pszName ) { return NULL; } // // 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; if ( pszName ) { psearchList->pszDomainOrZoneName = Dns_CreateStringCopy( pszName, 0 ); if ( ! psearchList->pszDomainOrZoneName ) { FREE_HEAP( psearchList ); return NULL; } } 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 )); // // DCR: eliminate search list DomainOrZoneName // if ( pSearchList ) { if ( pSearchList->pszDomainOrZoneName ) { FREE_HEAP( pSearchList->pszDomainOrZoneName ); } for ( i=0; i < pSearchList->MaxNameCount; i++ ) { PSTR 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, pSearchList->pszDomainOrZoneName ); if ( ! pcopy ) { return NULL; } for ( i=0; i < pSearchList->NameCount; i++ ) { PSTR pname = pSearchList->SearchNameArray[i].pszName; if ( pname ) { pname = Dns_CreateStringCopy( pname, 0 ); 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 PSTR 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_UTF8( pSearchList->SearchNameArray[ count ].pszName, pszName ) ) { return( TRUE ); } } return( FALSE ); } VOID SearchList_AddName( IN OUT PSEARCH_LIST pSearchList, IN PSTR 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; PSTR 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( pszName, 0 ); 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 PSTR 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 PCHAR pch = pszList; CHAR ch; PUCHAR pnameStart; 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 < DNS_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 != ' ' && ch != '\t' && ch != '\0' && ch != ',' ) { ch = *++pch; } *pch = '\0'; // // end of buffer? // if ( pch == pnameStart ) { break; } // // whack any trailing dot on name // pch--; if ( *pch == '.' ) { *pch = '\0'; } pch++; // // make copy of the name // pSearchList->SearchNameArray[ countNames ].pszName = Dns_CreateStringCopy( pnameStart, 0 ); if ( pSearchList->SearchNameArray[ countNames ].pszName ) { 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 PSTR pszPrimaryDomainName, IN PREG_SESSION pRegSession, IN HKEY hKey, IN OUT PDNS_NETINFO pNetInfo, IN BOOL fUseDomainNameDevolution, IN BOOL fUseDotLocalDomain ) /*++ 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; PSTR pregList = NULL; DWORD status; DNSDBG( TRACE, ( "Search_ListBuild()\n" )); ASSERT( pRegSession || hKey ); // // create search list using PDN // ptempList = SearchList_Alloc( DNS_MAX_SEARCH_LIST_ENTRIES, pszPrimaryDomainName ); if ( !ptempList ) { return( NULL ); } // // read search list from registry // ptempList->NameCount = 0; 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 ); } // // if no registry search list -- build one // // DCR: eliminate autobuilt search list // if ( ! ptempList->NameCount ) { PSTR pname; DWORD countNames = 0; DWORD iter; // // use PDN in first search list slot // if ( pszPrimaryDomainName ) { SearchList_AddName( ptempList, pszPrimaryDomainName, 0 ); } // // add devolved PDN if have NameDevolution // if ( ptempList->pszDomainOrZoneName && fUseDomainNameDevolution ) { PSTR ptoken = ptempList->pszDomainOrZoneName; ptoken = strchr( ptoken, '.' ); while ( ptoken ) { ptoken += 1; if ( strchr( ptoken, '.' ) ) { SearchList_AddName( ptempList, ptoken, DNS_QUERY_USE_QUICK_TIMEOUTS ); ptoken = strchr( ptoken, '.' ); continue; } break; } } // // add ".local" to search list if enabled // if ( fUseDotLocalDomain ) { SearchList_AddName( ptempList, MULTICAST_DNS_LOCAL_DOMAIN, DNS_QUERY_MULTICAST_ONLY ); } // indicate this is dummy search list if ( pNetInfo ) { pNetInfo->InfoFlags |= DNS_FLAG_DUMMY_SEARCH_LIST; } } return ptempList; } PSTR 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; PSTR 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(PDNS_ADAPTER) + (sizeof(PDNS_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( pNetInfo->AdapterArray[i] ); } 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; DWORD adapterCount; DWORD i; DNSDBG( TRACE, ( "NetInfo_Copy( %p )\n", pNetInfo )); if ( ! pNetInfo ) { return NULL; } IF_DNSDBG( NETINFO2 ) { 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; // // copy subcomponents // - domain name // - search list // - adapter info for each adapter // pcopy->pszDomainName = Dns_CreateStringCopy( pNetInfo->pszDomainName, 0 ); pcopy->pszHostName = Dns_CreateStringCopy( pNetInfo->pszHostName, 0 ); pcopy->pSearchList = SearchList_Copy( pNetInfo->pSearchList ); adapterCount = 0; for ( i=0; i < pNetInfo->AdapterCount; i++ ) { PDNS_ADAPTER pnew = AdapterInfo_Copy( pNetInfo->AdapterArray[i] ); if ( pnew ) { pcopy->AdapterArray[ adapterCount++ ] = pnew; } } pcopy->AdapterCount = adapterCount; IF_DNSDBG( NETINFO2 ) { DnsDbg_NetworkInfo( "Netinfo copy: ", pcopy ); } return pcopy; } BOOL NetInfo_AddAdapter( IN OUT PDNS_NETINFO pNetInfo, IN PDNS_ADAPTER pAdapter ) /*++ Routine Description: Add adapter info to network info. Arguments: pNetInfo -- netinfo to add to pAdapter -- actual adapter to add (it is plugged in NOT copied) Return Value: TRUE if successful. FALSE if list full. --*/ { DWORD i; DNSDBG( TRACE, ( "NetInfo_AddAdapter( %p )\n", pNetInfo )); // // find first open slot // for ( i = 0; i < pNetInfo->MaxAdapterCount; i++ ) { if ( ! pNetInfo->AdapterArray[i] ) { pNetInfo->AdapterArray[i] = pAdapter; pNetInfo->AdapterCount++; return( TRUE ); } } IF_DNSDBG( NETINFO ) { DnsDbg_NetworkInfo( "Network info failed adapter add: ", pNetInfo ); } return( FALSE ); } 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 --*/ { DWORD i; 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( i=0; iAdapterCount; i++ ) { PDNS_ADAPTER padapter; DWORD j; padapter = pNetInfo->AdapterArray[i]; padapter->Status = 0; padapter->RunFlags &= ClearLevel; padapter->ServerIndex = EMPTY_SERVER_INDEX; // clear server status fields for ( j=0; jServerCount; j++ ) { padapter->ServerArray[j].Status = DNSSS_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 --*/ { DWORD i; DWORD j; 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 // // DCR: local DNS check needs IP6 fixups // for ( i = 0; i < pNetInfo->AdapterCount; i++ ) { PDNS_ADAPTER padapter = pNetInfo->AdapterArray[i]; for ( j=0; j < padapter->ServerCount; j++ ) { PDNS_SERVER_INFO pserver = &padapter->ServerArray[j]; if ( !fLocalDnsOnly || Dns_IsAddressInIpArray( padapter->pAdapterIPAddresses, pserver->IpAddress ) || pserver->IpAddress == DNS_NET_ORDER_LOOPBACK ) { pserver->Priority = 0; } } } } PIP_ARRAY NetInfo_ConvertToIpArray( IN PDNS_NETINFO pNetInfo ) /*++ Routine Description: Create IP array of DNS servers from network info. Arguments: pNetInfo -- DNS net adapter list to convert Return Value: Ptr to IP array, if successful NULL on failure. --*/ { PIP_ARRAY parray; DWORD i; DWORD j; DWORD countServers = 0; DNSDBG( TRACE, ( "NetInfo_ConvertToIpArray( %p )\n", pNetInfo )); if ( ! pNetInfo ) { return NULL; } // // count up all the servers in list, create IP array of desired size // for ( i = 0; i < pNetInfo->AdapterCount; i++ ) { countServers += pNetInfo->AdapterArray[i]->ServerCount; } parray = Dns_CreateIpArray( countServers ); if ( !parray ) { SetLastError( DNS_ERROR_NO_MEMORY ); return( NULL ); } DNS_ASSERT( parray->AddrCount == countServers ); // // read all servers into IP array // countServers = 0; for ( i = 0; i < pNetInfo->AdapterCount; i++ ) { PDNS_ADAPTER padapter = pNetInfo->AdapterArray[i]; for ( j = 0; j < padapter->ServerCount; j++ ) { parray->AddrArray[ countServers++ ] = padapter->ServerArray[j].IpAddress; } } DNS_ASSERT( parray->AddrCount == countServers ); return( parray ); } PDNS_NETINFO NetInfo_CreateForUpdate( IN PSTR pszZone, IN PSTR pszServerName, IN PIP_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. --*/ { PSEARCH_LIST psearchList; PDNS_ADAPTER padapter; PDNS_NETINFO pnetworkInfo; DNSDBG( TRACE, ( "NetInfo_BuildForUpdate()\n" )); // // allocate // pnetworkInfo = NetInfo_Alloc( 1 ); if ( !pnetworkInfo ) { return NULL; } // // make search list from desired zone // if ( pszZone ) { psearchList = SearchList_Alloc( 0, pszZone ); if ( ! psearchList ) { goto Fail; } pnetworkInfo->pSearchList = psearchList; } // // convert IP array and server name to server list // padapter = AdapterInfo_CreateFromIpArray( pServerArray, dwFlags, pszServerName, NULL ); if ( ! padapter ) { goto Fail; } pnetworkInfo->AdapterArray[0] = padapter; pnetworkInfo->AdapterCount = 1; IF_DNSDBG( NETINFO2 ) { DnsDbg_NetworkInfo( "Update network info is: ", pnetworkInfo ); } return pnetworkInfo; Fail: NetInfo_Free( pnetworkInfo ); return NULL; } PSTR 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->pSearchList->pszDomainOrZoneName; // return pNetInfo->AdapterArray[0]-pszAdapterGuidName; // return pNetInfo->pszDomainName; } PSTR 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->pSearchList && pNetInfo->pSearchList->pszDomainOrZoneName && pNetInfo->AdapterCount == 1 && pNetInfo->AdapterArray[0] ); } PDNS_NETINFO NetInfo_CreateFromIpArray( 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. 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; PSTR 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_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( pNetInfo->pszDomainName, 0 ); } 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 & DNS_FLAG_DUMMY_SEARCH_LIST); } return( pnetInfo ); } // // 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 IP4_ADDRESS Ip4Addr ) /*++ 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 Ip4Addr -- DNS server address to test for reachability Return Value: TRUE if DNS server is reachable. FALSE otherwise. --*/ { PDNS_ADAPTER padapter; DWORD i; PIP4_ARRAY pipArray; PIP4_ARRAY psubnetArray; DWORD ipCount; DNSDBG( NETINFO, ( "Enter IsDnsReachableOnAlternateInterface( %p, %d, %08x )\n", pNetInfo, InterfaceIndex, Ip4Addr )); // // find DNS adapter for interface // for( i=0; iAdapterCount; i++ ) { padapter = pNetInfo->AdapterArray[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; } // // two success conditions: // 1) our IP matches IP of DNS server for this interface // 2) our IP is on subnet of IP on this interface // // if either of these is TRUE then either // - there is misconfiguration (not our problem) // OR // - these interfaces are connected and we can safely send on // them // // DCR: it would be cool to save the default gateway for the interface // and use it? for ( i=0; iServerCount; i++ ) { if ( Ip4Addr == padapter->ServerArray[i].IpAddress ) { DNSDBG( NETINFO, ( "DNS server %08x also DNS server on send interface %d\n", Ip4Addr, InterfaceIndex )); return( TRUE ); } } // test for subnet match pipArray = padapter->pAdapterIPAddresses; psubnetArray = padapter->pAdapterIPSubnetMasks; 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; iAddrArray[i]; if ( (subnet & Ip4Addr) == (subnet & pipArray->AddrArray[i]) ) { DNSDBG( NETINFO, ( "DNS server %08x on subnet of IP for send interface %d\n" "\tip = %08x, subnet = %08x\n", Ip4Addr, InterfaceIndex, pipArray->AddrArray[i], subnet )); return( TRUE ); } } 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; 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; iAdapterCount; i++ ) { padapter = pNetInfo->AdapterArray[i]; // ignore this adapter because there are no DNS // servers configured? if ( padapter->InfoFlags & DNS_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; for ( j=0; jServerCount; j++ ) { IP4_ADDRESS dnsIp = padapter->ServerArray[j].IpAddress; status = IpHelp_GetBestInterface( dnsIp, & serverIfIndex ); if ( status != NO_ERROR ) { DNS_ASSERT( FALSE ); DNSDBG( ANY, ( "GetBestInterface() failed! %d\n", status )); 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, dnsIp ) ) { validServers++; break; //continue; } } // // disable adapter if no servers found // // 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 ) { padapter->InfoFlags |= (DNS_FLAG_IGNORE_ADAPTER | DNS_FLAG_SERVERS_UNREACHABLE); DNSDBG( NETINFO, ( "No reachable servers on interface %d\n" "\tthis adapter (%p) ignored in DNS list!\n", adapterIfIndex, padapter )); } } return ERROR_SUCCESS; } // // Main netinfo build routine // 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 Return Value: ERROR_SUCCESS if successful. Error code on failure. --*/ { REG_SESSION regSession; PREG_SESSION pregSession = NULL; PDNS_NETINFO pnetInfo = NULL; PDNS_ADAPTER padapter = NULL; PIP_ARRAY ptempArray = NULL; PIP_ARRAY plocalIpArray = NULL; PIP_ARRAY psubnetIpArray = NULL; PIP_ARRAY pserverIpArray = NULL; DNS_STATUS status = NO_ERROR; DWORD count; PIP_ADAPTER_INFO pipAdapterInfo = NULL; PIP_ADAPTER_INFO pipAdapter = NULL; DWORD value; PREG_GLOBAL_INFO pregInfo = NULL; REG_GLOBAL_INFO regInfo; REG_ADAPTER_INFO regAdapterInfo; DNSDBG( TRACE, ( "NetInfo_Build( %d )\n", fGetIpAddrs )); // // always get 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 // fGetIpAddrs = TRUE; // // get adapters info from IP help // status = IpHelp_GetAdaptersInfo( &pipAdapterInfo ); if ( status != ERROR_SUCCESS ) { goto Cleanup; } // // open the registry // pregSession = ®Session; status = Reg_OpenSession( pregSession, 0, 0 ); if ( status != ERROR_SUCCESS ) { status = DNS_ERROR_NO_DNS_SERVERS; goto Cleanup; } // // read global registry info // pregInfo = ®Info; status = Reg_ReadGlobalInfo( pregSession, pregInfo ); if ( status != ERROR_SUCCESS ) { status = DNS_ERROR_NO_DNS_SERVERS; goto Cleanup; } // // build adapter information // // count up the active adapters pipAdapter = pipAdapterInfo; count = 0; while ( pipAdapter ) { count++; pipAdapter = pipAdapter->Next; } // // allocate net info blob // allocate DNS server IP array // pnetInfo = NetInfo_Alloc( count ); ptempArray = Dns_CreateIpArray( DNS_MAX_IP_INTERFACE_COUNT ); if ( !pnetInfo || !ptempArray ) { status = DNS_ERROR_NO_MEMORY; goto Cleanup; } // // set network info flags // // DCR: can use globals // if ( regInfo.fUseMulticast ) { pnetInfo->InfoFlags |= DNS_FLAG_ALLOW_MULTICAST; } if ( regInfo.fUseMulticastOnNameError ) { pnetInfo->InfoFlags |= DNS_FLAG_MULTICAST_ON_NAME_ERROR; } // // loop through adapters -- build network info for each // pipAdapter = pipAdapterInfo; while ( pipAdapter ) { DWORD adapterFlags = 0; PIP_PER_ADAPTER_INFO pperAdapterInfo = NULL; PSTR padapterDomainName = NULL; // // read adapter registry info // status = Reg_ReadAdapterInfo( pipAdapter->AdapterName, pregSession, & regInfo, // policy adapter info & regAdapterInfo // receives reg info read ); if ( status != NO_ERROR ) { goto Skip; } // translate results into flags if ( regAdapterInfo.fRegistrationEnabled ) { adapterFlags |= DNS_FLAG_REGISTER_IP_ADDRESSES; } if ( regAdapterInfo.fRegisterAdapterName ) { adapterFlags |= DNS_FLAG_REGISTER_DOMAIN_NAME; } // use domain name? // - if disable on per adapter basis, then it's dead if ( regAdapterInfo.fQueryAdapterName ) { padapterDomainName = regAdapterInfo.pszAdapterDomainName; } // set flag on DHCP adapters if ( pipAdapter->DhcpEnabled ) { adapterFlags |= DNS_FLAG_IS_DHCP_CFG_ADAPTER; } // // get adapter's IP addresses // // DCR_FIX: why get IPs if aren't going to use // ptempArray->AddrCount = 0; status = IpHelp_ParseIpAddressString( ptempArray, & pipAdapter->IpAddressList, FALSE, // Get the ip address(es) TRUE // Reverse the order because ); // of broken iphlpapi! if ( ( status == NO_ERROR ) && ( fGetIpAddrs || g_IsDnsServer ) ) { plocalIpArray = Dns_CreateIpArrayCopy( ptempArray ); // // Go the subnet mask(s) // // DCR_QUESTION: why failure case on getting subnets // if doesn't work then IP help gave bad info and // we should just agree we are dead // // DCR_FIX: default subnetting is class B! // ptempArray->AddrCount = 0; status = IpHelp_ParseIpAddressString( ptempArray, &pipAdapter->IpAddressList, TRUE, // Get the subnet mask(s) TRUE ); // Reverse the order because // of broken iphlpapi! if ( status == NO_ERROR ) { psubnetIpArray = Dns_CreateIpArrayCopy( ptempArray ); } else { psubnetIpArray = Dns_CreateIpArrayCopy( plocalIpArray ); if ( psubnetIpArray ) { DWORD i; for ( i=0; i < psubnetIpArray->AddrCount; i++ ) { psubnetIpArray->AddrArray[i] = 0x0000ffff; } status = NO_ERROR; } } } if ( status != NO_ERROR ) { goto Skip; } // // get per-adapter information from the iphlpapi.dll. // -- autonet // -- DNS server list // status = IpHelp_GetPerAdapterInfo( pipAdapter->Index, & pperAdapterInfo ); if ( status == NO_ERROR ) { if ( pperAdapterInfo->AutoconfigEnabled && pperAdapterInfo->AutoconfigActive ) { adapterFlags |= DNS_FLAG_IS_AUTONET_ADAPTER; } } // // build DNS servers list for adapter // - if policy override use it // - otherwise use IP help list // if ( regInfo.pDnsServerArray ) { pserverIpArray = regInfo.pDnsServerArray; } else if ( status == NO_ERROR ) { // parse DNS servers from IP help // // DCR: must be able to get IPv6 pserverIpArray = ptempArray; ptempArray->AddrCount = 0; status = IpHelp_ParseIpAddressString( ptempArray, &pperAdapterInfo->DnsServerList, FALSE, // get IP addresses FALSE // no reverse ); if ( status != ERROR_SUCCESS ) { // // if no DNS servers found -- write loopback if on DNS server // // DCR: should we bother to write -- why not just use in // memory? // if ( g_IsDnsServer && !(adapterFlags & DNS_FLAG_IS_DHCP_CFG_ADAPTER) && plocalIpArray && psubnetIpArray ) { Reg_WriteLoopbackDnsServerList( pipAdapter->AdapterName, pregSession ); ptempArray->AddrCount = 1; ptempArray->AddrArray[0] = DNS_NET_ORDER_LOOPBACK; } else // no DNS servers { pserverIpArray = NULL; goto Skip; } } } // // 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 // padapter = AdapterInfo_CreateFromIpArray( pserverIpArray, adapterFlags, padapterDomainName, pipAdapter->AdapterName ); if ( padapter ) { padapter->InterfaceIndex = pipAdapter->Index; if ( fGetIpAddrs && plocalIpArray && psubnetIpArray ) { padapter->pAdapterIPAddresses = plocalIpArray; plocalIpArray = NULL; padapter->pAdapterIPSubnetMasks = psubnetIpArray; psubnetIpArray = NULL; } NetInfo_AddAdapter( pnetInfo, padapter ); } Skip: // // cleanup adapter specific data // // note: no free of pserverIpArray, it IS the // ptempArray buffer that we free at the end // Reg_FreeAdapterInfo( ®AdapterInfo, FALSE // don't free blob, it is on stack ); if ( pperAdapterInfo ) { FREE_HEAP( pperAdapterInfo ); pperAdapterInfo = NULL; } if ( plocalIpArray ) { FREE_HEAP( plocalIpArray ); plocalIpArray = NULL; } if ( psubnetIpArray ) { FREE_HEAP( psubnetIpArray ); psubnetIpArray = NULL; } // get next adapter // reset status, so failure on the last adapter is not // seen as global failure pipAdapter = pipAdapter->Next; status = ERROR_SUCCESS; } // // 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, regInfo.fUseDotLocalDomain ); } // // host and domain name info // pnetInfo->pszDomainName = Dns_CreateStringCopy( regInfo.pszPrimaryDomainName, 0 ); pnetInfo->pszHostName = Dns_CreateStringCopy( regInfo.pszHostName, 0 ); // timestamp pnetInfo->TimeStamp = Dns_GetCurrentTimeInSeconds(); Cleanup: // free allocated reg info Reg_FreeGlobalInfo( pregInfo, FALSE // don't free blob, it is on stack ); if ( pipAdapterInfo ) { FREE_HEAP( pipAdapterInfo ); } if ( ptempArray ) { FREE_HEAP( ptempArray ); } 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 ); } DNSDBG( TRACE, ( "Leave: NetInfo_Build()\n" "\treturning fresh built network info (%p)\n", pnetInfo )); return( pnetInfo ); } // // Network info caching\state routines // VOID InitNetworkInfo( VOID ) /*++ Routine Description: Initialize network info. Arguments: None Return Value: None --*/ { // // currently using general CS as netinfo cache lock // so no real init here // // InitializeCriticalSection( &csNetworkInfo ); g_pNetInfo = NULL; } VOID CleanupNetworkInfo( VOID ) /*++ Routine Description: Initialize network info. Arguments: None Return Value: None --*/ { DNS_NETINFO pold; LOCK_NETINFO(); NetInfo_Free( g_pNetInfo ); g_pNetInfo = NULL; UNLOCK_NETINFO(); } // // 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" )); #if DNSBUILDOLD if ( !g_IsWin2000 ) { return NULL; } #endif // 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 BOOL fForce, IN BOOL fGetIpAddresses ) /*++ 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: fForce -- force reread from registry fGetIpAddresses -- keep the local IP addresses for each adapter and store within the structure. Return Value: ERROR_SUCCESS if successful. Error code on failure. --*/ { PDNS_NETINFO pnetInfo; PDNS_NETINFO poldNetInfo = NULL; BOOL flocked = FALSE; BOOL fcacheable = TRUE; DNSDBG( NETINFO, ( "NetInfo_Get()\n" )); // // hold lock while accessing cached network info // // try // - very recent local cached copy // - RPC copy from resolver // - build new // if ( ! fForce && ! g_DnsTestMode ) { LOCK_NETINFO(); flocked = TRUE; // check if valid copy cached in process if ( g_pNetInfo && (g_pNetInfo->TimeStamp + NETINFO_CACHE_TIMEOUT) > Dns_GetCurrentTimeInSeconds() ) { pnetInfo = NetInfo_Copy( g_pNetInfo ); if ( pnetInfo ) { DNSDBG( TRACE, ( "Netinfo found in process cache.\n" )); goto Unlock; } } // check RPC to resolver // // 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 pnetInfo = UpdateDnsConfig(); if ( pnetInfo ) { DNSDBG( TRACE, ( "Netinfo read from resolver.\n" )); goto CacheCopy; } } // // build fresh network info // - if successful clear "dirty" flag // // Note: we call down unlocked. Ideally we'd have only one thread // in the process building at a given time, and let other threads // wait for completion. However during call down, iphlpapi RPCs // to MPR (router service) which can result in deadlock through // a PPP call back to our DnsSetDwordConfig() => NetInfo_MarkDirty. // // Here's the deadlock scenario: // 1. RtrMgr calls GetHostByName() which ends up in a // iphlpapi!GetBestInterface call which in turn calls // Mprapi!IsRouterRunning (which is an RPC to mprdim). // 2. 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. // 3. Demand-dial disconnect is waiting for ppp to finish graceful // termination. // 4. PPP is waiting for dnsto return from DnsSetConfigDword, hence // the deadlock. // // This gave us a deadlock because NetInfo_MarkDirty() also waits // on this lock. MarkDirty could skip the lock -- one method would be // to have this function clear the dirty flag, then call down again // IF it got done and the flag was again dirty. But that method could // result if something is bogus down below. And even if MarkDirty is // fixed cleanly we still have the possibility that some service that // IpHlpApi RPCs to, will issue name resolution and come back to the // lock on a different thread. (Because our many services in process // model is busted.) // // So best solution is not to do anything this big while holding a lock. // (We could do hacking stuff here like have an event and wait for a // second or so before figuring we were part of a deadlock and proceeding.) // // Finally note that the perf hit is negligible because usually the netinfo // is grabbed from the resolver. // if ( flocked ) { UNLOCK_NETINFO(); flocked = FALSE; } pnetInfo = NetInfo_Build( fGetIpAddresses ); if ( !pnetInfo ) { goto Unlock; } fcacheable = fGetIpAddresses; CacheCopy: // // update cached copy // - but not if built without local IPs; // resolver copy always contains local IPs // if ( fcacheable ) { if ( !flocked ) { LOCK_NETINFO(); flocked = TRUE; } poldNetInfo = g_pNetInfo; g_pNetInfo = NetInfo_Copy( pnetInfo ); } Unlock: if ( flocked ) { UNLOCK_NETINFO(); } 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" )); // // 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(); pold = g_pNetInfo; g_pNetInfo = NULL; UNLOCK_NETINFO(); NetInfo_Free( pold ); } // // DNS server list // PIP4_ARRAY GetDnsServerList( IN BOOL fForce ) /*++ Routine Description: Get DNS server list as IP array. Arguments: fForce -- force reread from registry Return Value: ERROR_SUCCESS if successful. Error code on failure. --*/ { PDNS_NETINFO pnetInfo = NULL; PIP_ARRAY pserverIpArray = NULL; DNSDBG( TRACE, ( "GetDnsServerList()" )); // // get network info to make list from // - don't need IP address lists // pnetInfo = NetInfo_Get( fForce, // force registry read FALSE // no IP address lists ); if ( !pnetInfo ) { SetLastError( DNS_ERROR_NO_DNS_SERVERS ); return( NULL ); } // // convert network info to IP_ARRAY // pserverIpArray = NetInfo_ConvertToIpArray( pnetInfo ); NetInfo_Free( pnetInfo ); if ( !pserverIpArray ) { SetLastError( DNS_ERROR_NO_MEMORY ); return( NULL ); } // if no servers read, return if ( pserverIpArray->AddrCount == 0 ) { FREE_HEAP( pserverIpArray ); DNS_PRINT(( "Dns_GetDnsServerList() failed: no DNS servers found\n" "\tstatus = %d\n" )); SetLastError( DNS_ERROR_NO_DNS_SERVERS ); return( NULL ); } IF_DNSDBG( NETINFO ) { DnsDbg_IpArray( "DNS server list", "server", pserverIpArray ); } return( pserverIpArray ); } // // End netinfo.c //