/*++ Copyright (c) 2000-2001 Microsoft Corporation Module Name: config.c Abstract: DNS Resolver Service. Network configuration info Author: Jim Gilroy (jamesg) March 2000 Revision History: --*/ #include "local.h" // // Network info // PDNS_NETINFO g_NetworkInfo = NULL; DWORD g_TimeOfLastPnPUpdate; DWORD g_NetInfoTag = 0; DWORD g_ResetServerPriorityTime = 0; // // Network failure caching // DWORD g_NetFailureTime; DNS_STATUS g_NetFailureStatus; DWORD g_TimedOutAdapterTime; BOOL g_fTimedOutAdapter; DNS_STATUS g_PreviousNetFailureStatus; DWORD g_MessagePopupStrikes; DWORD g_NumberOfMessagePopups; // // Config globals // // DCR: eliminate useless config globals // DWORD g_HashTableSize; DWORD g_MaxSOACacheEntryTtlLimit; DWORD g_NegativeSOACacheTime; DWORD g_NetFailureCacheTime; DWORD g_MessagePopupLimit; // // Network Info reread time // - currently fifteen minutes // #define NETWORK_INFO_REREAD_TIME (900) // // Locking // CRITICAL_SECTION NetinfoCS; TIMED_LOCK NetinfoBuildLock; #define LOCK_NETINFO() EnterCriticalSection( &NetinfoCS ) #define UNLOCK_NETINFO() LeaveCriticalSection( &NetinfoCS ) #define LOCK_NETINFO_BUILD() TimedLock_Enter( &NetinfoBuildLock, 5000 ) #define UNLOCK_NETINFO_BUILD() TimedLock_Leave( &NetinfoBuildLock ) // // Network info configuration // VOID UpdateNetworkInfo( IN PDNS_NETINFO pNetInfo OPTIONAL ) /*++ Routine Description: Update network info global. Arguments: pNetInfo -- desired network info; if NULL clear cached copy Return Value: None --*/ { PDNS_NETINFO poldInfo = NULL; DNSDBG( TRACE, ( "UpdateNetworkInfo( %p )\n", pNetInfo )); LOCK_NETINFO(); // // cache previous netinfo to pickup server priority changes // - don't cache if not same version // - kills copy that was created before last build // - kills copy that was before last PnP (cache clear) // - don't cache if never reset priorities // if ( pNetInfo ) { if ( pNetInfo->Tag != g_NetInfoTag ) { DNSDBG( INIT, ( "Skip netinfo update -- previous version" "\tptr = %p\n" "\tversion = %d\n" "\tcurrent version = %d\n", pNetInfo, pNetInfo->Tag, g_NetInfoTag )); poldInfo = pNetInfo; DNS_ASSERT( pNetInfo->Tag < g_NetInfoTag ); goto Cleanup; } if ( g_ServerPriorityTimeLimit == 0 ) { DNSDBG( INIT, ( "Skip netinfo update -- no priority reset!\n" )); poldInfo = pNetInfo; goto Cleanup; } NetInfo_Clean( pNetInfo, CLEAR_LEVEL_QUERY ); } // // no netinfo means clear cached copy // - push up tag count, so no copy out for update can // come back and be reused through path above else { g_NetInfoTag++; } // // swap -- caches copy or clears // poldInfo = g_NetworkInfo; g_NetworkInfo = pNetInfo; Cleanup: UNLOCK_NETINFO(); NetInfo_Free( poldInfo ); } PDNS_NETINFO GrabNetworkInfo( VOID ) /*++ Routine Description: Get copy of network info. Named it "Grab" to avoid confusion with GetNetworkInfo() in dnsapi.dll. Arguments: None Return Value: Ptr to copy of network info (caller must free). NULL on error. --*/ { PDNS_NETINFO poldInfo = NULL; PDNS_NETINFO pnewInfo = NULL; DWORD currentTime = Dns_GetCurrentTimeInSeconds(); DWORD newTag; BOOL fbuildLock = FALSE; BOOL fnetLock = FALSE; DNSDBG( TRACE, ( "GrabNetworkInfo()\n" )); // // Locking note: // // Can not hold a single NET_LIST lock during netinfo build. // - IpHlpApi doing routing info call RPCs to router which can // depend on PnP notifications, which in turn can be causing calls // back into resolver indicating config change; overall the loss // of control is too large // // There are ways to make the config change issue go away (ex setting // some sort of "invalid" flag under interlock), but they basically boil // down to introducing some other sort of lock. // // The bottom line is there are TWO SEPARATE issues here: // // 1) Access to cache netinfo, which may be invalidated. // 2) Single build of netinfo for perf. // // Implementation wise, you can have the invalidation\clear under a // separate interlock, and thus have a single lock for copy-access\build, // but the reality is the same. // // // Algorithm: // - check for valid cached copy // => found, out // - get build lock // - check again for valid cached copy // => found, out // - build // - cache new netinfo // - release build lock // // Where the check and cache work are independently locked with the shorter // duration NET_LIST lock, which simply protects access to the cached global // (and hence is also used in invalidation and update). // // // check for valid cached netinfo // - within forced reread time // - reset server priorities if // // DCR: priority reset time should move into netinfo blob // // for perf we do this BEFORE entering build lock // then again once have build lock // while ( 1 ) { LOCK_NETINFO(); fnetLock = TRUE; if ( g_NetworkInfo && g_NetworkInfo->TimeStamp + NETWORK_INFO_REREAD_TIME > currentTime ) { if ( g_ResetServerPriorityTime < currentTime ) { NetInfo_ResetServerPriorities( g_NetworkInfo, TRUE ); g_ResetServerPriorityTime = currentTime + g_ServerPriorityTimeLimit; } goto Copy; } // // if no cached info // - get build lock // - loop back and recheck cache // if ( fbuildLock ) { goto Build; } UNLOCK_NETINFO(); fnetLock = FALSE; fbuildLock = LOCK_NETINFO_BUILD(); if ( fbuildLock ) { continue; } goto Unlock; } // // current global expired // - build new netinfo // - tag it with unique monotonically increasing id // this makes sure we never use older version // - push forward priorities reset time // - make newinfo the global copy // Build: DNS_ASSERT( fnetLock && fbuildLock ); newTag = ++g_NetInfoTag; UNLOCK_NETINFO(); fnetLock = FALSE; pnewInfo = NetInfo_Build( TRUE ); if ( !pnewInfo ) { DNSDBG( ANY, ( "ERROR: GrabNetworkInfo() failed -- no netinfo blob!\n" )); goto Unlock; } LOCK_NETINFO(); fnetLock = TRUE; pnewInfo->Tag = newTag; if ( newTag != g_NetInfoTag ) { DNS_ASSERT( newTag < g_NetInfoTag ); DNSDBG( ANY, ( "WARNING: New netinfo uncacheable -- tag is old!\n" "\tour tag = %d\n" "\tcurrent tag = %d\n", newTag, g_NetInfoTag )); goto Unlock; } // if tag still current cache this new netinfo g_ResetServerPriorityTime = currentTime + g_ServerPriorityTimeLimit; g_TimedOutAdapterTime = 0; g_fTimedOutAdapter = FALSE; poldInfo = g_NetworkInfo; g_NetworkInfo = pnewInfo; Copy: // // make copy of global (new or reset) // pnewInfo = NetInfo_Copy( g_NetworkInfo ); Unlock: if ( fnetLock ) { UNLOCK_NETINFO(); } if ( fbuildLock ) { UNLOCK_NETINFO_BUILD(); } NetInfo_Free( poldInfo ); return pnewInfo; } VOID ZeroNetworkConfigGlobals( VOID ) /*++ Routine Description: Zero init network globals. Arguments: None Return Value: None --*/ { // net failure g_NetFailureTime = 0; g_NetFailureStatus = NO_ERROR; g_PreviousNetFailureStatus = NO_ERROR; g_fTimedOutAdapter = FALSE; g_TimedOutAdapterTime = 0; g_MessagePopupStrikes = 0; g_NumberOfMessagePopups = 0; // network info g_TimeOfLastPnPUpdate = 0; g_NetworkInfo = NULL; } VOID CleanupNetworkInfo( VOID ) /*++ Routine Description: Cleanup network info. Arguments: None Return Value: None --*/ { LOCK_NETINFO(); if ( g_NetworkInfo ) { NetInfo_Free( g_NetworkInfo ); g_NetworkInfo = NULL; } UNLOCK_NETINFO(); } // // General configuration // VOID ReadRegistryConfig( VOID ) { // // re-read full DNS registry info // Reg_ReadGlobalsEx( 0, NULL ); // // set "we are the resolver" global // g_InResolver = TRUE; // // just default old config globals until remove // // DCR: status of old config globals? // g_MaxSOACacheEntryTtlLimit = DNS_DEFAULT_MAX_SOA_TTL_LIMIT; g_NegativeSOACacheTime = DNS_DEFAULT_NEGATIVE_SOA_CACHE_TIME; g_NetFailureCacheTime = DNS_DEFAULT_NET_FAILURE_CACHE_TIME; g_HashTableSize = DNS_DEFAULT_HASH_TABLE_SIZE; g_MessagePopupLimit = DNS_DEFAULT_MESSAGE_POPUP_LIMIT; } VOID HandleConfigChange( IN PSTR pszReason, IN BOOL fCacheFlush ) /*++ Routine Description: Response to configuration change. Arguments: pszReason -- config change cause (informational only) fCache_Flush -- flush if config change requires cache flush Return Value: None --*/ { DNSDBG( INIT, ( "\n" "HandleConfigChange() => %s\n" "\tflush = %d\n", pszReason, fCacheFlush )); // // lock out all work while tear down everything // - lock with no start option so if we are already torn // down we don't rebuild // - optionally flush cache // - dump IP list // - dump network info // LOCK_CACHE_NO_START(); // // cache flush? // // all config changes don't necessarily require cache flush; // if not required just rebuild local list and config // if ( fCacheFlush ) { Cache_Flush(); } #if 0 // FIX6: no separate local addr info now // UpdateNetworkInfo() handles freshness issue else { ClearLocalAddrArray(); } #endif // // clear network info // save PnP time // clear net failure flags // UpdateNetworkInfo( NULL ); g_TimeOfLastPnPUpdate = Dns_GetCurrentTimeInSeconds(); g_NetFailureTime = 0; g_NetFailureStatus = NO_ERROR; DNSDBG( INIT, ( "Leave HandleConfigChange() => %s\n" "\tflush = %d\n\n", pszReason, fCacheFlush )); UNLOCK_CACHE(); } // // Remote APIs // VOID R_ResolverGetConfig( IN DNS_RPC_HANDLE Handle, IN DWORD Cookie, OUT PDNS_NETINFO * ppNetInfo, OUT PDNS_GLOBALS_BLOB * ppGlobals ) /*++ Routine Description: Make the query to remote DNS server. Arguments: Handle -- RPC handle Cookie -- cookie of last succesful config transfer zero indicates no previous successful transfer ppNetInfo -- addr to receive ptr to network info ppGlobals -- addr to receive ptr to globals blob Return Value: None --*/ { PDNS_GLOBALS_BLOB pblob; DNSLOG_F1( "R_ResolverGetConfig" ); DNSDBG( RPC, ( "R_ResolverGetConfig\n" "\tcookie = %p\n", Cookie )); if ( !ppNetInfo || !ppGlobals ) { return; } // // DCR: config cookie check // - no need to get data if client has current copy // // // copy network info global // // note: currently RPC is using same allocator (dnslib) // as GrabNetworkInfo(); so we are ok just passing // NETINFO blob as is // // note: could build on "no-global" but since we create on // cache start we should always have global or be // just starting // *ppNetInfo = (PDNS_NETINFO) GrabNetworkInfo(); pblob = (PDNS_GLOBALS_BLOB) RPC_HEAP_ALLOC( sizeof(*pblob) ); if ( pblob ) { RtlCopyMemory( pblob, & DnsGlobals, sizeof(DnsGlobals) ); // clear "in resolver" flag pblob->InResolver = FALSE; } *ppGlobals = pblob; DNSDBG( RPC, ( "Leave R_ResolverGetConfig\n\n" )); } VOID R_ResolverPoke( IN DNS_RPC_HANDLE Handle, IN DWORD Cookie, IN DWORD Id ) /*++ Routine Description: Test interface to poke resolver to update. Arguments: Handle -- RPC handle Cookie -- cookie Id -- operation Id Return Value: None --*/ { DNSLOG_F1( "R_ResolverPoke" ); DNSDBG( RPC, ( "R_ResolverPoke\n" "\tcookie = %08x\n" "\tid = %d\n", Cookie, Id )); if ( !Rpc_AccessCheck( RESOLVER_ACCESS_FLUSH ) ) { DNSLOG_F1( "R_ResolverPoke - ERROR_ACCESS_DENIED" ); return; } // // do operation for particular id // - update netinfo clears cached copy if ( Id == POKE_OP_UPDATE_NETINFO ) { if ( Cookie == POKE_COOKIE_UPDATE_NETINFO ) { UpdateNetworkInfo( NULL ); } } } // // Net failure stuff -- of dubious value // #if 0 BOOL IsKnownNetFailure( VOID ) /*++ Routine Description: Determine if we are in known net failure window. Arguments: None Return Value: TRUE if in known net failure FALSE otherwise --*/ { BOOL flag = FALSE; DNSDBG( TRACE, ( "IsKnownNetFailure()\n" )); // // DCR: should have NetFailure check outside of lock for perf // LOCK_NET_FAILURE(); if ( g_NetFailureStatus ) { if ( g_NetFailureTime < Dns_GetCurrentTimeInSeconds() ) { g_NetFailureTime = 0; g_NetFailureStatus = ERROR_SUCCESS; flag = FALSE; } else { SetLastError( g_NetFailureStatus ); flag = TRUE; } } UNLOCK_NET_FAILURE(); return flag; } VOID SetKnownNetFailure( IN DNS_STATUS Status ) /*++ Routine Description: Set cause of net failure. Arguments: Status -- status code for cause of net failure Return Value: None --*/ { LPSTR DnsString = NULL; LPWSTR InsertStrings[3]; WCHAR String1[25]; WCHAR String2[256]; WCHAR String3[25]; DNSDBG( TRACE, ( "SetKnownNetFailure()\n" )); // // don't indicate failure during boot // if ( Dns_GetCurrentTimeInSeconds() < THREE_MINUTES_FROM_SYSTEM_BOOT ) { return; } // // DCR: need to detect no-net // // FIX6: should detect no-net and skip this // if ( g_NetFailureCacheTime == 0 ) { // // We are in a no-net configuration, there is no need // to display the pop-up message. No point warning // of DNS configuration problems when the system is // off the net. // - or - // We are on a NT server, and therefore don't do poor network // performance caching. // return; } LOCK_NET_FAILURE(); g_NetFailureTime = Dns_GetCurrentTimeInSeconds() + g_NetFailureCacheTime; g_NetFailureStatus = Status; wsprintfW( String1, L"0x%.8X", Status ); DnsString = DnsStatusString( Status ); if ( DnsString ) { Dns_StringCopy( (PBYTE) String2, NULL, (PCHAR) DnsString, (WORD) strlen( DnsString ), DnsCharSetAnsi, DnsCharSetUnicode ); // // No need to free this since the string is just a pointer // to a global table entry. // // FREE_HEAP( DnsString ); } else { wsprintfW( String2, L"" ); } wsprintfW( String3, L"%d", g_NetFailureCacheTime ); if ( g_MessagePopupStrikes < 3 ) { g_MessagePopupStrikes++; } else { if ( Status != g_PreviousNetFailureStatus ) { // // DCR_PERF: should remove logging from inside lock // InsertStrings[0] = String1; InsertStrings[1] = String2; InsertStrings[2] = String3; ResolverLogEvent( EVENT_DNS_CACHE_NETWORK_PERF_WARNING, EVENTLOG_WARNING_TYPE, 3, InsertStrings, Status ); g_PreviousNetFailureStatus = Status; } g_MessagePopupStrikes = 0; } UNLOCK_NET_FAILURE(); } #endif // // End config.c //