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.

1406 lines
37 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. globals.cxx
  5. Abstract:
  6. This module contains global variable definitions shared by the
  7. various SMTP Service components.
  8. Author:
  9. KeithMo 07-Mar-1993 Created.
  10. --*/
  11. #define INCL_INETSRV_INCS
  12. #include "smtpinc.h"
  13. #include "smtpcli.hxx"
  14. #include "smtpout.hxx"
  15. #include "dropdir.hxx"
  16. #include <isplat.h>
  17. #include "mailmsg_i.c"
  18. #include "mailmsgi_i.c"
  19. #include "aqueue_i.c"
  20. #include "aqstore.hxx"
  21. #include <dnsapi.h>
  22. //
  23. // Version string for this server
  24. //
  25. #define MSSMTP_VERSION_STR_IIS "Microsoft-IIS/K2"
  26. #define MSSMTP_VERSION_STR_W95 "Microsoft-PWS-95/K2"
  27. #define MSSMTP_VERSION_STR_NTW "Microsoft-PWS/K2"
  28. //
  29. // Set to the largest of the three
  30. //
  31. #define MSSMTP_VERSION_STR_MAX MSSMTP_VERSION_STR_W95
  32. //
  33. // Creates the version string
  34. //
  35. #define MAKE_VERSION_STRING( _s ) ("Server: " ##_s "\r\n")
  36. //
  37. // MIME version we say we support
  38. //
  39. #define SMTP_MIME_VERSION_STR "MIME-version: 1.0"
  40. #define SMTP_TEMP_DIR_NAME " "
  41. //
  42. // Server type string
  43. //
  44. CHAR g_szServerType[ sizeof(MSSMTP_VERSION_STR_MAX)];
  45. DWORD g_cbServerType = 0;
  46. CHAR szServerVersion[sizeof(MAKE_VERSION_STRING(MSSMTP_VERSION_STR_MAX))];
  47. DWORD cbServerVersionString = 0;
  48. DWORD g_ProductType = 5;
  49. PLATFORM_TYPE g_SmtpPlatformType = PtNtServer;
  50. //computer name
  51. CHAR g_ComputerName[MAX_PATH + 1];
  52. DWORD g_ComputerNameLength;
  53. // number of procs on system for thread mgmt.
  54. DWORD g_NumProcessors = 1;
  55. CHAR g_VersionString[128];
  56. CHAR g_Password[MAX_PATH + 1];
  57. CHAR g_UserName[MAX_PATH + 1];
  58. CHAR g_DomainName[MAX_PATH + 1];
  59. static char g_BoundaryChars [] = "0123456789abcdefghijklmnopqrstuvwxyz"
  60. "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  61. //Max Objects
  62. DWORD g_cMaxAddressObjects;
  63. DWORD g_cMaxPropertyBagObjects;
  64. DWORD g_cMaxMailObjects;
  65. DWORD g_cMaxEtrnObjects;
  66. DWORD g_cMaxRoutingThreads;
  67. DWORD g_cMaxConnectionObjs = 2000;
  68. BOOL g_CalledSrand;
  69. DWORD g_dwIncMsgId;
  70. //These buffers are associated with every incoming connection - so we
  71. //will need to have atleast those any plus a few more for use in Dir pickup
  72. //and large SSL buffers
  73. DWORD g_cMaxDirBuffers = 2500;
  74. //This buffer is now used primarily as WRITEBUFFER for every connection
  75. //We have decided to go with 32K buffer
  76. //NK** : Make this metabse readable
  77. DWORD g_cMaxDirChangeIoSize = SMTP_WRITE_BUFFER_SIZE;
  78. //loopback address
  79. DWORD g_LoopBackAddr;
  80. unsigned char GlobalIpBuffer[10000];
  81. CShareLockNH g_GlobalLock;
  82. SOCKET g_IpListSocket = INVALID_SOCKET;
  83. WSAOVERLAPPED WsaOverLapped;
  84. HANDLE g_ShutdownHandle = NULL;
  85. HANDLE g_TcpNotifyHandle = NULL;
  86. HANDLE g_FreeLibThreadHandle = NULL;
  87. CTcpRegIpList g_TcpRegIpList;
  88. //
  89. // Notification object used to watch for changes in CAPI stores
  90. //
  91. STORE_CHANGE_NOTIFIER *g_pCAPIStoreChangeNotifier;
  92. //
  93. // Miscellaneous data.
  94. //
  95. LARGE_INTEGER AllocationGranularity; // Page allocation granularity.
  96. HANDLE g_hSysAccToken = NULL;
  97. TCHAR * g_pszSmtpTempDirName; // Name of temporary directory.
  98. DWORD g_PickupWait;
  99. DWORD g_FreeLibInterval = 1; //Interval in min to wait before calling CoFreeUnusedLib
  100. DWORD g_UseMapiDriver = 0;
  101. LONG g_MaxFindThreads;
  102. //
  103. // Platform type
  104. //
  105. PLATFORM_TYPE SmtpPlatformType = PtNtServer;
  106. BOOL g_fIsWindows95 = FALSE;
  107. //
  108. // Statistics.
  109. // used to write statistics counter values to when instance is unknown
  110. //
  111. LPSMTP_SERVER_STATISTICS g_pSmtpStats;
  112. //
  113. // SEO Handle
  114. //
  115. IUnknown *g_punkSEOHandle;
  116. //
  117. // Externals for SEO
  118. //
  119. extern HRESULT SEOGetServiceHandle(IUnknown **);
  120. //
  121. // Generate the string storage space
  122. //
  123. #if 0
  124. # include "strconst.h"
  125. # define CStrM( FriendlyName, ActualString) \
  126. const char PSZ_ ## FriendlyName[] = ActualString;
  127. ConstantStringsForThisModule()
  128. # undef CStrM
  129. #endif
  130. DWORD SmtpDebug;
  131. extern "C" {
  132. BOOL g_IsShuttingDown = FALSE;
  133. }
  134. DWORD g_SmtpInitializeStatus = 0;
  135. TIME_ZONE_INFORMATION tzInfo;
  136. #define MAX_CONNECTION_OBJECTS 5000;
  137. BOOL GetMachineIpAddresses(void);
  138. void DeleteIpListFunction(PVOID IpList);
  139. DWORD TcpRegNotifyThread( LPDWORD lpdw );
  140. DWORD FreeLibThread( LPDWORD lpdw );
  141. USERDELETEFUNC CTcpRegIpList::m_DeleteFunc = DeleteIpListFunction;
  142. //
  143. // eventlog object
  144. //
  145. CEventLogWrapper g_EventLog;
  146. //
  147. // Header Date time cache
  148. //
  149. //PCACHED_DATETIME_FORMATS g_pDateTimeCache = NULL;
  150. static TCHAR szParamPath[] = TEXT("System\\CurrentControlSet\\Services\\SmtpSvc\\Parameters");
  151. static WCHAR szParamPathW[] = L"System\\CurrentControlSet\\Services\\SmtpSvc\\Parameters";
  152. static TCHAR szMaxAddrObjects[] = TEXT("MaxAddressObjects");
  153. static WCHAR szMaxAddrObjectsW[] = L"MaxAddressObjects";
  154. static TCHAR szMaxPropertyBagObjects[] = TEXT("MaxPropertyBagObjects");
  155. static WCHAR szMaxPropertyBagObjectsW[] = L"MaxPropertyBagObjects";
  156. static TCHAR szMaxMailObjects[] = TEXT("MaxMailObjects");
  157. static WCHAR szMaxMailObjectsW[] = L"MaxMailObjects";
  158. static TCHAR szMaxEtrnObjects[] = TEXT("MaxEtrnObjects");
  159. static WCHAR szMaxEtrnObjectsW[] = L"MaxEtrnObjects";
  160. static TCHAR szDirBuffers[] = TEXT("MaxDirectoryBuffers");
  161. static WCHAR szDirBuffersW[] = L"MaxDirectoryBuffers";
  162. static TCHAR szDirBuffersSize[] = TEXT("DirectoryBuffSize");
  163. static WCHAR szDirBuffersSizeW[] = L"DirectoryBufferSize";
  164. static TCHAR szDirPendingIos[] = TEXT("NumDirPendingIos");
  165. static WCHAR szDirPendingIosW[] = L"NumDirPendingIos";
  166. static TCHAR szRoutingThreads[] = TEXT("RoutingThreads");
  167. static WCHAR szRoutingThreadsW[] = L"RoutingThreads";
  168. static TCHAR szProductType[] = TEXT("ProductType");
  169. static WCHAR szProductTypeW[] = L"ProductType";
  170. static TCHAR szResolverSockets[] = TEXT("NumDnsResolverSockets");
  171. static WCHAR szResolverSocketsW[] = L"NumDnsResolverSockets";
  172. static TCHAR szDnsSocketTimeout[] = TEXT("msDnsSocketTimeout");
  173. static WCHAR szDnsSocketTimeoutW[] = L"msDnsSocketTimeout";
  174. static TCHAR szPickupWait[] = TEXT("PickupWait");
  175. static WCHAR szPickupWaitW[] = L"PickupWait";
  176. static TCHAR szMaxFindThreads[] = TEXT("MaxFindThreads");
  177. static WCHAR szMaxFindThreadsW[] = L"MaxFindThreads";
  178. static TCHAR szFreeLibInterval[] = TEXT("FreeLibInterval");
  179. static WCHAR szFreeLibIntervalW[] = L"FreeLibInterval";
  180. static TCHAR szUseMapiDrv[] = TEXT("UseMapiDriver");
  181. static WCHAR szUseMapiDrvW[] = L"UseMapiDriver";
  182. //
  183. // resolver globals
  184. //
  185. DWORD g_ResolverSockets = 10;
  186. DWORD g_DnsSocketTimeout = 60000;
  187. typedef struct tagVERTAG {
  188. LPSTR pszTag;
  189. } VERTAG, *PVERTAG, FAR *LPVERTAG;
  190. VERTAG Tags[] = {
  191. // { "FileDescription" },
  192. // { "OriginalFilename" },
  193. // { "ProductName" },
  194. { "ProductVersion" },
  195. // { "LegalCopyright" },
  196. // { "LegalCopyright" },
  197. };
  198. #define NUM_TAGS (sizeof( Tags ) / sizeof( VERTAG ))
  199. //DWORD ConfigIMCService(void);
  200. DWORD SetVersionStrings( LPSTR lpszFile, LPSTR lpszTitle, LPSTR lpstrOut, DWORD cbOut )
  201. {
  202. static char sz[256], szFormat[256], sz2[256];
  203. int i;
  204. UINT uBytes;
  205. LPVOID lpMem;
  206. DWORD dw = 0, dwSize;
  207. HANDLE hMem;
  208. LPVOID lpsz;
  209. LPDWORD lpLang;
  210. DWORD dwLang2;
  211. BOOL bRC, bFileFound = FALSE;
  212. LPSTR lpstrOrig = lpstrOut ;
  213. //CharUpper( lpszTitle );
  214. if ( dwSize = GetFileVersionInfoSize( lpszFile, &dw ) ) {
  215. if ( hMem = GlobalAlloc( GMEM_MOVEABLE|GMEM_ZEROINIT, (UINT)dwSize ) ) {
  216. lpMem = GlobalLock(hMem);
  217. if (GetFileVersionInfo( lpszFile, 0, dwSize, lpMem ) &&
  218. VerQueryValue( lpMem, "\\VarFileInfo\\Translation",
  219. (LPVOID FAR *)&lpLang, &uBytes ) )
  220. {
  221. dwLang2 = MAKELONG( HIWORD(lpLang[0]), LOWORD(lpLang[0]) );
  222. for( i=0; i<NUM_TAGS; i++ ) {
  223. lpsz = 0 ;
  224. //
  225. // need to do the reverse because most winnt files are wrong
  226. //
  227. wsprintf( sz, "\\StringFileInfo\\%08lx\\%s", lpLang[0], Tags[i].pszTag );
  228. wsprintf( sz2, "\\StringFileInfo\\%08lx\\%s", dwLang2, Tags[i].pszTag );
  229. bRC = VerQueryValue( lpMem, sz, &lpsz, &uBytes ) ||
  230. VerQueryValue( lpMem, sz2, &lpsz, &uBytes ) ;
  231. if( lpsz != 0 )
  232. {
  233. if( uBytes+1 < cbOut )
  234. {
  235. uBytes = min( (UINT)lstrlen( (char*)lpsz ), uBytes ) ;
  236. CopyMemory( lpstrOut, lpsz, uBytes ) ;
  237. lpstrOut[uBytes++] = ' ' ;
  238. lpstrOut += uBytes ;
  239. cbOut -= uBytes ;
  240. }
  241. else
  242. {
  243. GlobalUnlock( hMem );
  244. GlobalFree( hMem );
  245. return (DWORD)(lpstrOut - lpstrOrig) ;
  246. }
  247. }
  248. }
  249. // version info from fixed struct
  250. bRC = VerQueryValue(lpMem,
  251. "\\",
  252. &lpsz,
  253. &uBytes );
  254. #define lpvs ((VS_FIXEDFILEINFO FAR *)lpsz)
  255. static char szVersion[] = "Version: %d.%d.%d.%d" ;
  256. if ( (cbOut > (sizeof( szVersion )*2)) && lpsz ) {
  257. CopyMemory( szFormat, szVersion, sizeof( szVersion ) ) ;
  258. //LoadString( hInst, IDS_VERSION, szFormat, sizeof(szFormat) );
  259. DWORD cbPrint = wsprintf( lpstrOut, szFormat, HIWORD(lpvs->dwFileVersionMS),
  260. LOWORD(lpvs->dwFileVersionMS),
  261. HIWORD(lpvs->dwFileVersionLS),
  262. LOWORD(lpvs->dwFileVersionLS) );
  263. lpstrOut += cbPrint ;
  264. }
  265. bFileFound = TRUE;
  266. } else {
  267. }
  268. GlobalUnlock( hMem );
  269. GlobalFree( hMem );
  270. } else {
  271. }
  272. } else {
  273. }
  274. DWORD dw2 = GetLastError() ;
  275. return (DWORD)(lpstrOut - lpstrOrig) ;
  276. }
  277. BOOL InitServerVersionString( VOID )
  278. {
  279. BOOL fRet = TRUE ;
  280. DWORD szSize;
  281. char szServerPath[MAX_PATH + 1];
  282. char * szOffset;
  283. CopyMemory(szServerPath, "c:\\", sizeof( "c:\\" ) ) ;
  284. g_VersionString [0] = '\0';
  285. HMODULE hModule = GetModuleHandle( "smtpsvc.dll" ) ;
  286. if( hModule != 0 )
  287. {
  288. if( !GetModuleFileName( hModule, szServerPath, sizeof( szServerPath ) ) )
  289. {
  290. lstrcpy( szServerPath, "c:\\") ;
  291. }
  292. else
  293. {
  294. szSize = SetVersionStrings(szServerPath, "", g_VersionString, 128 );
  295. szOffset = strstr(g_VersionString, "Version");
  296. if(szOffset)
  297. {
  298. //Move interesting part of string (including the
  299. //terminating NULL) to front of g_VersionString.
  300. MoveMemory(g_VersionString, szOffset,
  301. szSize+1 - (szOffset - g_VersionString));
  302. }
  303. }
  304. }
  305. return TRUE ;
  306. }
  307. BOOL GetGlobalRegistrySettings(void)
  308. {
  309. BOOL fRet = TRUE;
  310. HKEY hkeySmtp = NULL;
  311. HKEY hkeySub = NULL;
  312. DWORD dwErr;
  313. DWORD dwDisp;
  314. DWORD dwMaxFindThreads;
  315. TraceFunctEnterEx((LPARAM)NULL, "GetGlobalRegistrySettings");
  316. dwErr = RegCreateKeyEx(HKEY_LOCAL_MACHINE, szParamPath, NULL, NULL,
  317. REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkeySmtp, &dwDisp);
  318. if (dwErr != ERROR_SUCCESS)
  319. {
  320. SmtpLogEventEx(SMTP_EVENT_CANNOT_OPEN_SVC_REGKEY, (const char *)SMTP_PARAMETERS_KEY, dwErr);
  321. TraceFunctLeave();
  322. SetLastError(dwErr);
  323. return FALSE;
  324. }
  325. g_cMaxAddressObjects = ReadRegistryDword(hkeySmtp, szMaxAddrObjects, 100000);
  326. StateTrace((LPARAM)NULL, "g_cMaxAddressObjects = %u", g_cMaxAddressObjects);
  327. //NK ** We atleast need as many buffers as many connections we accept
  328. //so I have now tied it to that value
  329. //g_cMaxDirBuffers = ReadRegistryDword(hkeySmtp, szDirBuffers, 5000);
  330. //g_cMaxDirChangeIoSize = ReadRegistryDword(hkeySmtp, szDirBuffersSize, MAX_WRITE_FILE_BLOCK);
  331. g_ResolverSockets = ReadRegistryDword(hkeySmtp, szResolverSockets, 10);
  332. g_DnsSocketTimeout = ReadRegistryDword(hkeySmtp, szDnsSocketTimeout, 60000);
  333. g_PickupWait = ReadRegistryDword(hkeySmtp, szPickupWait, 200);
  334. // don't let them make this wait more than 5 secs. that is too much.
  335. if (g_PickupWait > 5000)
  336. {
  337. g_PickupWait = 5000;
  338. }
  339. //In seems like after the call to unload, the dlls get physically unloaded
  340. //11 min after that. So I am setting the interval by default to 11.
  341. g_FreeLibInterval = ReadRegistryDword(hkeySmtp,szFreeLibInterval, 11);
  342. // don't let them make this wait more than 60 min. that is too much.
  343. if (g_FreeLibInterval > 60)
  344. {
  345. g_FreeLibInterval = 60;
  346. }
  347. dwMaxFindThreads = ReadRegistryDword(hkeySmtp, szMaxFindThreads, 3);
  348. // don't want this to be bigger than the routing threads, but we want at least one.
  349. if (dwMaxFindThreads > 3)
  350. {
  351. dwMaxFindThreads = 3;
  352. }
  353. else if (dwMaxFindThreads <= 0)
  354. {
  355. dwMaxFindThreads = 1;
  356. }
  357. g_MaxFindThreads = dwMaxFindThreads;
  358. RegCloseKey(hkeySmtp);
  359. TraceFunctLeaveEx((LPARAM)NULL);
  360. return fRet;
  361. }
  362. void IpAddressListCallBack (DWORD dwError, DWORD cbTransferred, LPWSAOVERLAPPED LpOverlapped,
  363. DWORD dwFlags)
  364. {
  365. DWORD wsError = 0;
  366. DWORD bytesReturned = 0;
  367. GetMachineIpAddresses();
  368. wsError = WSAIoctl(g_IpListSocket, SIO_ADDRESS_LIST_CHANGE, NULL, 0, NULL,
  369. 0, &bytesReturned, &WsaOverLapped, IpAddressListCallBack);
  370. }
  371. BOOL GetMachineIpAddresses(void)
  372. {
  373. DWORD bytesReturned = 0;
  374. DWORD wsError = 0;
  375. BOOL fRet = FALSE;
  376. g_GlobalLock.ExclusiveLock();
  377. ZeroMemory((void *)GlobalIpBuffer, sizeof(GlobalIpBuffer));
  378. if(g_IpListSocket != INVALID_SOCKET)
  379. {
  380. wsError = WSAIoctl(g_IpListSocket, SIO_ADDRESS_LIST_QUERY, NULL, 0, (LPVOID) GlobalIpBuffer,
  381. sizeof(GlobalIpBuffer), &bytesReturned, NULL, NULL);
  382. if(wsError == 0)
  383. {
  384. fRet = TRUE;
  385. }
  386. }
  387. g_GlobalLock.ExclusiveUnlock();
  388. return fRet;
  389. }
  390. BOOL IsIpInGlobalList(DWORD IpAddress)
  391. {
  392. INT AddressCount = 0;
  393. SOCKET_ADDRESS_LIST * ptr = NULL;
  394. sockaddr_in * Current = NULL;
  395. char Scratch[100];
  396. TraceFunctEnterEx((LPARAM)NULL, "IsIpInGlobalList");
  397. g_GlobalLock.ShareLock();
  398. Scratch[0] = '\0';
  399. ptr = (SOCKET_ADDRESS_LIST *)GlobalIpBuffer;
  400. for (AddressCount = 0; AddressCount < ptr->iAddressCount;AddressCount++)
  401. {
  402. Current = (sockaddr_in *) ptr->Address[AddressCount].lpSockaddr;
  403. if(Current)
  404. {
  405. DebugTrace((LPARAM)NULL," Address - %s", inet_ntoa( Current->sin_addr));
  406. if(Current->sin_addr.s_addr == IpAddress)
  407. {
  408. InetNtoa(*(struct in_addr *) &Current->sin_addr.s_addr, Scratch);
  409. ErrorTrace((LPARAM) NULL, "IpAddress %s is one of mine - Failing connection", Scratch);
  410. g_GlobalLock.ShareUnlock();
  411. TraceFunctLeaveEx((LPARAM)NULL);
  412. return TRUE;
  413. }
  414. }
  415. }
  416. g_GlobalLock.ShareUnlock();
  417. InetNtoa(*(struct in_addr *) &IpAddress, Scratch);
  418. DebugTrace((LPARAM) NULL, "IpAddress %s is not one of mine ", Scratch);
  419. TraceFunctLeaveEx((LPARAM)NULL);
  420. return FALSE;
  421. }
  422. void VerifyFQDNWithGlobalIp(DWORD InstanceId, char * szFQDomainName)
  423. {
  424. INT AddressCount = 0;
  425. SOCKET_ADDRESS_LIST * ptr = NULL;
  426. sockaddr_in * Current = NULL;
  427. char Scratch[100];
  428. Scratch[0] = '\0';
  429. CONST CHAR *apszMsgs[2];
  430. CHAR achInstance[20];
  431. CHAR achIPAddr[20];
  432. PHOSTENT pH = NULL;
  433. //Get the current instnace id
  434. wsprintf( achInstance,
  435. "%lu",
  436. InstanceId );
  437. apszMsgs[1] = achInstance;
  438. g_GlobalLock.ShareLock();
  439. ptr = (SOCKET_ADDRESS_LIST *)GlobalIpBuffer;
  440. for (AddressCount = 0; AddressCount < ptr->iAddressCount;AddressCount++)
  441. {
  442. Current = (sockaddr_in *) ptr->Address[AddressCount].lpSockaddr;
  443. if(Current)
  444. {
  445. ((PSMTP_IIS_SERVICE) g_pInetSvc)->StartHintFunction();
  446. //For each IP address find the host name
  447. pH = gethostbyaddr( (char*)(&((PSOCKADDR_IN)Current)->sin_addr), 4, PF_INET );
  448. if(pH == NULL)
  449. {
  450. SmtpLogEvent( SMTP_EVENT_UNRESOLVED_FQDN, 0, (const CHAR **)NULL, 0 );
  451. }
  452. else if(_strnicmp(pH->h_name,szFQDomainName,strlen(szFQDomainName)))
  453. {
  454. wsprintf( achIPAddr,"%s",inet_ntoa( Current->sin_addr));
  455. apszMsgs[0] = achIPAddr;
  456. SmtpLogEvent( SMTP_EVENT_UNRESOLVED_FQDN,2,apszMsgs,0 );
  457. }
  458. }
  459. }
  460. g_GlobalLock.ShareUnlock();
  461. }
  462. //
  463. // Public functions.
  464. //
  465. APIERR
  466. InitializeGlobals(
  467. VOID
  468. )
  469. /*++
  470. Routine Description:
  471. Initializes global shared variables. Some values are
  472. initialized with constants, others are read from the
  473. configuration registry.
  474. Arguments:
  475. None.
  476. Return Value:
  477. Win32
  478. --*/
  479. {
  480. DWORD err;
  481. DWORD MaxConnections;
  482. SYSTEM_INFO systemInfo;
  483. HRESULT hr = S_OK;
  484. DWORD wsError = 0;
  485. DWORD bytesReturned = 0;
  486. DWORD dwThreadId = 0;
  487. TraceFunctEnter( "InitializeGlobals" );
  488. g_CalledSrand = FALSE;
  489. g_dwIncMsgId = 0;
  490. g_ShutdownHandle = CreateEvent( NULL, TRUE, FALSE, NULL );
  491. if(g_ShutdownHandle == NULL)
  492. {
  493. err = GetLastError();
  494. ErrorTrace(0, "Cannot allocate shutdown handle. err: %u", err);
  495. _ASSERT(err != NO_ERROR);
  496. if(err == NO_ERROR)
  497. SetLastError (ERROR_NOT_ENOUGH_MEMORY);
  498. goto error_exit;
  499. }
  500. g_TcpNotifyHandle =
  501. CreateThread( NULL,
  502. 0,
  503. (LPTHREAD_START_ROUTINE)TcpRegNotifyThread,
  504. NULL,
  505. 0,
  506. &dwThreadId );
  507. if (g_TcpNotifyHandle == NULL )
  508. {
  509. err = GetLastError();
  510. ErrorTrace(0, "Cannot create notify thread. err: %u", err);
  511. _ASSERT(err != NO_ERROR);
  512. if(err == NO_ERROR)
  513. SetLastError (ERROR_NOT_ENOUGH_MEMORY);
  514. goto error_exit;
  515. }
  516. hr = g_EventLog.Initialize("smtpsvc");
  517. if (FAILED(hr)) {
  518. // do nothing
  519. }
  520. g_IpListSocket = socket (AF_INET, SOCK_STREAM, 0);
  521. if(g_IpListSocket != INVALID_SOCKET)
  522. {
  523. GetMachineIpAddresses();
  524. wsError = WSAIoctl(g_IpListSocket, SIO_ADDRESS_LIST_CHANGE, NULL, 0, NULL,
  525. 0, &bytesReturned, &WsaOverLapped, IpAddressListCallBack);
  526. if(wsError == 0)
  527. {
  528. //fRet = TRUE;
  529. }
  530. }
  531. //
  532. // read the global registry settings
  533. //
  534. g_SmtpPlatformType = IISGetPlatformType();
  535. if(!GetGlobalRegistrySettings())
  536. {
  537. FatalTrace(NULL, "Could not read global reg settings!");
  538. TraceFunctLeave();
  539. return ERROR_SERVICE_DISABLED;
  540. }
  541. //thread to periodically call free ununsed libraries
  542. //so dll's can be unloaded
  543. g_FreeLibThreadHandle =
  544. CreateThread( NULL,
  545. 0,
  546. (LPTHREAD_START_ROUTINE)FreeLibThread,
  547. NULL,
  548. 0,
  549. &dwThreadId );
  550. if (g_FreeLibThreadHandle == NULL )
  551. {
  552. err = GetLastError();
  553. ErrorTrace(0, "Cannot create Free Library thread. err: %u", err);
  554. _ASSERT(err != NO_ERROR);
  555. if(err == NO_ERROR)
  556. SetLastError (ERROR_NOT_ENOUGH_MEMORY);
  557. goto error_exit;
  558. }
  559. //
  560. // do global SEO initialization
  561. //
  562. hr = SEOGetServiceHandle(&g_punkSEOHandle);
  563. if (FAILED(hr))
  564. {
  565. ErrorTrace(0, "SEOGetServiceHandle returned %x", hr);
  566. // we're in trouble here. we'll try and continue on, but server events
  567. // probably won't work right
  568. g_punkSEOHandle = NULL;
  569. //SmtpLogEventSimple(SEO_INIT_FAILED, hr);
  570. }
  571. //
  572. // Initialize the server version string based on the platform type
  573. //
  574. InitServerVersionString();
  575. SmtpPlatformType = IISGetPlatformType();
  576. switch ( SmtpPlatformType )
  577. {
  578. case PtNtWorkstation:
  579. lstrcpy(szServerVersion,MAKE_VERSION_STRING(MSSMTP_VERSION_STR_NTW));
  580. lstrcpy(g_szServerType, MSSMTP_VERSION_STR_NTW);
  581. break;
  582. case PtWindows95:
  583. case PtWindows9x:
  584. lstrcpy(szServerVersion,MAKE_VERSION_STRING(MSSMTP_VERSION_STR_W95));
  585. lstrcpy(g_szServerType, MSSMTP_VERSION_STR_W95);
  586. g_fIsWindows95 = TRUE;
  587. break;
  588. default:
  589. //
  590. // Either server or unhandled platform type!
  591. //
  592. DBG_ASSERT(InetIsNtServer(SmtpPlatformType));
  593. lstrcpy(szServerVersion,MAKE_VERSION_STRING(MSSMTP_VERSION_STR_IIS));
  594. lstrcpy(g_szServerType, MSSMTP_VERSION_STR_IIS);
  595. }
  596. g_cbServerType = lstrlen( g_szServerType);
  597. cbServerVersionString = lstrlen(szServerVersion);
  598. //store the computer name
  599. g_ComputerNameLength = MAX_PATH;
  600. if (!GetComputerName(g_ComputerName, &g_ComputerNameLength))
  601. {
  602. err = GetLastError();
  603. ErrorTrace((LPARAM)NULL, "GetComputerName() failed with err %d", err);
  604. TraceFunctLeave();
  605. return err;
  606. }
  607. // number of processors on the system.
  608. GetSystemInfo( &systemInfo );
  609. g_NumProcessors = systemInfo.dwNumberOfProcessors;
  610. g_LoopBackAddr = inet_addr ("127.0.0.1");
  611. g_pSmtpStats = NULL;
  612. //find out what the max connection paramater is
  613. MaxConnections = MAX_CONNECTION_OBJECTS;
  614. DebugTrace(NULL, "g_cMaxConnectionObjs = %d", g_cMaxConnectionObjs);
  615. //allocate some SMTP_CONNECTION objects from CPOOL
  616. if (!SMTP_CONNECTION::Pool.ReserveMemory( g_cMaxConnectionObjs, sizeof(SMTP_CONNECTION) ) )
  617. {
  618. err = GetLastError();
  619. ErrorTrace(0, "ReserveMemory failed for SMTP_CONNECTION. err: %u", err);
  620. _ASSERT(err != NO_ERROR);
  621. if(err == NO_ERROR)
  622. SetLastError (ERROR_NOT_ENOUGH_MEMORY);
  623. goto error_exit;
  624. }
  625. g_SmtpInitializeStatus |= INITIALIZE_INBOUNDPOOL;
  626. //allocate some SMTP_CONNECTION objects from CPOOL
  627. if (!SMTP_CONNOUT::Pool.ReserveMemory(MaxConnections, sizeof(SMTP_CONNOUT) ) )
  628. {
  629. err = GetLastError();
  630. ErrorTrace(0, "ReserveMemory failed for SMTP_CONNOUT. err: %u", err);
  631. _ASSERT(err != NO_ERROR);
  632. if(err == NO_ERROR)
  633. SetLastError (ERROR_NOT_ENOUGH_MEMORY);
  634. goto error_exit;
  635. }
  636. g_SmtpInitializeStatus |= INITIALIZE_OUTBOUNDPOOL;
  637. //allocate some CAddr objects from CPOOL
  638. if (!CAddr::Pool.ReserveMemory(1000, sizeof(CAddr) ) )
  639. {
  640. err = GetLastError();
  641. ErrorTrace(0, "ReserveMemory failed for CAddr. err: %u", err);
  642. _ASSERT(err != NO_ERROR);
  643. if(err == NO_ERROR)
  644. SetLastError (ERROR_NOT_ENOUGH_MEMORY);
  645. goto error_exit;
  646. }
  647. g_SmtpInitializeStatus |= INITIALIZE_ADDRESSPOOL;
  648. if (!CAsyncMx::Pool.ReserveMemory(3000, sizeof(CAsyncMx)))
  649. {
  650. err = GetLastError();
  651. ErrorTrace(0, "ReserveMemory failed for CBuffer. err: %u", err);
  652. _ASSERT(err != NO_ERROR);
  653. if(err == NO_ERROR)
  654. SetLastError (ERROR_NOT_ENOUGH_MEMORY);
  655. goto error_exit;
  656. }
  657. g_SmtpInitializeStatus |= INITIALIZE_CASYNCMX;
  658. if (!CAsyncSmtpDns::Pool.ReserveMemory(4000, sizeof(CAsyncSmtpDns)))
  659. {
  660. err = GetLastError();
  661. ErrorTrace(0, "ReserveMemory failed for CBuffer. err: %u", err);
  662. _ASSERT(err != NO_ERROR);
  663. if(err == NO_ERROR)
  664. SetLastError (ERROR_NOT_ENOUGH_MEMORY);
  665. goto error_exit;
  666. }
  667. g_SmtpInitializeStatus |= INITIALIZE_CASYNCDNS;
  668. //
  669. // Initialize the file handle cache
  670. //
  671. if (!InitializeCache()) {
  672. err = GetLastError();
  673. ErrorTrace(0, "InitializeCache failed err: %u", err);
  674. _ASSERT(err != NO_ERROR);
  675. if(err == NO_ERROR)
  676. SetLastError (ERROR_NOT_ENOUGH_MEMORY);
  677. goto error_exit;
  678. }
  679. g_SmtpInitializeStatus |= INITIALIZE_FILEHC;
  680. if (!CBuffer::Pool.ReserveMemory(g_cMaxDirBuffers, sizeof(CBuffer)))
  681. {
  682. err = GetLastError();
  683. ErrorTrace(0, "ReserveMemory failed for CBuffer. err: %u", err);
  684. _ASSERT(err != NO_ERROR);
  685. if(err == NO_ERROR)
  686. SetLastError (ERROR_NOT_ENOUGH_MEMORY);
  687. goto error_exit;
  688. }
  689. g_SmtpInitializeStatus |= INITIALIZE_CBUFFERPOOL;
  690. if (!CIoBuffer::Pool.ReserveMemory(g_cMaxDirBuffers, g_cMaxDirChangeIoSize))
  691. {
  692. err = GetLastError();
  693. ErrorTrace(0, "ReserveMemory failed for CIOBuffer. err: %u", err);
  694. _ASSERT(err != NO_ERROR);
  695. if(err == NO_ERROR)
  696. SetLastError (ERROR_NOT_ENOUGH_MEMORY);
  697. goto error_exit;
  698. }
  699. g_SmtpInitializeStatus |= INITIALIZE_CIOBUFFPOOL;
  700. if (!CBlockMemoryAccess::m_Pool.ReserveMemory(2000, sizeof(BLOCK_HEAP_NODE)))
  701. {
  702. err = GetLastError();
  703. ErrorTrace(0, "ReserveMemory failed for CBlockMemoryAccess. err: %u", err);
  704. _ASSERT(err != NO_ERROR);
  705. if(err == NO_ERROR)
  706. SetLastError (ERROR_NOT_ENOUGH_MEMORY);
  707. goto error_exit;
  708. }
  709. g_SmtpInitializeStatus |= INITIALIZE_CBLOCKMGR;
  710. if (!CDropDir::m_Pool.ReserveMemory(1000, sizeof(CDropDir)))
  711. {
  712. err = GetLastError();
  713. ErrorTrace(0, "ReserveMemory failed for CDropDir. err: %u", err);
  714. _ASSERT(err != NO_ERROR);
  715. if(err == NO_ERROR)
  716. SetLastError (ERROR_NOT_ENOUGH_MEMORY);
  717. goto error_exit;
  718. }
  719. g_SmtpInitializeStatus |= INITIALIZE_CDROPDIR;
  720. //
  721. // Create the CAPI store notification object
  722. //
  723. g_pCAPIStoreChangeNotifier = new STORE_CHANGE_NOTIFIER();
  724. if ( g_pCAPIStoreChangeNotifier == NULL )
  725. {
  726. err = GetLastError();
  727. ErrorTrace(0, "Failed to create CAPIStoreChange notifier err: %u", err);
  728. _ASSERT(err != NO_ERROR);
  729. if(err == NO_ERROR)
  730. SetLastError (ERROR_NOT_ENOUGH_MEMORY);
  731. goto error_exit;
  732. }
  733. if (!CEncryptCtx::Initialize(
  734. "SmtpSvc",
  735. (struct IMDCOM*) g_pInetSvc->QueryMDObject(),
  736. (PVOID) (&g_SmtpSMC)))
  737. {
  738. err = GetLastError();
  739. ErrorTrace(0, "Initializing SSL Context failed. err: %u", err);
  740. _ASSERT(err != NO_ERROR);
  741. if(err == NO_ERROR)
  742. SetLastError (ERROR_NOT_ENOUGH_MEMORY);
  743. goto error_exit;
  744. }
  745. g_SmtpInitializeStatus |= INITIALIZE_SSLCONTEXT;
  746. if (!CSecurityCtx::Initialize(FALSE, FALSE))
  747. {
  748. err = GetLastError();
  749. ErrorTrace(NULL, "CSecurityCtx::Initialize failed, %u", err);
  750. if(err == NO_ERROR)
  751. SetLastError (ERROR_NOT_ENOUGH_MEMORY);
  752. goto error_exit;
  753. }
  754. g_SmtpInitializeStatus |= INITIALIZE_CSECURITY;
  755. GetTimeZoneInformation(&tzInfo);
  756. TraceFunctLeave();
  757. return NO_ERROR;
  758. error_exit:
  759. err = GetLastError();
  760. if(err == NO_ERROR)
  761. {
  762. SetLastError(ERROR_PATH_NOT_FOUND);
  763. err = ERROR_PATH_NOT_FOUND;
  764. }
  765. TraceFunctLeave();
  766. return err;
  767. } // InitializeGlobals
  768. VOID
  769. TerminateGlobals(
  770. VOID
  771. )
  772. /*++
  773. Routine Description:
  774. Terminates global shared variables.
  775. Arguments:
  776. None.
  777. Return Value:
  778. None.
  779. --*/
  780. {
  781. if(g_ShutdownHandle)
  782. {
  783. SetEvent(g_ShutdownHandle);
  784. }
  785. if(g_SmtpInitializeStatus & INITIALIZE_INBOUNDPOOL)
  786. {
  787. //finally, release all our memory
  788. SMTP_CONNECTION::Pool.ReleaseMemory();
  789. }
  790. if(g_SmtpInitializeStatus & INITIALIZE_OUTBOUNDPOOL)
  791. {
  792. SMTP_CONNOUT::Pool.ReleaseMemory();
  793. }
  794. if(g_SmtpInitializeStatus & INITIALIZE_ADDRESSPOOL)
  795. {
  796. CAddr::Pool.ReleaseMemory();
  797. }
  798. if(g_SmtpInitializeStatus & INITIALIZE_CBUFFERPOOL)
  799. {
  800. //finally, release all our memory
  801. CBuffer::Pool.ReleaseMemory();
  802. }
  803. if(g_SmtpInitializeStatus & INITIALIZE_CIOBUFFPOOL)
  804. {
  805. //finally, release all our memory
  806. CIoBuffer::Pool.ReleaseMemory();
  807. }
  808. if (g_SmtpInitializeStatus & INITIALIZE_CDROPDIR)
  809. {
  810. CDropDir::m_Pool.ReleaseMemory();
  811. }
  812. if ( g_pCAPIStoreChangeNotifier )
  813. {
  814. delete g_pCAPIStoreChangeNotifier;
  815. g_pCAPIStoreChangeNotifier = NULL;
  816. }
  817. if (g_SmtpInitializeStatus & INITIALIZE_SSLCONTEXT) {
  818. CEncryptCtx::Terminate();
  819. }
  820. if (g_SmtpInitializeStatus & INITIALIZE_CSECURITY)
  821. CSecurityCtx::Terminate();
  822. if(g_SmtpInitializeStatus & INITIALIZE_CASYNCMX)
  823. {
  824. //finally, release all our memory
  825. CAsyncMx::Pool.ReleaseMemory();
  826. }
  827. if(g_SmtpInitializeStatus & INITIALIZE_CASYNCDNS)
  828. {
  829. //finally, release all our memory
  830. CAsyncSmtpDns::Pool.ReleaseMemory();
  831. }
  832. if (g_SmtpInitializeStatus & INITIALIZE_FILEHC) {
  833. TerminateCache();
  834. }
  835. if(g_SmtpInitializeStatus & INITIALIZE_CBLOCKMGR)
  836. {
  837. //finally, release all our memory
  838. CBlockMemoryAccess::m_Pool.ReleaseMemory();
  839. }
  840. if( g_pSmtpStats != NULL )
  841. {
  842. delete g_pSmtpStats;
  843. g_pSmtpStats = NULL;
  844. }
  845. if(g_IpListSocket != INVALID_SOCKET)
  846. {
  847. closesocket (g_IpListSocket);
  848. g_IpListSocket = INVALID_SOCKET;
  849. }
  850. UnLoadQueueDriver();
  851. //
  852. // do global SEO cleanup
  853. //
  854. if (g_punkSEOHandle != NULL)
  855. {
  856. g_punkSEOHandle->Release();
  857. g_punkSEOHandle = NULL;
  858. }
  859. if(g_TcpNotifyHandle != NULL)
  860. {
  861. WaitForSingleObject(g_TcpNotifyHandle, INFINITE);
  862. CloseHandle(g_TcpNotifyHandle);
  863. g_TcpNotifyHandle = NULL;
  864. }
  865. if(g_FreeLibThreadHandle != NULL)
  866. {
  867. WaitForSingleObject(g_FreeLibThreadHandle, INFINITE);
  868. CloseHandle(g_FreeLibThreadHandle);
  869. g_FreeLibThreadHandle = NULL;
  870. }
  871. if(g_ShutdownHandle != NULL)
  872. {
  873. CloseHandle(g_ShutdownHandle);
  874. g_ShutdownHandle = NULL;
  875. }
  876. } // TerminateGlobals
  877. //
  878. // Given a directory path, this subroutine will create the direct layer by layer
  879. //
  880. BOOL CreateLayerDirectory( char * str )
  881. {
  882. BOOL fReturn = TRUE;
  883. char Tmp [MAX_PATH + 1];
  884. do
  885. {
  886. INT index=0;
  887. INT iLength = lstrlen(str) + 1;
  888. // first find the index for the first directory
  889. if ( iLength > 2 )
  890. {
  891. if ( str[1] == _T(':'))
  892. {
  893. // assume the first character is driver letter
  894. if ( str[2] == _T('\\'))
  895. {
  896. index = 2;
  897. } else
  898. {
  899. index = 1;
  900. }
  901. } else if ( str[0] == _T('\\'))
  902. {
  903. if ( str[1] == _T('\\'))
  904. {
  905. BOOL fFound = FALSE;
  906. INT i;
  907. INT nNum = 0;
  908. // unc name
  909. for (i = 2; i < iLength; i++ )
  910. {
  911. if ( str[i]==_T('\\'))
  912. {
  913. // find it
  914. nNum ++;
  915. if ( nNum == 2 )
  916. {
  917. fFound = TRUE;
  918. break;
  919. }
  920. }
  921. }
  922. if ( fFound )
  923. {
  924. index = i;
  925. } else
  926. {
  927. // bad name
  928. break;
  929. }
  930. } else
  931. {
  932. index = 1;
  933. }
  934. }
  935. } else if ( str[0] == _T('\\'))
  936. {
  937. index = 0;
  938. }
  939. // okay ... build directory
  940. do
  941. {
  942. // find next one
  943. do
  944. {
  945. if ( index < ( iLength - 1))
  946. {
  947. index ++;
  948. } else
  949. {
  950. break;
  951. }
  952. } while ( str[index] != _T('\\'));
  953. TCHAR szCurrentDir[MAX_PATH+1];
  954. GetCurrentDirectory( MAX_PATH+1, szCurrentDir );
  955. lstrcpyn(Tmp, str, ( index + 1 ));
  956. if ( !SetCurrentDirectory( Tmp))
  957. {
  958. if (( fReturn = CreateDirectory( Tmp, NULL )) != TRUE )
  959. {
  960. break;
  961. }
  962. }
  963. SetCurrentDirectory( szCurrentDir );
  964. if ( index >= ( iLength - 1 ))
  965. {
  966. fReturn = TRUE;
  967. break;
  968. }
  969. } while ( TRUE );
  970. } while (FALSE);
  971. return(fReturn);
  972. }
  973. void GenerateMessageId (char * Buffer, DWORD BuffLen)
  974. {
  975. //Temporary stuff
  976. DWORD MsgIdLen = 20;
  977. if(BuffLen < MsgIdLen)
  978. MsgIdLen = BuffLen;
  979. if( !g_CalledSrand )
  980. {
  981. srand( GetTickCount() );
  982. g_CalledSrand = TRUE;
  983. }
  984. lstrcpyn (Buffer, g_ComputerName, (MsgIdLen - 1));
  985. DWORD Loop = lstrlen(Buffer);
  986. while (Loop < (MsgIdLen - 1) )
  987. {
  988. Buffer[Loop] = g_BoundaryChars[rand() % (sizeof(g_BoundaryChars) - 1)];
  989. Loop++;
  990. }
  991. Buffer [Loop] = '\0';
  992. }
  993. DWORD GetIncreasingMsgId()
  994. {
  995. return( InterlockedIncrement( (LONG*)&g_dwIncMsgId ) );
  996. }
  997. void DeleteIpListFunction(PVOID IpList)
  998. {
  999. PIP_ARRAY aipServers = (PIP_ARRAY) IpList;
  1000. if(aipServers != NULL)
  1001. {
  1002. DnsApiFree(aipServers);
  1003. }
  1004. }
  1005. DWORD FreeLibThread( LPDWORD lpdw )
  1006. {
  1007. DWORD dw = 0;
  1008. DWORD dwWaitMillisec = g_FreeLibInterval * 1000 * 60;
  1009. TraceFunctEnterEx((LPARAM) NULL, "FreeLibThread");
  1010. for ( ;; )
  1011. {
  1012. dw = WaitForSingleObject(g_ShutdownHandle,
  1013. dwWaitMillisec );
  1014. switch( dw )
  1015. {
  1016. //
  1017. // normal shutdown signalled
  1018. //
  1019. case WAIT_OBJECT_0:
  1020. ErrorTrace((LPARAM) NULL, "Exiting FreeLibThread for hShutdownEvent");
  1021. return 0;
  1022. //
  1023. // Timeout occured
  1024. //
  1025. case WAIT_TIMEOUT:
  1026. CoFreeUnusedLibraries();
  1027. break;
  1028. default:
  1029. ErrorTrace((LPARAM) NULL, "Exiting FreeLibThread for default reasons");
  1030. return 1;
  1031. }
  1032. }
  1033. return 2;
  1034. }
  1035. #define NUM_REG_THREAD_OBJECTS 2
  1036. DWORD TcpRegNotifyThread( LPDWORD lpdw )
  1037. {
  1038. HANDLE Handles[NUM_REG_THREAD_OBJECTS];
  1039. PIP_ARRAY aipServers =NULL;
  1040. PLIST_ENTRY pEntry = NULL;
  1041. CTcpRegIpList * pIpEntry = NULL;
  1042. CTcpRegIpList *IpList = NULL;
  1043. HKEY hKey = NULL;
  1044. DWORD dw = 0;
  1045. TraceFunctEnterEx((LPARAM) NULL, "TcpRegNotifyThread");
  1046. Handles[0] = g_ShutdownHandle;
  1047. Handles[1] = CreateEvent( NULL, FALSE, FALSE, NULL );
  1048. if ( Handles[1] == NULL )
  1049. {
  1050. return 1;
  1051. }
  1052. DnsGetDnsServerList( (PIP_ARRAY *) &aipServers );
  1053. if (aipServers != NULL)
  1054. g_TcpRegIpList.Update(aipServers);
  1055. if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  1056. "System\\CurrentControlSet\\Services\\Tcpip",
  1057. 0,
  1058. KEY_READ,
  1059. &hKey ) != ERROR_SUCCESS )
  1060. {
  1061. ErrorTrace((LPARAM) NULL, "RegNotifyThread RegOpenKeyEx failed %d", GetLastError());
  1062. CloseHandle( Handles[1] );
  1063. return 1;
  1064. }
  1065. for ( ;; )
  1066. {
  1067. if ( RegNotifyChangeKeyValue(hKey,
  1068. TRUE,
  1069. REG_NOTIFY_CHANGE_ATTRIBUTES |
  1070. REG_NOTIFY_CHANGE_LAST_SET,
  1071. Handles[1],
  1072. TRUE ) != ERROR_SUCCESS )
  1073. {
  1074. ErrorTrace((LPARAM) NULL, "RegNotifyThread RegNotifyChangeKeyValue failed %d", GetLastError());
  1075. RegCloseKey( hKey );
  1076. CloseHandle( Handles[1] );
  1077. return 1;
  1078. }
  1079. dw = WaitForMultipleObjects(NUM_REG_THREAD_OBJECTS,
  1080. Handles,
  1081. FALSE,
  1082. INFINITE );
  1083. switch( dw )
  1084. {
  1085. //
  1086. // normal signalled event
  1087. //
  1088. case WAIT_OBJECT_0:
  1089. //close all the handles
  1090. RegCloseKey( hKey );
  1091. CloseHandle( Handles[1] );
  1092. Handles[1] = NULL;
  1093. hKey = NULL;
  1094. g_TcpRegIpList.Update(NULL);
  1095. ErrorTrace((LPARAM) NULL, "Exiting TcpRegNotifyThread for hShutdownEvent");
  1096. return 0;
  1097. //
  1098. // signalled that our registry keys have changed
  1099. //
  1100. case WAIT_OBJECT_0+1:
  1101. DnsGetDnsServerList( &aipServers );
  1102. g_TcpRegIpList.Update(aipServers);
  1103. break;
  1104. default:
  1105. RegCloseKey( hKey );
  1106. CloseHandle( Handles[1] );
  1107. return 1;
  1108. }
  1109. }
  1110. RegCloseKey( hKey );
  1111. CloseHandle( Handles[1] );
  1112. return 2;
  1113. }