/**********************************************************************/ /** Microsoft Windows NT **/ /** Copyright(c) Microsoft Corp., 1993 **/ /**********************************************************************/ /* main.cxx This module contains the main startup code for the SMTP Service. FILE HISTORY: KeithMo 07-Mar-1993 Created. JohnL ???? MuraliK 11-July-1995 Used Ipc() functions from Inetsvcs.dll */ #define INCL_INETSRV_INCS #include "smtpinc.h" #include "inetsvcs.h" #include #include // ATL Header files #define _ATL_NO_DEBUG_CRT #define _ASSERTE _ASSERT #define _WINDLL #include "atlbase.h" extern CComModule _Module; #include "atlcom.h" #undef _WINDLL #ifdef _ATL_STATIC_REGISTRY #include #include #endif #include // // RPC related includes // extern "C" { #include #include }; #include extern DWORD g_cMaxConnectionObjs; BOOL InitializeSmtpServiceRpc( IN LPCSTR pszServiceName, IN RPC_IF_HANDLE hRpcInterface ); BOOL CleanupSmtpServiceRpc( VOID ); // // Private constants. // BOOL fAnySecureFilters = FALSE; // // for PDC hack // #define VIRTUAL_ROOTS_KEY_A "Virtual Roots" #define HTTP_EXT_MAPS "Script Map" #define SMTP_MODULE_NAME "smtpsvc" // // Global startup named event // DWORD GlobalInitializeStatus = 0; BOOL g_ServiceBooted = FALSE; // // Private globals. // #define INITIALIZE_IPC 0x00000001 #define INITIALIZE_SOCKETS 0x00000002 #define INITIALIZE_ACCESS 0x00000004 #define INITIALIZE_SERVICE 0x00000008 #define INITIALIZE_CONNECTIONS 0x00000010 #define INITIALIZE_DISCOVERY 0x00000020 #define INITIALIZE_RPC 0x00000040 #define INITIALIZE_GLOBALS 0x00000080 #define INITIALIZE_COMMON_DLLS 0x00000100 #define INITIALIZE_ROUTE_SORT 0x00000200 #define INITIALIZE_FIO 0x00000400 DEFINE_TSVC_INFO_INTERFACE(); DECLARE_DEBUG_PRINTS_OBJECT(); #ifndef _NO_TRACING_ #include DEFINE_GUID(IisSmtpServerGuid, 0x784d8907, 0xaa8c, 0x11d2, 0x92, 0x5e, 0x00, 0xc0, 0x4f, 0x72, 0xd9, 0x0e); #else DECLARE_DEBUG_VARIABLE(); #endif // // The following critical section synchronizes execution in ServiceEntry(). // This is necessary because the NT Service Controller may reissue a service // start notification immediately after we have set our status to stopped. // This can lead to an unpleasant race condition in ServiceEntry() as one // thread cleans up global state as another thread is initializing it. // CRITICAL_SECTION g_csServiceEntryLock; // // Private prototypes. // extern VOID SmtpOnConnect( IN SOCKET sNew, IN SOCKADDR_IN * psockaddr, //Should be SOCKADDR * IN PVOID pEndpointContext, IN PVOID pAtqEndpointObject ); extern VOID SmtpOnConnectEx( VOID * patqContext, DWORD cbWritten, DWORD err, OVERLAPPED * lpo ); VOID SmtpCompletion( PVOID pvContext, DWORD cbWritten, DWORD dwCompletionStatus, OVERLAPPED * lpo ); APIERR InitializeService( LPVOID pContext ); APIERR TerminateService( LPVOID pContext ); VOID TerminateInstances( PSMTP_IIS_SERVICE pService); /************************************************************ * Symbolic Constants ************************************************************/ static TCHAR szTcpipPath[] = TEXT("System\\CurrentControlSet\\Services\\Tcpip\\Parameters"); /************************************************************ * ATL Module ************************************************************/ CComModule _Module; BEGIN_OBJECT_MAP(ObjectMap) END_OBJECT_MAP() #if 0 /************************************************************ * ATL Module ************************************************************/ CComModule _Module; BEGIN_OBJECT_MAP(ObjectMap) END_OBJECT_MAP() #endif //+--------------------------------------------------------------------------- // // Function: // // DllEntryPoint // // Synopsis: // Arguments: // Returns: // See Win32 SDK // // History: // // Richard Kamicar (rkamicar) 5 January 1996 // // Notes: // // If we find we need this per service, we can move it out of here.. // //---------------------------------------------------------------------------- BOOL WINAPI DllEntryPoint(HINSTANCE hInst, DWORD dwReason, LPVOID lpvContext) { switch (dwReason) { case DLL_PROCESS_ATTACH: #ifndef _NO_TRACING_ CREATE_DEBUG_PRINT_OBJECT( SMTP_MODULE_NAME, IisSmtpServerGuid); #else CREATE_DEBUG_PRINT_OBJECT( SMTP_MODULE_NAME); SET_DEBUG_FLAGS( 0); #endif // // To help performance, cancel thread attach and detach notifications // _Module.Init(ObjectMap, hInst); DisableThreadLibraryCalls((HMODULE) hInst); InitializeCriticalSection( &g_csServiceEntryLock ); break; case DLL_PROCESS_DETACH: // Shutdown ATL _Module.Term(); #ifdef _NO_TRACING_ DBG_CLOSE_LOG_FILE(); #endif DELETE_DEBUG_PRINT_OBJECT(); DeleteCriticalSection( &g_csServiceEntryLock ); break; case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: break; } return TRUE; } BOOL WINAPI DllMain (HANDLE hInst, ULONG dwReason, LPVOID lpvReserve) { return DllEntryPoint((HINSTANCE) hInst, dwReason, lpvReserve); } // // Public functions. // VOID ServiceEntry( DWORD cArgs, LPSTR pArgs[], PTCPSVCS_GLOBAL_DATA pGlobalData // unused ) /*++ Routine: This is the "real" entrypoint for the service. When the Service Controller dispatcher is requested to start a service, it creates a thread that will begin executing this routine. Arguments: cArgs - Number of command line arguments to this service. pArgs - Pointers to the command line arguments. Returns: None. Does not return until service is stopped. --*/ { APIERR err = NO_ERROR; BOOL fInitSvcObject = FALSE; EnterCriticalSection( &g_csServiceEntryLock ); if ( !InitCommonDlls() ) { DBGPRINTF(( DBG_CONTEXT, "[ServiceEntry] InitCommonDlls failed! Bailing\n" )); err = GetLastError(); LeaveCriticalSection( &g_csServiceEntryLock ); goto notify_scm; } InitAsyncTrace(); GlobalInitializeStatus |= INITIALIZE_COMMON_DLLS; if (!InitializeCache()) goto exit; GlobalInitializeStatus |= INITIALIZE_FIO; // // Initialize Globals // err = InitializeGlobals(); if ( err != NO_ERROR ) { goto exit; } GlobalInitializeStatus |= INITIALIZE_GLOBALS; // Initialize the service status structure. // g_pInetSvc = new SMTP_IIS_SERVICE( SMTP_SERVICE_NAME_A, SMTP_MODULE_NAME, SMTP_PARAMETERS_KEY, INET_SMTP_SVC_ID, INET_SMTP_SVCLOC_ID, TRUE, 0, SmtpOnConnect, SmtpOnConnectEx, SmtpCompletion ); // // If we couldn't allocate memory for the service info struct, then the // machine is really hosed. // if( ( g_pInetSvc != NULL ) && g_pInetSvc->IsActive() ) { err = ((SMTP_IIS_SERVICE *)g_pInetSvc)->LoadAdvancedQueueingDll(); if( err != NO_ERROR ) goto exit; fInitSvcObject = TRUE; err = g_pInetSvc->StartServiceOperation( SERVICE_CTRL_HANDLER(), InitializeService, TerminateService ); if ( err ) { // // The event has already been logged // DBGPRINTF(( DBG_CONTEXT, "SMTP ServiceEntry: StartServiceOperation returned %d\n", err )); } } else if (g_pInetSvc == NULL) { err = ERROR_NOT_ENOUGH_MEMORY; } else { err = g_pInetSvc->QueryCurrentServiceError(); } exit: if ( g_pInetSvc != NULL ) { g_pInetSvc->CloseService( ); } TerminateGlobals( ); if( GlobalInitializeStatus & INITIALIZE_FIO) { TerminateCache(); } if( GlobalInitializeStatus & INITIALIZE_COMMON_DLLS) { TerminateCommonDlls(); } TermAsyncTrace(); LeaveCriticalSection( &g_csServiceEntryLock ); notify_scm: // // We need to tell the Service Control Manager that the service // is stopped if we haven't called g_pInetSvc->StartServiceOperation. // 1) InitCommonDlls fails, or // 2) InitializeGlobals failed, or // 3) new operator failed, or // 4) SMTP_IIS_SERVICE constructor couldn't initialize properly // if ( !fInitSvcObject ) { SERVICE_STATUS_HANDLE hsvcStatus; SERVICE_STATUS svcStatus; hsvcStatus = RegisterServiceCtrlHandler( SMTP_SERVICE_NAME, SERVICE_CTRL_HANDLER() ); if ( hsvcStatus != NULL_SERVICE_STATUS_HANDLE ) { svcStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS; svcStatus.dwCurrentState = SERVICE_STOPPED; svcStatus.dwWin32ExitCode = err; svcStatus.dwServiceSpecificExitCode = err; svcStatus.dwControlsAccepted = 0; svcStatus.dwCheckPoint = 0; svcStatus.dwWaitHint = 0; SetServiceStatus( hsvcStatus, (LPSERVICE_STATUS) &svcStatus ); } } } // ServiceEntry // // Private functions. // APIERR InitializeService( LPVOID pContext ) /*++ Routine: This function initializes the various SMTP Service components. Arguments: lpContext - Pointer to the service object Returns: NO_ERROR if successful, otherwise a Win32 status code. --*/ { APIERR err; DWORD dwErr = NO_ERROR; PSMTP_IIS_SERVICE psi = (PSMTP_IIS_SERVICE)pContext; MB mb( (IMDCOM*) psi->QueryMDObject() ); STR TempString; char szTcpipName[MAX_PATH + 1]; BOOL bUpdatedDomain; BOOL bUpdatedFQDN; HRESULT hr; g_IsShuttingDown = FALSE; TraceFunctEnter("InitializeService"); DBGPRINTF(( DBG_CONTEXT, "initializing Smtp service\n" )); SetLastError(NO_ERROR); psi->StartHintFunction(); hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); if (FAILED(hr)) { DBGPRINTF(( DBG_CONTEXT, "Cannot CoInitialize, error %lu\n", hr )); FatalTrace(0,"Cannot CoInitialize, error %d",hr); // TraceFunctLeave(); // return hr; } g_ProductType = 5; psi->StartHintFunction(); //g_ProductType = 0; if ( !mb.Open( "/LM/SMTPSVC/", METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE )) { DBGPRINTF(( DBG_CONTEXT, "InitializeService: Cannot open path %s, error %lu\n", "/LM/SMTPSVC/", GetLastError() )); g_pInetSvc->ShutdownService( ); TraceFunctLeave(); return ERROR_SERVICE_DISABLED; } g_ServiceBooted = TRUE; // // Initialize the Default Domain, Fully Qualified Domain Name (FQDN) settings. // The service will use the default TCP/IP settings in the control panel for these // values if the user has never modified the settings. // DWORD tmp; if (!mb.GetDword("", MD_UPDATED_DEFAULT_DOMAIN, IIS_MD_UT_SERVER, &tmp)) { bUpdatedDomain = FALSE; } else { bUpdatedDomain = !!tmp; } if (!mb.GetDword("", MD_UPDATED_FQDN, IIS_MD_UT_SERVER, &tmp)) { bUpdatedFQDN = FALSE; } else { bUpdatedFQDN = !!tmp; } psi->StartHintFunction(); szTcpipName[0] = '\0'; lstrcpyn(szTcpipName, g_ComputerName, MAX_PATH); // // will need to check against TCP/IP settings // HKEY hkeyTcpipParam = NULL; DWORD SizeOfBuffer = 0; DWORD cbOffset; DWORD dwType; DWORD err2; if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szTcpipPath, 0, KEY_QUERY_VALUE, &hkeyTcpipParam) == ERROR_SUCCESS) { SizeOfBuffer = MAX_PATH; err2 = RegQueryValueEx(hkeyTcpipParam, "Hostname", 0, &dwType, (LPBYTE)szTcpipName, &SizeOfBuffer); if (err2 != ERROR_SUCCESS || SizeOfBuffer <= 1 || dwType != REG_SZ) { lstrcpyn(szTcpipName, g_ComputerName, MAX_PATH); } else { cbOffset = SizeOfBuffer - 1; szTcpipName[cbOffset] = '.'; SizeOfBuffer = MAX_PATH - (cbOffset); err2 = RegQueryValueEx(hkeyTcpipParam, "Domain", 0, &dwType, (LPBYTE)szTcpipName + cbOffset + 1, &SizeOfBuffer); if (err2 != ERROR_SUCCESS || SizeOfBuffer <= 1 || dwType != REG_SZ) { szTcpipName[cbOffset] = '\0'; } } _VERIFY(RegCloseKey(hkeyTcpipParam) == ERROR_SUCCESS); } ((SMTP_IIS_SERVICE *) g_pInetSvc)->SetTcpipName(szTcpipName); if (!bUpdatedDomain) { TempString.Reset(); if(! mb.GetStr("", MD_DEFAULT_DOMAIN_VALUE, IIS_MD_UT_SERVER, &TempString) || TempString.IsEmpty()) { mb.SetString("", MD_DEFAULT_DOMAIN_VALUE, IIS_MD_UT_SERVER, szTcpipName); } else { if (lstrcmpi(szTcpipName,TempString.QueryStr())) // // no match, update // { mb.SetString("", MD_DEFAULT_DOMAIN_VALUE, IIS_MD_UT_SERVER, szTcpipName); } } } if (!bUpdatedFQDN) { TempString.Reset(); if(! mb.GetStr("", MD_FQDN_VALUE, IIS_MD_UT_SERVER, &TempString) || TempString.IsEmpty()) { mb.SetString("", MD_FQDN_VALUE, IIS_MD_UT_SERVER, szTcpipName); } else { if (lstrcmpi(szTcpipName,TempString.QueryStr())) // // no match, update // { mb.SetString("", MD_FQDN_VALUE, IIS_MD_UT_SERVER, szTcpipName); } } } if (!mb.GetDword("", MD_MAX_MAIL_OBJECTS, IIS_MD_UT_SERVER, &g_cMaxConnectionObjs)) { g_cMaxConnectionObjs = 5000; } mb.Close(); psi->StartHintFunction(); // // Initialize various components. The ordering of the // components is somewhat limited. Globals should be // initialized first, then the event logger. After // the event logger is initialized, the other components // may be initialized in any order with one exception. // InitializeSockets must be the last initialization // routine called. It kicks off the main socket connection // thread. // if( err = psi->InitializeDiscovery( )) { DBGPRINTF(( DBG_CONTEXT, "psi->InitializeDiscovery failed, error %lu\n", err )); FatalTrace(0,"psi->InitializeDiscovery failed %d\n",err); TraceFunctLeave(); return err; } GlobalInitializeStatus |= INITIALIZE_DISCOVERY; if( err = psi->InitializeSockets( ) ) { DBGPRINTF(( DBG_CONTEXT, "cannot initialize service, error %lu\n", err )); FatalTrace(0,"psi->InitializeSockets failed %d\n",err); TraceFunctLeave(); return err; } GlobalInitializeStatus |= INITIALIZE_SOCKETS; psi->StartHintFunction(); if(!InitializeSmtpServiceRpc(SMTP_SERVICE_NAME, smtp_ServerIfHandle)) { err = GetLastError(); DBGPRINTF(( DBG_CONTEXT, "cannot initialize RPC service, error %lu\n", err )); FatalTrace(0,"InitializeSmtpServiceRpc failed %d\n",err); TraceFunctLeave(); return err; } GlobalInitializeStatus |= INITIALIZE_RPC; // // Reset any Service Principal Names (for Kerberos) that may have been // registered // if (psi->ResetServicePrincipalNames()) { DebugTrace( 0, "Unable to reset Kerberos Principal Names %lu, will try later", GetLastError()); } // // Read and activate all the instances configured // InitializeInstances( psi ); // // Success! // DBGPRINTF(( DBG_CONTEXT, "SMTP Service initialized\n" )); TraceFunctLeave(); return NO_ERROR; } // InitializeService APIERR TerminateService(IN LPVOID pContext) /*++ Routine: This function cleans up the various SMTP Service components. Arguments: pContext - Pointer to the service object Returns: NO_ERROR if successful, otherwise a Win32 status code. --*/ { PSMTP_IIS_SERVICE psi = (PSMTP_IIS_SERVICE)pContext; DWORD err; TraceFunctEnter("TerminateService"); if(!g_ServiceBooted) { ErrorTrace(NULL, "Smtp service not started, returning"); return NO_ERROR; } g_ServiceBooted = FALSE ; g_IsShuttingDown = TRUE; DBG_ASSERT( pContext == g_pInetSvc); DBGPRINTF(( DBG_CONTEXT, " SMTP terminating service\n" )); // // Components should be terminated in reverse // initialization order. // //get an exclusive lock on the server. //this will wait until all RPCs have //exited out of the server and then //return psi->AcquireServiceExclusiveLock(); psi->ReleaseServiceExclusiveLock(); TerminateInstances(psi); g_pInetSvc->ShutdownService( ); if( GlobalInitializeStatus & INITIALIZE_DISCOVERY) { if ( (err = psi->TerminateDiscovery()) != NO_ERROR) { DBGPRINTF(( DBG_CONTEXT, "TerminateDiscovery() failed. Error = %u\n", err)); } } if( GlobalInitializeStatus & INITIALIZE_SOCKETS) { psi->CleanupSockets( ); } TsFlushMetaCache(METACACHE_SMTP_SERVER_ID,TRUE); if( GlobalInitializeStatus & INITIALIZE_RPC) { CleanupSmtpServiceRpc(); } CoFreeUnusedLibraries(); CoUninitialize(); DBGPRINTF(( DBG_CONTEXT,"service terminated\n" )); TraceFunctLeave(); return NO_ERROR; } // TerminateService