Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

799 lines
19 KiB

  1. /**********************************************************************/
  2. /** Microsoft Windows NT **/
  3. /** Copyright(c) Microsoft Corp., 1993 **/
  4. /**********************************************************************/
  5. /*
  6. main.cxx
  7. This module contains the main startup code for the SMTP Service.
  8. FILE HISTORY:
  9. KeithMo 07-Mar-1993 Created.
  10. JohnL ????
  11. MuraliK 11-July-1995 Used Ipc() functions from Inetsvcs.dll
  12. */
  13. #define INCL_INETSRV_INCS
  14. #include "smtpinc.h"
  15. #include "inetsvcs.h"
  16. #include <metacach.hxx>
  17. #include <dbgutil.h>
  18. // ATL Header files
  19. #define _ATL_NO_DEBUG_CRT
  20. #define _ASSERTE _ASSERT
  21. #define _WINDLL
  22. #include "atlbase.h"
  23. extern CComModule _Module;
  24. #include "atlcom.h"
  25. #undef _WINDLL
  26. #ifdef _ATL_STATIC_REGISTRY
  27. #include <statreg.h>
  28. #include <statreg.cpp>
  29. #endif
  30. #include <atlimpl.cpp>
  31. //
  32. // RPC related includes
  33. //
  34. extern "C" {
  35. #include <inetinfo.h>
  36. #include <smtpsvc.h>
  37. };
  38. #include <smtpinet.h>
  39. extern DWORD g_cMaxConnectionObjs;
  40. BOOL
  41. InitializeSmtpServiceRpc(
  42. IN LPCSTR pszServiceName,
  43. IN RPC_IF_HANDLE hRpcInterface
  44. );
  45. BOOL CleanupSmtpServiceRpc(
  46. VOID
  47. );
  48. //
  49. // Private constants.
  50. //
  51. BOOL fAnySecureFilters = FALSE;
  52. //
  53. // for PDC hack
  54. //
  55. #define VIRTUAL_ROOTS_KEY_A "Virtual Roots"
  56. #define HTTP_EXT_MAPS "Script Map"
  57. #define SMTP_MODULE_NAME "smtpsvc"
  58. //
  59. // Global startup named event
  60. //
  61. DWORD GlobalInitializeStatus = 0;
  62. BOOL g_ServiceBooted = FALSE;
  63. //
  64. // Private globals.
  65. //
  66. #define INITIALIZE_IPC 0x00000001
  67. #define INITIALIZE_SOCKETS 0x00000002
  68. #define INITIALIZE_ACCESS 0x00000004
  69. #define INITIALIZE_SERVICE 0x00000008
  70. #define INITIALIZE_CONNECTIONS 0x00000010
  71. #define INITIALIZE_DISCOVERY 0x00000020
  72. #define INITIALIZE_RPC 0x00000040
  73. #define INITIALIZE_GLOBALS 0x00000080
  74. #define INITIALIZE_COMMON_DLLS 0x00000100
  75. #define INITIALIZE_ROUTE_SORT 0x00000200
  76. #define INITIALIZE_FIO 0x00000400
  77. DEFINE_TSVC_INFO_INTERFACE();
  78. DECLARE_DEBUG_PRINTS_OBJECT();
  79. #ifndef _NO_TRACING_
  80. #include <initguid.h>
  81. DEFINE_GUID(IisSmtpServerGuid,
  82. 0x784d8907, 0xaa8c, 0x11d2, 0x92, 0x5e, 0x00, 0xc0, 0x4f, 0x72, 0xd9, 0x0e);
  83. #else
  84. DECLARE_DEBUG_VARIABLE();
  85. #endif
  86. //
  87. // The following critical section synchronizes execution in ServiceEntry().
  88. // This is necessary because the NT Service Controller may reissue a service
  89. // start notification immediately after we have set our status to stopped.
  90. // This can lead to an unpleasant race condition in ServiceEntry() as one
  91. // thread cleans up global state as another thread is initializing it.
  92. //
  93. CRITICAL_SECTION g_csServiceEntryLock;
  94. //
  95. // Private prototypes.
  96. //
  97. extern VOID SmtpOnConnect( IN SOCKET sNew,
  98. IN SOCKADDR_IN * psockaddr, //Should be SOCKADDR *
  99. IN PVOID pEndpointContext,
  100. IN PVOID pAtqEndpointObject );
  101. extern VOID
  102. SmtpOnConnectEx(
  103. VOID * patqContext,
  104. DWORD cbWritten,
  105. DWORD err,
  106. OVERLAPPED * lpo
  107. );
  108. VOID
  109. SmtpCompletion(
  110. PVOID pvContext,
  111. DWORD cbWritten,
  112. DWORD dwCompletionStatus,
  113. OVERLAPPED * lpo
  114. );
  115. APIERR InitializeService( LPVOID pContext );
  116. APIERR TerminateService( LPVOID pContext );
  117. VOID TerminateInstances( PSMTP_IIS_SERVICE pService);
  118. /************************************************************
  119. * Symbolic Constants
  120. ************************************************************/
  121. static TCHAR szTcpipPath[] = TEXT("System\\CurrentControlSet\\Services\\Tcpip\\Parameters");
  122. /************************************************************
  123. * ATL Module
  124. ************************************************************/
  125. CComModule _Module;
  126. BEGIN_OBJECT_MAP(ObjectMap)
  127. END_OBJECT_MAP()
  128. #if 0
  129. /************************************************************
  130. * ATL Module
  131. ************************************************************/
  132. CComModule _Module;
  133. BEGIN_OBJECT_MAP(ObjectMap)
  134. END_OBJECT_MAP()
  135. #endif
  136. //+---------------------------------------------------------------------------
  137. //
  138. // Function:
  139. //
  140. // DllEntryPoint
  141. //
  142. // Synopsis:
  143. // Arguments:
  144. // Returns:
  145. // See Win32 SDK
  146. //
  147. // History:
  148. //
  149. // Richard Kamicar (rkamicar) 5 January 1996
  150. //
  151. // Notes:
  152. //
  153. // If we find we need this per service, we can move it out of here..
  154. //
  155. //----------------------------------------------------------------------------
  156. BOOL WINAPI
  157. DllEntryPoint(HINSTANCE hInst, DWORD dwReason, LPVOID lpvContext)
  158. {
  159. switch (dwReason)
  160. {
  161. case DLL_PROCESS_ATTACH:
  162. #ifndef _NO_TRACING_
  163. CREATE_DEBUG_PRINT_OBJECT( SMTP_MODULE_NAME, IisSmtpServerGuid);
  164. #else
  165. CREATE_DEBUG_PRINT_OBJECT( SMTP_MODULE_NAME);
  166. SET_DEBUG_FLAGS( 0);
  167. #endif
  168. //
  169. // To help performance, cancel thread attach and detach notifications
  170. //
  171. _Module.Init(ObjectMap, hInst);
  172. DisableThreadLibraryCalls((HMODULE) hInst);
  173. InitializeCriticalSection( &g_csServiceEntryLock );
  174. break;
  175. case DLL_PROCESS_DETACH:
  176. // Shutdown ATL
  177. _Module.Term();
  178. #ifdef _NO_TRACING_
  179. DBG_CLOSE_LOG_FILE();
  180. #endif
  181. DELETE_DEBUG_PRINT_OBJECT();
  182. DeleteCriticalSection( &g_csServiceEntryLock );
  183. break;
  184. case DLL_THREAD_ATTACH:
  185. break;
  186. case DLL_THREAD_DETACH:
  187. break;
  188. }
  189. return TRUE;
  190. }
  191. BOOL WINAPI DllMain (HANDLE hInst, ULONG dwReason, LPVOID lpvReserve)
  192. {
  193. return DllEntryPoint((HINSTANCE) hInst, dwReason, lpvReserve);
  194. }
  195. //
  196. // Public functions.
  197. //
  198. VOID
  199. ServiceEntry(
  200. DWORD cArgs,
  201. LPSTR pArgs[],
  202. PTCPSVCS_GLOBAL_DATA pGlobalData // unused
  203. )
  204. /*++
  205. Routine:
  206. This is the "real" entrypoint for the service. When
  207. the Service Controller dispatcher is requested to
  208. start a service, it creates a thread that will begin
  209. executing this routine.
  210. Arguments:
  211. cArgs - Number of command line arguments to this service.
  212. pArgs - Pointers to the command line arguments.
  213. Returns:
  214. None. Does not return until service is stopped.
  215. --*/
  216. {
  217. APIERR err = NO_ERROR;
  218. BOOL fInitSvcObject = FALSE;
  219. EnterCriticalSection( &g_csServiceEntryLock );
  220. if ( !InitCommonDlls() )
  221. {
  222. DBGPRINTF(( DBG_CONTEXT,
  223. "[ServiceEntry] InitCommonDlls failed! Bailing\n" ));
  224. err = GetLastError();
  225. LeaveCriticalSection( &g_csServiceEntryLock );
  226. goto notify_scm;
  227. }
  228. InitAsyncTrace();
  229. GlobalInitializeStatus |= INITIALIZE_COMMON_DLLS;
  230. if (!InitializeCache()) goto exit;
  231. GlobalInitializeStatus |= INITIALIZE_FIO;
  232. //
  233. // Initialize Globals
  234. //
  235. err = InitializeGlobals();
  236. if ( err != NO_ERROR )
  237. {
  238. goto exit;
  239. }
  240. GlobalInitializeStatus |= INITIALIZE_GLOBALS;
  241. // Initialize the service status structure.
  242. //
  243. g_pInetSvc = new SMTP_IIS_SERVICE(
  244. SMTP_SERVICE_NAME_A,
  245. SMTP_MODULE_NAME,
  246. SMTP_PARAMETERS_KEY,
  247. INET_SMTP_SVC_ID,
  248. INET_SMTP_SVCLOC_ID,
  249. TRUE,
  250. 0,
  251. SmtpOnConnect,
  252. SmtpOnConnectEx,
  253. SmtpCompletion
  254. );
  255. //
  256. // If we couldn't allocate memory for the service info struct, then the
  257. // machine is really hosed.
  258. //
  259. if( ( g_pInetSvc != NULL ) && g_pInetSvc->IsActive() )
  260. {
  261. err = ((SMTP_IIS_SERVICE *)g_pInetSvc)->LoadAdvancedQueueingDll();
  262. if( err != NO_ERROR )
  263. goto exit;
  264. fInitSvcObject = TRUE;
  265. err = g_pInetSvc->StartServiceOperation(
  266. SERVICE_CTRL_HANDLER(),
  267. InitializeService,
  268. TerminateService
  269. );
  270. if ( err )
  271. {
  272. //
  273. // The event has already been logged
  274. //
  275. DBGPRINTF(( DBG_CONTEXT,
  276. "SMTP ServiceEntry: StartServiceOperation returned %d\n",
  277. err ));
  278. }
  279. }
  280. else if (g_pInetSvc == NULL)
  281. {
  282. err = ERROR_NOT_ENOUGH_MEMORY;
  283. }
  284. else
  285. {
  286. err = g_pInetSvc->QueryCurrentServiceError();
  287. }
  288. exit:
  289. if ( g_pInetSvc != NULL )
  290. {
  291. g_pInetSvc->CloseService( );
  292. }
  293. TerminateGlobals( );
  294. if( GlobalInitializeStatus & INITIALIZE_FIO)
  295. {
  296. TerminateCache();
  297. }
  298. if( GlobalInitializeStatus & INITIALIZE_COMMON_DLLS)
  299. {
  300. TerminateCommonDlls();
  301. }
  302. TermAsyncTrace();
  303. LeaveCriticalSection( &g_csServiceEntryLock );
  304. notify_scm:
  305. //
  306. // We need to tell the Service Control Manager that the service
  307. // is stopped if we haven't called g_pInetSvc->StartServiceOperation.
  308. // 1) InitCommonDlls fails, or
  309. // 2) InitializeGlobals failed, or
  310. // 3) new operator failed, or
  311. // 4) SMTP_IIS_SERVICE constructor couldn't initialize properly
  312. //
  313. if ( !fInitSvcObject ) {
  314. SERVICE_STATUS_HANDLE hsvcStatus;
  315. SERVICE_STATUS svcStatus;
  316. hsvcStatus = RegisterServiceCtrlHandler( SMTP_SERVICE_NAME,
  317. SERVICE_CTRL_HANDLER() );
  318. if ( hsvcStatus != NULL_SERVICE_STATUS_HANDLE ) {
  319. svcStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS;
  320. svcStatus.dwCurrentState = SERVICE_STOPPED;
  321. svcStatus.dwWin32ExitCode = err;
  322. svcStatus.dwServiceSpecificExitCode = err;
  323. svcStatus.dwControlsAccepted = 0;
  324. svcStatus.dwCheckPoint = 0;
  325. svcStatus.dwWaitHint = 0;
  326. SetServiceStatus( hsvcStatus, (LPSERVICE_STATUS) &svcStatus );
  327. }
  328. }
  329. } // ServiceEntry
  330. //
  331. // Private functions.
  332. //
  333. APIERR
  334. InitializeService(
  335. LPVOID pContext
  336. )
  337. /*++
  338. Routine:
  339. This function initializes the various SMTP Service components.
  340. Arguments:
  341. lpContext - Pointer to the service object
  342. Returns:
  343. NO_ERROR if successful, otherwise a Win32
  344. status code.
  345. --*/
  346. {
  347. APIERR err;
  348. DWORD dwErr = NO_ERROR;
  349. PSMTP_IIS_SERVICE psi = (PSMTP_IIS_SERVICE)pContext;
  350. MB mb( (IMDCOM*) psi->QueryMDObject() );
  351. STR TempString;
  352. char szTcpipName[MAX_PATH + 1];
  353. BOOL bUpdatedDomain;
  354. BOOL bUpdatedFQDN;
  355. HRESULT hr;
  356. g_IsShuttingDown = FALSE;
  357. TraceFunctEnter("InitializeService");
  358. DBGPRINTF(( DBG_CONTEXT,
  359. "initializing Smtp service\n" ));
  360. SetLastError(NO_ERROR);
  361. psi->StartHintFunction();
  362. hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
  363. if (FAILED(hr))
  364. {
  365. DBGPRINTF(( DBG_CONTEXT,
  366. "Cannot CoInitialize, error %lu\n",
  367. hr ));
  368. FatalTrace(0,"Cannot CoInitialize, error %d",hr);
  369. // TraceFunctLeave();
  370. // return hr;
  371. }
  372. g_ProductType = 5;
  373. psi->StartHintFunction();
  374. //g_ProductType = 0;
  375. if ( !mb.Open( "/LM/SMTPSVC/",
  376. METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE ))
  377. {
  378. DBGPRINTF(( DBG_CONTEXT,
  379. "InitializeService: Cannot open path %s, error %lu\n",
  380. "/LM/SMTPSVC/", GetLastError() ));
  381. g_pInetSvc->ShutdownService( );
  382. TraceFunctLeave();
  383. return ERROR_SERVICE_DISABLED;
  384. }
  385. g_ServiceBooted = TRUE;
  386. //
  387. // Initialize the Default Domain, Fully Qualified Domain Name (FQDN) settings.
  388. // The service will use the default TCP/IP settings in the control panel for these
  389. // values if the user has never modified the settings.
  390. //
  391. DWORD tmp;
  392. if (!mb.GetDword("", MD_UPDATED_DEFAULT_DOMAIN, IIS_MD_UT_SERVER, &tmp))
  393. {
  394. bUpdatedDomain = FALSE;
  395. }
  396. else
  397. {
  398. bUpdatedDomain = !!tmp;
  399. }
  400. if (!mb.GetDword("", MD_UPDATED_FQDN, IIS_MD_UT_SERVER, &tmp))
  401. {
  402. bUpdatedFQDN = FALSE;
  403. }
  404. else
  405. {
  406. bUpdatedFQDN = !!tmp;
  407. }
  408. psi->StartHintFunction();
  409. szTcpipName[0] = '\0';
  410. lstrcpyn(szTcpipName, g_ComputerName, MAX_PATH);
  411. //
  412. // will need to check against TCP/IP settings
  413. //
  414. HKEY hkeyTcpipParam = NULL;
  415. DWORD SizeOfBuffer = 0;
  416. DWORD cbOffset;
  417. DWORD dwType;
  418. DWORD err2;
  419. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szTcpipPath, 0, KEY_QUERY_VALUE, &hkeyTcpipParam) == ERROR_SUCCESS)
  420. {
  421. SizeOfBuffer = MAX_PATH;
  422. err2 = RegQueryValueEx(hkeyTcpipParam, "Hostname", 0, &dwType, (LPBYTE)szTcpipName, &SizeOfBuffer);
  423. if (err2 != ERROR_SUCCESS || SizeOfBuffer <= 1 || dwType != REG_SZ)
  424. {
  425. lstrcpyn(szTcpipName, g_ComputerName, MAX_PATH);
  426. }
  427. else
  428. {
  429. cbOffset = SizeOfBuffer - 1;
  430. szTcpipName[cbOffset] = '.';
  431. SizeOfBuffer = MAX_PATH - (cbOffset);
  432. err2 = RegQueryValueEx(hkeyTcpipParam, "Domain", 0, &dwType, (LPBYTE)szTcpipName + cbOffset + 1, &SizeOfBuffer);
  433. if (err2 != ERROR_SUCCESS || SizeOfBuffer <= 1 || dwType != REG_SZ)
  434. {
  435. szTcpipName[cbOffset] = '\0';
  436. }
  437. }
  438. _VERIFY(RegCloseKey(hkeyTcpipParam) == ERROR_SUCCESS);
  439. }
  440. ((SMTP_IIS_SERVICE *) g_pInetSvc)->SetTcpipName(szTcpipName);
  441. if (!bUpdatedDomain)
  442. {
  443. TempString.Reset();
  444. if(! mb.GetStr("", MD_DEFAULT_DOMAIN_VALUE, IIS_MD_UT_SERVER, &TempString) ||
  445. TempString.IsEmpty())
  446. {
  447. mb.SetString("", MD_DEFAULT_DOMAIN_VALUE, IIS_MD_UT_SERVER, szTcpipName);
  448. }
  449. else
  450. {
  451. if (lstrcmpi(szTcpipName,TempString.QueryStr()))
  452. //
  453. // no match, update
  454. //
  455. {
  456. mb.SetString("", MD_DEFAULT_DOMAIN_VALUE, IIS_MD_UT_SERVER, szTcpipName);
  457. }
  458. }
  459. }
  460. if (!bUpdatedFQDN)
  461. {
  462. TempString.Reset();
  463. if(! mb.GetStr("", MD_FQDN_VALUE, IIS_MD_UT_SERVER, &TempString) ||
  464. TempString.IsEmpty())
  465. {
  466. mb.SetString("", MD_FQDN_VALUE, IIS_MD_UT_SERVER, szTcpipName);
  467. }
  468. else
  469. {
  470. if (lstrcmpi(szTcpipName,TempString.QueryStr()))
  471. //
  472. // no match, update
  473. //
  474. {
  475. mb.SetString("", MD_FQDN_VALUE, IIS_MD_UT_SERVER, szTcpipName);
  476. }
  477. }
  478. }
  479. if (!mb.GetDword("", MD_MAX_MAIL_OBJECTS, IIS_MD_UT_SERVER, &g_cMaxConnectionObjs))
  480. {
  481. g_cMaxConnectionObjs = 5000;
  482. }
  483. mb.Close();
  484. psi->StartHintFunction();
  485. //
  486. // Initialize various components. The ordering of the
  487. // components is somewhat limited. Globals should be
  488. // initialized first, then the event logger. After
  489. // the event logger is initialized, the other components
  490. // may be initialized in any order with one exception.
  491. // InitializeSockets must be the last initialization
  492. // routine called. It kicks off the main socket connection
  493. // thread.
  494. //
  495. if( err = psi->InitializeDiscovery( ))
  496. {
  497. DBGPRINTF(( DBG_CONTEXT,
  498. "psi->InitializeDiscovery failed, error %lu\n",
  499. err ));
  500. FatalTrace(0,"psi->InitializeDiscovery failed %d\n",err);
  501. TraceFunctLeave();
  502. return err;
  503. }
  504. GlobalInitializeStatus |= INITIALIZE_DISCOVERY;
  505. if( err = psi->InitializeSockets( ) )
  506. {
  507. DBGPRINTF(( DBG_CONTEXT,
  508. "cannot initialize service, error %lu\n",
  509. err ));
  510. FatalTrace(0,"psi->InitializeSockets failed %d\n",err);
  511. TraceFunctLeave();
  512. return err;
  513. }
  514. GlobalInitializeStatus |= INITIALIZE_SOCKETS;
  515. psi->StartHintFunction();
  516. if(!InitializeSmtpServiceRpc(SMTP_SERVICE_NAME, smtp_ServerIfHandle))
  517. {
  518. err = GetLastError();
  519. DBGPRINTF(( DBG_CONTEXT,
  520. "cannot initialize RPC service, error %lu\n",
  521. err ));
  522. FatalTrace(0,"InitializeSmtpServiceRpc failed %d\n",err);
  523. TraceFunctLeave();
  524. return err;
  525. }
  526. GlobalInitializeStatus |= INITIALIZE_RPC;
  527. //
  528. // Reset any Service Principal Names (for Kerberos) that may have been
  529. // registered
  530. //
  531. if (psi->ResetServicePrincipalNames()) {
  532. DebugTrace(
  533. 0,
  534. "Unable to reset Kerberos Principal Names %lu, will try later",
  535. GetLastError());
  536. }
  537. //
  538. // Read and activate all the instances configured
  539. //
  540. InitializeInstances( psi );
  541. //
  542. // Success!
  543. //
  544. DBGPRINTF(( DBG_CONTEXT, "SMTP Service initialized\n" ));
  545. TraceFunctLeave();
  546. return NO_ERROR;
  547. } // InitializeService
  548. APIERR TerminateService(IN LPVOID pContext)
  549. /*++
  550. Routine:
  551. This function cleans up the various SMTP Service components.
  552. Arguments:
  553. pContext - Pointer to the service object
  554. Returns:
  555. NO_ERROR if successful, otherwise a Win32
  556. status code.
  557. --*/
  558. {
  559. PSMTP_IIS_SERVICE psi = (PSMTP_IIS_SERVICE)pContext;
  560. DWORD err;
  561. TraceFunctEnter("TerminateService");
  562. if(!g_ServiceBooted)
  563. {
  564. ErrorTrace(NULL, "Smtp service not started, returning");
  565. return NO_ERROR;
  566. }
  567. g_ServiceBooted = FALSE ;
  568. g_IsShuttingDown = TRUE;
  569. DBG_ASSERT( pContext == g_pInetSvc);
  570. DBGPRINTF(( DBG_CONTEXT,
  571. " SMTP terminating service\n" ));
  572. //
  573. // Components should be terminated in reverse
  574. // initialization order.
  575. //
  576. //get an exclusive lock on the server.
  577. //this will wait until all RPCs have
  578. //exited out of the server and then
  579. //return
  580. psi->AcquireServiceExclusiveLock();
  581. psi->ReleaseServiceExclusiveLock();
  582. TerminateInstances(psi);
  583. g_pInetSvc->ShutdownService( );
  584. if( GlobalInitializeStatus & INITIALIZE_DISCOVERY)
  585. {
  586. if ( (err = psi->TerminateDiscovery()) != NO_ERROR)
  587. {
  588. DBGPRINTF(( DBG_CONTEXT, "TerminateDiscovery() failed. Error = %u\n",
  589. err));
  590. }
  591. }
  592. if( GlobalInitializeStatus & INITIALIZE_SOCKETS)
  593. {
  594. psi->CleanupSockets( );
  595. }
  596. TsFlushMetaCache(METACACHE_SMTP_SERVER_ID,TRUE);
  597. if( GlobalInitializeStatus & INITIALIZE_RPC)
  598. {
  599. CleanupSmtpServiceRpc();
  600. }
  601. CoFreeUnusedLibraries();
  602. CoUninitialize();
  603. DBGPRINTF(( DBG_CONTEXT,"service terminated\n" ));
  604. TraceFunctLeave();
  605. return NO_ERROR;
  606. } // TerminateService