/*++ Copyright (c) 1997-2001 Microsoft Corporation Module Name: dnsrslvr.c Abstract: DNS Resolver Service Main service module. Author: Glenn Curtis (glennc) 25-Feb-1997 Revision History: Jim Gilroy (jamesg) March 2000 cleanup Jim Gilroy (jamesg) Nov 2000 rewrite --*/ #include "local.h" #ifdef BUILD_W2K #include #else #include #endif // // Service control // SERVICE_STATUS ServiceStatus; SERVICE_STATUS_HANDLE ServiceStatusHandle = (SERVICE_STATUS_HANDLE) 0; #ifdef BUILD_W2K PSVCS_GLOBAL_DATA g_pSvchostData; #else PSVCHOST_GLOBAL_DATA g_pSvchostData; #endif HANDLE g_hStopEvent; BOOL g_StopFlag; BOOL g_fServiceControlHandled; // // Service state // #define RES_STATUS_BEGIN 0x0cc00000 #define RES_STATUS_ZERO_INIT 0x0cc00001 #define RES_STATUS_CREATED_CS 0x0cc00002 #define RES_STATUS_CREATED_EVENT 0x0cc00003 #define RES_STATUS_READ_REGISTRY 0x0cc00004 #define RES_STATUS_ALLOC_CACHE 0x0cc00005 #define RES_STATUS_START_NOTIFY 0x0cc00006 #define RES_STATUS_START_IP_LIST 0x0cc00007 #define RES_STATUS_START_RPC 0x0cc00008 #define RES_STATUS_REG_CONTROL 0x0cc00009 #define RES_STATUS_RUNNING 0x0cc00100 #define RES_STATUS_STOPPING 0x0cc00300 #define RES_STATUS_SIGNALED_STOP 0x0cc00301 #define RES_STATUS_STOP_RPC 0x0cc00302 #define RES_STATUS_STOP_NOTIFY 0x0cc00303 #define RES_STATUS_STOP_IP_LIST 0x0cc00304 #define RES_STATUS_FREE_CACHE 0x0cc00305 #define RES_STATUS_FREE_NET_INFO 0x0cc00306 #define RES_STATUS_FREE_IP_LIST 0x0cc00307 #define RES_STATUS_FREE_SERVICE_NOTIFY 0x0cc00308 #define RES_STATUS_DEL_EVENT 0x0cc00309 #define RES_STATUS_DEL_CS 0x0cc00310 #define RES_STATUS_END 0x0cc00400 DWORD g_ResolverStatus = RES_STATUS_BEGIN; // // Initialization cleanup\state // // Track what we intialized for safer\faster cleanup // #define INITFLAG_CS_CREATED 0x00000001 #define INITFLAG_WINSOCK 0x00000002 #define INITFLAG_EVENTS_CREATED 0x00000004 #define INITFLAG_CACHE_CREATED 0x00000008 #define INITFLAG_NOTIFY_STARTED 0x00000010 #define INITFLAG_IP_LIST_CREATED 0x00000020 #define INITFLAG_RPC_SERVER_STARTED 0x00000100 DWORD g_InitState; // // Critical sections used // CRITICAL_SECTION CacheCritSec; CRITICAL_SECTION NetworkListCritSec; CRITICAL_SECTION NetworkFailureCritSec; // // Logging control // BOOL g_LogTraceInfo = TRUE; // // Private protos // DWORD ResolverInitialize( VOID ); VOID ResolverShutdown( IN DWORD ErrorCode ); VOID ResolverControlHandler( IN DWORD Opcode ); DWORD ResolverUpdateStatus( VOID ); // // Service routines // // // NT5 version -- inside services.exe // #ifdef BUILD_W2K VOID SVCS_ENTRY_POINT( DWORD NumArgs, LPTSTR * ArgsArray, PSVCS_GLOBAL_DATA pSvcsGlobalData, HANDLE SvcRefHandle ) /*++ Routine Description: Main main entry point of resolver service Arguments: NumArgs - number of arguments to service start call ArgsArray - array of ptrs to arguments in service start call Return Value: None --*/ { // // save pointer to service data block // g_pSvchostData = pSvcsGlobalData; ResolverInitialize(); } #else // // Whistler+ version -- in svchost.exe process // VOID SvchostPushServiceGlobals( PSVCHOST_GLOBAL_DATA pGlobals ) { g_pSvchostData = pGlobals; } VOID ServiceMain( IN DWORD NumArgs, IN LPTSTR * ArgsArray ) /*++ Routine Description: Main entry point of resolver service. Arguments: NumArgs - number of strings specified in ArgsArray. ArgsArray - array of ptrs to arguments in service start call Return Value: None --*/ { // // Make sure svchost.exe gave us global data // ASSERT( g_pSvchostData != NULL ); // // Startup service, then exit // ResolverInitialize(); } #endif // NT4 \ Whistler+ switch VOID ResolverInitFailure( IN DNS_STATUS Status, IN DWORD EventId, IN DWORD MemEventId, IN PSTR pszDebugString ) /*++ Routine Description: Handle resolver init failure. Function exists to avoid duplicate code. Arguments: Return Value: None --*/ { WCHAR numberString[16]; PWSTR eventStrings[1]; DNSLOG_TIME(); DNSLOG_F1( "Resolver Init Failure" ); DNSLOG_F2( " Failure = %s", pszDebugString ); DNSLOG_F2( " Status = %d", Status ); DNSLOG_F1( "" ); DNSDBG( ANY, ( "Resolver Init FAILED!\n" "\tname = %s\n" "\tstatus = %d\n" "\tevent id = %d\n" "\tmem event = %08x\n", pszDebugString, Status, EventId, MemEventId )); DnsDbg_PrintfToDebugger( "ResolverInitialize - Returning status %d 0x%08x\n" "\tname = %s\n", Status, Status, pszDebugString ); // // log in memory event // LogEventInMemory( MemEventId, Status ); // // log event // - convert status to string // wsprintfW( numberString, L"0x%.8X", Status ); eventStrings[0] = numberString; ResolverLogEvent( EventId, EVENTLOG_ERROR_TYPE, 1, eventStrings, Status ); // clean up ResolverShutdown( Status ); } DWORD ResolverInitialize( VOID ) /*++ Routine Description: This function initializes the DNS Caching Resolver service. Arguments: InitState - Returns a flag to indicate how far we got with initializing the service before an error occurred. Return Value: ERROR_SUCCESS if successful. ErrorCode on failure. --*/ { DNS_STATUS status = NO_ERROR; // // init service state // g_ResolverStatus = RES_STATUS_BEGIN; g_InitState = 0; g_StopFlag = FALSE; g_hStopEvent = NULL; g_fServiceControlHandled = FALSE; // // initialize logging // DNSLOG_INIT(); DNSLOG_F1( "DNS Caching Resolver Service - ResolverInitialize" ); #if DBG #if 0 Dns_StartDebug( 0, "dnsres.flag", NULL, "dnsres.log", 0 ); #endif Dns_StartDebugEx( 0, // no flag value "dnsres.flag", NULL, // no external flag "dnsres.log", 0, // no wrap limit FALSE, // don't use existing global FALSE, TRUE // make this file global ); #endif DNSDBG( INIT, ( "DNS resolver startup.\n" )); IF_DNSDBG( START_BREAK ) { // since resolver moved to NetworkServices permissions do // not properly bring up ntsd; instead just give time // to attach debugger Sleep( 20000 ); } // // initialize service status block // ServiceStatusHandle = (SERVICE_STATUS_HANDLE) 0; ServiceStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS; ServiceStatus.dwCurrentState = SERVICE_START_PENDING; ServiceStatus.dwControlsAccepted = 0; ServiceStatus.dwCheckPoint = 0; ServiceStatus.dwWaitHint = 5000; ServiceStatus.dwWin32ExitCode = NO_ERROR; ServiceStatus.dwServiceSpecificExitCode = 0; ResolverUpdateStatus(); // // init globals to zero // ZeroInitIpListGlobals(); ZeroNetworkConfigGlobals(); g_ResolverStatus = RES_STATUS_ZERO_INIT; // // initialize all our critical sections as soon as we can // LogEventInMemory( RES_EVENT_INITCRIT_START, 0 ); InitializeCriticalSection( & CacheCritSec ); InitializeCriticalSection( & NetworkListCritSec ); InitializeCriticalSection( & NetworkFailureCritSec ); LogEventInMemory( RES_EVENT_INITCRIT_END,0 ); // // init our dnslib heap to use dnsapi heap // // this is important because we currently mix and match records // created inside dnsapi (hosts file and query) with a few that // we roll on our own; need this to be common // Dns_LibHeapReset( DnsApiAlloc, DnsApiRealloc, DnsApiFree ); // // init winsock // Dns_InitializeWinsock(); g_InitState |= INITFLAG_WINSOCK; // // shutdown event // g_hStopEvent = CreateEvent( NULL, // no security descriptor TRUE, // do not use automatic reset FALSE, // initial state: not signalled NULL // no name ); if ( !g_hStopEvent ) { status = GetLastError(); ResolverInitFailure( status, 0, 0, "CreateEvent() failed" ); return status; } g_InitState |= INITFLAG_EVENTS_CREATED; g_ResolverStatus = RES_STATUS_CREATED_EVENT; ResolverUpdateStatus(); // // initialize our global registry values // - force this just once on startup so we have the // relevant cache params; after that read only on // demand when building netinfo blobs ReadRegistryConfig(); // // Set the query timeouts to be used from defaults or registry // Dns_InitQueryTimeouts(); // // init socket caching // - improves perf and prevents socket DOS attack // - default cache to 10 sockets // // DCR: create global for socket caching // Dns_CacheSocketInit( 10 ); #if 0 // // start cache on demand now // // // initialize cache // status = Cache_Initialize(); LogEventInMemory( RES_EVENT_INITCACHE, status ); if ( status != NO_ERROR ) { ResolverInitFailure( status, EVENT_DNS_CACHE_START_FAILURE_NO_CONTROL, 0, "Cache_Initialize() failed" ); return status; } g_InitState |= INITFLAG_CACHE_CREATED; g_ResolverStatus = RES_STATUS_ALLOC_CACHE; ResolverUpdateStatus(); #endif // // notification thread (host file and registry) // StartNotify(); g_InitState |= INITFLAG_NOTIFY_STARTED; g_ResolverStatus = RES_STATUS_START_NOTIFY; ResolverUpdateStatus(); // // IP notification thread // status = InitIpListAndNotification(); if ( status != ERROR_SUCCESS ) { ResolverInitFailure( status, 0, 0, "IP list init failed" ); return status; } g_InitState |= INITFLAG_IP_LIST_CREATED; g_ResolverStatus = RES_STATUS_START_IP_LIST; ResolverUpdateStatus(); // // register control handler // allows us to receive service requests // ServiceStatusHandle = RegisterServiceCtrlHandlerW( DNS_RESOLVER_SERVICE, ResolverControlHandler ); if ( !ServiceStatusHandle ) { status = GetLastError(); ResolverInitFailure( status, EVENT_DNS_CACHE_START_FAILURE_LOW_MEMORY, RES_EVENT_REGISTER_SCH, "Call to RegisterServiceCtrlHandlerW failed!" ); return status; } g_ResolverStatus = RES_STATUS_REG_CONTROL; ResolverUpdateStatus(); // // initialize RPC interfaces // - bump our requested stack size up to 8K // (RPC uses 1800 bytes before we get the stack, // the new() operator followed by the heap code uses // another 1200 -- leaving only about a 1000 for // DNS) // LogEventInMemory( RES_EVENT_STARTRPC, 0 ); #if 0 // should not be necessary // default for all svchost instances has been increased if ( status != NO_ERROR ) { DNSDBG( ANY, ( "RpcMgmtSetServerStackSize( 2000 ) = %d\n", status )); } #endif status = Rpc_Initialize(); #if 0 status = g_pSvchostData->StartRpcServer( SERVER_INTERFACE_NAME_W, DnsResolver_ServerIfHandle ); #endif if ( status != NO_ERROR ) { LogEventInMemory( RES_EVENT_STATUS, status ); if ( status == RPC_S_TYPE_ALREADY_REGISTERED || status == RPC_NT_TYPE_ALREADY_REGISTERED ) { DNSLOG_TIME(); DNSLOG_F1( " Call to StartRpcServer returned warning that" ); DNSLOG_F1( " the service is already running!" ); DNSLOG_F2( " RpcPipeName : %S", RESOLVER_INTERFACE_NAME_W ); DNSLOG_F1( " Going to just continue running . . ." ); DNSLOG_F1( "" ); DnsDbg_PrintfToDebugger( "DNS Client (dnsrslvr.dll) - Call to StartRpcServer\n" "returned warning that the service is already running!\n" "RpcPipeName : %S" "Going to just continue running . . .\n", RESOLVER_INTERFACE_NAME_W ); status = NO_ERROR; } else { DNSDBG( ANY, ( "RPC init FAILED! status = %d\n" "\tpipe name = %s\n", status, RESOLVER_INTERFACE_NAME_W )); ResolverInitFailure( status, EVENT_DNS_CACHE_START_FAILURE_NO_RPC, 0, "Call to StartRpcServer failed!" ); return status; } } g_ResolverStatus = RES_STATUS_START_RPC; g_InitState |= INITFLAG_RPC_SERVER_STARTED; // // successful startup // - indicate running // - indicate what service control messages we want to get // ServiceStatus.dwCurrentState = SERVICE_RUNNING; ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PARAMCHANGE | SERVICE_ACCEPT_NETBINDCHANGE; ServiceStatus.dwWaitHint = 0; ServiceStatus.dwWin32ExitCode = NO_ERROR; ResolverUpdateStatus(); g_ResolverStatus = RES_STATUS_RUNNING; DNSLOG_F1( "ResolverInitialize - Successful" ); DNSLOG_F1( "" ); return NO_ERROR; } VOID ResolverShutdown( IN DWORD ErrorCode ) /*++ Routine Description: This function shuts down the DNS cache service. Arguments: ErrorCode - Supplies the error code of the failure Return Value: None. --*/ { DWORD status = NO_ERROR; LONG existingStopFlag; DNSLOG_TIME(); DNSLOG_F1( "DNS Caching Resolver Service - ResolverShutdown" ); DnsDbg_PrintfToDebugger( "DNS Client - ResolverShutdown!\n" ); // // indicate shutdown // - but interlock to avoid dual shutdown // existingStopFlag = InterlockedExchange( &g_StopFlag, (LONG) TRUE ); if ( existingStopFlag ) { DNS_ASSERT( FALSE ); return; } DNS_ASSERT( g_StopFlag ); // // indicate stop in progress // ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING; ServiceStatus.dwCheckPoint = 1; ServiceStatus.dwWaitHint = 60000; ResolverUpdateStatus(); g_ResolverStatus = RES_STATUS_STOPPING; // // wakeup threads to shut down // g_StopFlag = TRUE; if ( g_hStopEvent ) { if ( !SetEvent(g_hStopEvent) ) { DnsDbg_PrintfToDebugger( "DNSCACHE: Error setting g_hStopEvent %lu\n", GetLastError()); DNS_ASSERT( FALSE ); } } g_ResolverStatus = RES_STATUS_SIGNALED_STOP; // // cleanup RPC // if ( g_InitState & INITFLAG_RPC_SERVER_STARTED ) { LogEventInMemory( RES_EVENT_STOPRPC, 0 ); Rpc_Shutdown(); #if 0 //status = g_pSvchostData->StopRpcServer( DnsResolver_ServerIfHandle ); #endif LogEventInMemory( RES_EVENT_STATUS, status ); } g_ResolverStatus = RES_STATUS_STOP_RPC; // // re-signal stop within lock // LOCK_CACHE_NO_START(); g_StopFlag = TRUE; if ( g_hStopEvent ) { if ( !SetEvent(g_hStopEvent) ) { DnsDbg_PrintfToDebugger( "DNSCACHE: Error setting g_hStopEvent %lu\n", GetLastError()); DNS_ASSERT( FALSE ); } } UNLOCK_CACHE(); // // stop notify thread // if ( g_InitState & INITFLAG_NOTIFY_STARTED ) { ShutdownNotify(); } g_ResolverStatus = RES_STATUS_STOP_NOTIFY; // // stop IP notify thread // if ( g_InitState & INITFLAG_IP_LIST_CREATED ) { ShutdownIpListAndNotify(); } g_ResolverStatus = RES_STATUS_STOP_IP_LIST; // // cleanup cache // Cache_Shutdown(); g_ResolverStatus = RES_STATUS_FREE_CACHE; // // cleanup service notification list // //CleanupServiceNotification(); //g_ResolverStatus = RES_STATUS_FREE_SERVICE_NOTIFY; // // cleanup network info globals // CleanupNetworkInfo(); g_ResolverStatus = RES_STATUS_FREE_NET_INFO; // // cleanup winsock // cleanup socket caching also // - this is irrelevant for other services running in // our process so we shouldn't leave the handles open // if ( g_InitState & INITFLAG_WINSOCK ) { Dns_CacheSocketCleanup(); Dns_CleanupWinsock(); } // // cleanup main shutdown event // if ( g_InitState & INITFLAG_EVENTS_CREATED ) { if ( g_hStopEvent ) { CloseHandle(g_hStopEvent); g_hStopEvent = NULL; } } g_ResolverStatus = RES_STATUS_DEL_EVENT; // // delete critical sections // if ( g_InitState & INITFLAG_CS_CREATED ) { LogEventInMemory( RES_EVENT_DELCRIT_START, 0 ); DeleteCriticalSection( &CacheCritSec ); DeleteCriticalSection( &NetworkListCritSec ); DeleteCriticalSection( &NetworkFailureCritSec ); LogEventInMemory( RES_EVENT_DELCRIT_END, 0 ); } g_ResolverStatus = RES_STATUS_DEL_CS; // // cleanup complete // tell Service Controller that we are stopped // ServiceStatus.dwCurrentState = SERVICE_STOPPED; ServiceStatus.dwControlsAccepted = 0; ServiceStatus.dwWin32ExitCode = ErrorCode; ServiceStatus.dwServiceSpecificExitCode = 0; ServiceStatus.dwCheckPoint = 0; ServiceStatus.dwWaitHint = 0; ResolverUpdateStatus(); g_ResolverStatus = RES_STATUS_END; DNSLOG_F1( "ResolverShutdown - Finished" ); DNSLOG_F1( "" ); } BOOL GetServiceControlLock( VOID ) /*++ Routine Description: Get exclusive access handling service control message. Arguments: None Return Value: TRUE -- have exclusive access to handle SCM, other threads locked out FALSE -- another thread still handling SCM --*/ { BOOL fresult; // // set handled flag, if not previously set // if not previous set -> we have exclusive access // fresult = InterlockedCompareExchange( &g_fServiceControlHandled, (LONG) TRUE, // new value (LONG) 0 // previous value to do exchange ); return !fresult; } VOID ReleaseServiceControlLock( VOID ) /*++ Routine Description: Release service control exclusive access. Arguments: None Return Value: None --*/ { // // clear handled flag // - since GetServiceControlLock() uses CompareExchange // we can just clear without interlock // DNS_ASSERT( g_fServiceControlHandled ); g_fServiceControlHandled = FALSE; } VOID ResolverControlHandler( IN DWORD Opcode ) /*++ Routine Description: Service control handler for DNS cache service. Arguments: Opcode - specifies service action Return Value: None. --*/ { LogEventInMemory( RES_EVENT_SCH, Opcode ); DNSLOG_TIME(); DNSLOG_F2( "ResolverControlHandler - Recieved opcode %d", Opcode ); DNSDBG( ANY, ( "\n\n" "ResolverControlHandler() Opcode = %d\n", Opcode )); // // handle various service control codes // switch( Opcode ) { case SERVICE_CONTROL_STOP: ResolverShutdown( NO_ERROR ); break; case SERVICE_CONTROL_PARAMCHANGE : DNSLOG_F1( " Handle Paramchange" ); DNSLOG_F1( "" ); if ( !GetServiceControlLock() ) { return; } // // rebuild -- with cache flush // HandleConfigChange( "SC -- ParamChange", TRUE // flush cache ); // // signal other services about PnP // // SendServiceNotifications(); ReleaseServiceControlLock(); break; case SERVICE_CONTROL_NETBINDENABLE: case SERVICE_CONTROL_NETBINDDISABLE: DNSLOG_F1( " Handle NetBindEnable\\Disable" ); DNSLOG_F1( "" ); if ( !GetServiceControlLock() ) { return; } // // rebuild -- with cache flush // HandleConfigChange( "SC -- NetBind", TRUE // flush cache ); ReleaseServiceControlLock(); break; case SERVICE_CONTROL_INTERROGATE: case SERVICE_CONTROL_NETBINDADD: case SERVICE_CONTROL_NETBINDREMOVE: default: DNSLOG_F1( " This is an unknown opcode, ignoring ..." ); DNSLOG_F1( "" ); break; } // // update service status // ResolverUpdateStatus(); DNSLOG_F2( "Resolver Controll Handler (opcode = %d) -- returning", Opcode ); DNSDBG( ANY, ( "Leaving ResolverControlHandler( %d )\n\n\n", Opcode )); } DWORD ResolverUpdateStatus( VOID ) /*++ Routine Description: Update service controller with current service status. Arguments: None. Return Value: Return code from SetServiceStatus. --*/ { DWORD status = NO_ERROR; DNSDBG( TRACE, ( "ResolverUpdateStatus()\n" )); // // bump the checkpoint // if status, set it ServiceStatus.dwCheckPoint++; #if 0 if ( Status ) { ServiceStatus.dwWin32ExitCode = Status; } #endif // // DCR_FIX: this doesn't do much // usually people write something like this that sets the service // state AND does the update // // QUESTION: what's up with the LogEventInMemory? // if ( ServiceStatusHandle == (SERVICE_STATUS_HANDLE) 0 ) { LogEventInMemory( RES_EVENT_UPDATE_STATUS, ERROR_INVALID_HANDLE ); return ERROR_INVALID_HANDLE; } LogEventInMemory( RES_EVENT_UPDATE_STATUS, ServiceStatus.dwWin32ExitCode ); if ( ! SetServiceStatus( ServiceStatusHandle, &ServiceStatus ) ) { status = GetLastError(); } LogEventInMemory( RES_EVENT_UPDATE_STATUS, status ); return status; } // // Event logging // VOID ResolverLogEvent( IN DWORD MessageId, IN WORD EventType, IN DWORD StringCount, IN PWSTR * StringArray, IN DWORD ErrorCode ) /*++ Routine Description: Log to eventlog. Arguments: MessageId -- event message id EventType -- event type (error, warning, info, etc.) StringCount -- string arg count StringArray -- imbedded strings ErrorCode -- error code for data section of event Return Value: None --*/ { HANDLE hlog; PVOID pdata = NULL; // // open resolver as event source // // note: we don't keep log open because events are few // hlog = RegisterEventSourceW( NULL, DNS_RESOLVER_SERVICE ); if ( hlog == NULL ) { return; } if ( ErrorCode != NO_ERROR ) { pdata = &ErrorCode; } // // Write to event log // // DCR: should get suppression technology here // ReportEventW( hlog, EventType, 0, // event category MessageId, (PSID) NULL, (WORD) StringCount, sizeof(DWORD), StringArray, (PVOID) pdata ); DeregisterEventSource( hlog ); } // // End dnsrslvr.c //