/*++ 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) // // 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_NET_LIST(); // // 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_NET_LIST(); 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(); DNSDBG( TRACE, ( "GrabNetworkInfo()\n" )); // // check for valid network info global // - within forced reread time // - reset server priorities if // LOCK_NET_LIST(); 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; } } // // 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 // // Do not hold lock during 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 // // // Two separate issues: // // 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. // // // Solution // - get our tag first // - clear lock // - make build call // - on return check that tag is still valid, if not we can // USE the result, but we don't cache it, as the global tag // could be higher because of config change, and in any case // we let the last builder win // // DCR: netinfo build lock for perf, independent of protecting global // could have "timed-lock" using event that waits for a short interval to // to try to allow first guy to complete // else { DWORD newTag = ++g_NetInfoTag; UNLOCK_NET_LIST(); pnewInfo = NetInfo_Build( TRUE ); if ( !pnewInfo ) { DNSDBG( ANY, ( "ERROR: GrabNetworkInfo() failed -- no netinfo blob!\n" )); goto Done; } LOCK_NET_LIST(); 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; } // // make copy of global (new or reset) // pnewInfo = NetInfo_Copy( g_NetworkInfo ); Unlock: UNLOCK_NET_LIST(); NetInfo_Free( poldInfo ); Done: 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_NET_LIST(); if ( g_NetworkInfo ) { NetInfo_Free( g_NetworkInfo ); g_NetworkInfo = NULL; } UNLOCK_NET_LIST(); } // // 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: 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(); } else { ClearLocalAddrArray(); } // // 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 PRPC_DNS_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 = (PRPC_DNS_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 )); // // do operation for particular id // - update netinfo clears cached copy if ( Id == POKE_OP_UPDATE_NETINFO ) { if ( Cookie == POKE_COOKIE_UPDATE_NETINFO ) { UpdateNetworkInfo( NULL ); } } } // // End config.c //