windows-nt/Source/XPSP1/NT/inetsrv/iis/svcs/smtp/server/main.cxx
2020-09-26 16:20:57 +08:00

800 lines
19 KiB
C++

/**********************************************************************/
/** 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 <metacach.hxx>
#include <dbgutil.h>
// 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 <statreg.h>
#include <statreg.cpp>
#endif
#include <atlimpl.cpp>
//
// RPC related includes
//
extern "C" {
#include <inetinfo.h>
#include <smtpsvc.h>
};
#include <smtpinet.h>
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 <initguid.h>
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