Leaked source code of windows server 2003
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.

2107 lines
69 KiB

  1. //-----------------------------------------------------------------------------
  2. //
  3. //
  4. // File: ConnMgr.cpp
  5. //
  6. // Description: Implementation of CConnMgr which provides the
  7. // IConnectionManager interface.
  8. //
  9. // Author: mikeswa
  10. //
  11. // Copyright (C) 1997 Microsoft Corporation
  12. //
  13. //-----------------------------------------------------------------------------
  14. #include "aqprecmp.h"
  15. #include "retrsink.h"
  16. #include "ConnMgr.h"
  17. #include "fifoqimp.h"
  18. #include "smtpconn.h"
  19. #include "tran_evntlog.h"
  20. VOID LookupQueueforETRN(PVOID pvContext, PVOID pvData, BOOL fWildcard,
  21. BOOL *pfContinue, BOOL *pfDelete);
  22. VOID CreateETRNDomainList(PVOID pvContext, PVOID pvData, BOOL fWildcard,
  23. BOOL *pfContinue, BOOL *pfDelete);
  24. //If we are not limiting the number of messages that each connection can handle,
  25. //then lets use this as a guide to determine how many connections to create.
  26. #define UNLIMITED_MSGS_PER_CONNECTION 20
  27. //---[ CConnMgr::CConnMgr ]----------------------------------------------------
  28. //
  29. //
  30. // Description:
  31. // Default constructor for CConnMgr class.
  32. // Parameters:
  33. // -
  34. // Returns:
  35. // -
  36. //
  37. //-----------------------------------------------------------------------------
  38. CConnMgr::CConnMgr() : CSyncShutdown()
  39. {
  40. HRESULT hr = S_OK;
  41. m_paqinst = NULL;
  42. m_pqol = NULL;
  43. m_hNextConnectionEvent = NULL;
  44. m_hShutdownEvent = NULL;
  45. m_hReleaseAllEvent = NULL;
  46. m_cConnections = 0;
  47. m_cMaxLinkConnections = g_cMaxLinkConnections;
  48. m_cMinMessagesPerConnection = g_cMinMessagesPerConnection;
  49. m_cMaxMessagesPerConnection = g_cMaxMessagesPerConnection;
  50. m_cMaxConnections = g_cMaxConnections;
  51. m_cGetNextConnectionWaitTime = g_dwConnectionWaitMilliseconds;
  52. m_dwConfigVersion = 0;
  53. m_fStoppedByAdmin = FALSE;
  54. }
  55. //---[ CConnMgr::~CConnMgr ]-----------------------------------------------------
  56. //
  57. //
  58. // Description:
  59. // Default destructor for CConnMgr
  60. // Parameters:
  61. // -
  62. // Returns:
  63. // -
  64. //
  65. //-----------------------------------------------------------------------------
  66. CConnMgr::~CConnMgr()
  67. {
  68. TraceFunctEnterEx((LPARAM) this, "CConnMgr::~CConnMgr");
  69. if (NULL != m_hNextConnectionEvent)
  70. {
  71. if (!CloseHandle(m_hNextConnectionEvent))
  72. {
  73. DebugTrace((LPARAM) HRESULT_FROM_WIN32(GetLastError()),
  74. "Unable to close handle for Get Next Connection Event");
  75. }
  76. }
  77. if (NULL != m_hShutdownEvent)
  78. {
  79. if (!CloseHandle(m_hShutdownEvent))
  80. {
  81. DebugTrace((LPARAM) HRESULT_FROM_WIN32(GetLastError()),
  82. "Unable to close handle for Connection Manger Shutdown Event");
  83. }
  84. }
  85. if (NULL != m_hReleaseAllEvent)
  86. {
  87. if (!CloseHandle(m_hReleaseAllEvent))
  88. {
  89. DebugTrace((LPARAM) HRESULT_FROM_WIN32(GetLastError()),
  90. "Unable to close handle for Connection Manger Release All Event");
  91. }
  92. }
  93. TraceFunctLeave();
  94. }
  95. //---[ CConnMgr::HrInitialize ]------------------------------------------------
  96. //
  97. //
  98. // Description:
  99. // CConnMgr Initialization function.
  100. // Parameters:
  101. // paqinst ptr fo CAQSvrInst virtual instance object
  102. // Returns:
  103. // S_OK on success
  104. //
  105. //-----------------------------------------------------------------------------
  106. HRESULT CConnMgr::HrInitialize(CAQSvrInst *paqinst)
  107. {
  108. TraceFunctEnterEx((LPARAM) this, "CConnMgr::HrInitialize");
  109. HRESULT hr = S_OK;
  110. IConnectionRetryManager *pIRetryMgr = NULL;
  111. _ASSERT(paqinst);
  112. paqinst->AddRef();
  113. m_paqinst = paqinst;
  114. //Create Manual reset event to release all waiting threads on shutdown
  115. m_hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  116. if (NULL == m_hShutdownEvent)
  117. {
  118. hr = HRESULT_FROM_WIN32(GetLastError());
  119. goto Exit;
  120. }
  121. //Create Queue of Links
  122. m_pqol = new QueueOfLinks;
  123. if (NULL == m_pqol)
  124. {
  125. hr = E_OUTOFMEMORY;
  126. goto Exit;
  127. }
  128. m_hNextConnectionEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  129. if (NULL == m_hNextConnectionEvent)
  130. {
  131. hr = HRESULT_FROM_WIN32(GetLastError());
  132. goto Exit;
  133. }
  134. //Create Manual reset event to release all waiting threads on caller's request
  135. m_hReleaseAllEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  136. if (NULL == m_hReleaseAllEvent)
  137. {
  138. hr = HRESULT_FROM_WIN32(GetLastError());
  139. goto Exit;
  140. }
  141. hr = QueryInterface(IID_IConnectionRetryManager, (PVOID *) &pIRetryMgr);
  142. if (FAILED(hr))
  143. goto Exit;
  144. //Create the default retry handler object and initialize it
  145. m_pDefaultRetryHandler = new CSMTP_RETRY_HANDLER();
  146. if (!m_pDefaultRetryHandler)
  147. {
  148. hr = E_OUTOFMEMORY;
  149. goto Exit;
  150. }
  151. //Addref IConnectionRetryManager here
  152. //and release it during deinit
  153. pIRetryMgr->AddRef();
  154. hr = m_pDefaultRetryHandler->HrInitialize(pIRetryMgr);
  155. if (FAILED(hr))
  156. {
  157. ErrorTrace((LPARAM) hr, "ERROR: Unable to initialize the retry handler!");
  158. goto Exit;
  159. }
  160. Exit:
  161. if (pIRetryMgr)
  162. pIRetryMgr->Release();
  163. TraceFunctLeave();
  164. return hr;
  165. }
  166. //---[ CConnMgr::HrDeinitialize ]----------------------------------------------
  167. //
  168. //
  169. // Description:
  170. // CConnMgr Deinitialization function.
  171. // Parameters:
  172. // -
  173. // Returns:
  174. // S_OK on success
  175. //
  176. //-----------------------------------------------------------------------------
  177. HRESULT CConnMgr::HrDeinitialize()
  178. {
  179. TraceFunctEnterEx((LPARAM) this, "CConnMgr::HrDeinitialize");
  180. //Wait max of 3 minutes no-progress.
  181. const DWORD CONNMGR_WAIT_SECONDS = 5;
  182. const DWORD MAX_CONNMGR_SHUTDOWN_WAITS = 1200/CONNMGR_WAIT_SECONDS;
  183. const DWORD MAX_CONNMGR_SHUTDOWN_WAITS_WITHOUT_PROGRESS = 180/CONNMGR_WAIT_SECONDS;
  184. HRESULT hr = S_OK;
  185. HRESULT hrQueue = S_OK;
  186. CLinkMsgQueue *plmq = NULL;
  187. DWORD cWaits = 0;
  188. DWORD cWaitsSinceLastProgress = 0;
  189. DWORD cConnectionsPrevious = 0;
  190. if (m_paqinst)
  191. m_paqinst->ServerStopHintFunction();
  192. SignalShutdown();
  193. if (NULL != m_hShutdownEvent)
  194. {
  195. if (!SetEvent(m_hShutdownEvent))
  196. {
  197. if (SUCCEEDED(hr))
  198. hr = HRESULT_FROM_WIN32(GetLastError());
  199. }
  200. }
  201. if (NULL != m_pqol)
  202. {
  203. //Dequeue Links until empty
  204. hrQueue = m_pqol->HrDequeue(&plmq);
  205. while (SUCCEEDED(hrQueue))
  206. {
  207. _ASSERT(plmq);
  208. plmq->Release();
  209. hrQueue = m_pqol->HrDequeue(&plmq);
  210. }
  211. delete m_pqol;
  212. m_pqol = NULL;
  213. }
  214. cConnectionsPrevious = m_cConnections;
  215. while (m_cConnections)
  216. {
  217. cWaits++;
  218. cWaitsSinceLastProgress++;
  219. if (m_paqinst)
  220. m_paqinst->ServerStopHintFunction();
  221. Sleep(CONNMGR_WAIT_SECONDS * 1000);
  222. if (m_cConnections != cConnectionsPrevious)
  223. {
  224. cConnectionsPrevious = m_cConnections;
  225. cWaitsSinceLastProgress = 0;
  226. }
  227. if ((cWaits > MAX_CONNMGR_SHUTDOWN_WAITS) ||
  228. (cWaitsSinceLastProgress > MAX_CONNMGR_SHUTDOWN_WAITS_WITHOUT_PROGRESS))
  229. {
  230. _ASSERT(0 && "SMTP not returning all connections");
  231. ErrorTrace((LPARAM) this, "ERROR: %d Connections outstanding on shutdown", m_cConnections);
  232. break;
  233. }
  234. }
  235. //Must happen after we are done caller server stop hint functions
  236. if (NULL != m_paqinst)
  237. {
  238. m_paqinst->Release();
  239. m_paqinst = NULL;
  240. }
  241. //NK** To be safe do it as interlocked exchange
  242. if (m_pDefaultRetryHandler)
  243. {
  244. m_pDefaultRetryHandler->HrDeInitialize();
  245. m_pDefaultRetryHandler = NULL;
  246. }
  247. TraceFunctLeave();
  248. return hr;
  249. }
  250. //---[ CConnMgr::HrNotify ]----------------------------------------------------
  251. //
  252. //
  253. // Description:
  254. // Method exposed to recieve a notification about a change in queue status
  255. // Parameters:
  256. // IN paqstats Notification object
  257. // Returns:
  258. // S_OK on success
  259. //
  260. //-----------------------------------------------------------------------------
  261. HRESULT CConnMgr::HrNotify(IN CAQStats *paqstats, BOOL fAdd)
  262. {
  263. TraceFunctEnterEx((LPARAM) this, "CConnMgr::HrNotify");
  264. HRESULT hr = S_OK;
  265. CLinkMsgQueue *plmq = NULL;
  266. DWORD cbDomainName = 0;
  267. LPSTR szDomainName = NULL;
  268. _ASSERT(paqstats);
  269. plmq = paqstats->m_plmq;
  270. _ASSERT(plmq); //ConnMgr notifications must have a link associated with then
  271. if (paqstats->m_dwNotifyType & NotifyTypeNewLink)
  272. {
  273. hr = plmq->HrGetSMTPDomain(&cbDomainName, &szDomainName);
  274. if (FAILED(hr))
  275. goto Exit;
  276. //must add new link to QueueOfLinks
  277. plmq->IncrementConnMgrCount();
  278. hr = m_pqol->HrEnqueue(plmq);
  279. if (FAILED(hr))
  280. {
  281. plmq->DecrementConnMgrCount();
  282. DebugTrace((LPARAM) hr, "ERROR: Unable to add new link to connection manager!");
  283. goto Exit;
  284. }
  285. }
  286. //See if we can (and *should*) create a connection
  287. if ((m_cConnections < m_cMaxConnections) &&
  288. plmq->fShouldConnect(m_cMaxLinkConnections, m_cMinMessagesPerConnection))
  289. {
  290. DebugTrace((LPARAM) m_hNextConnectionEvent, "INFO: Setting Next Connection Event");
  291. if (!SetEvent(m_hNextConnectionEvent))
  292. {
  293. hr = HRESULT_FROM_WIN32(GetLastError());
  294. goto Exit;
  295. }
  296. }
  297. Exit:
  298. TraceFunctLeave();
  299. return hr;
  300. }
  301. //---[ CConnMgr::ReleaseConnection ]-------------------------------------------
  302. //
  303. //
  304. // Description:
  305. // Releases the connection count when a connection is being destroyed
  306. // Parameters:
  307. // IN pSMTPConn SMTP Connection Object to release
  308. // OUT pfForceCheckForDSNGeneration
  309. // TRUE if there was a hard error and we must
  310. // pass this link through DSN generation.
  311. //
  312. // This 2nd parameter does not mean that will will or will not NDR the
  313. // messages... just that we will iterate over all of the messages in the
  314. // link. If CMsgRef::fIsFatalError() returns TRUE for the current
  315. // connection status (as passed into the DSN generation code, then messages
  316. // will be NDR'd. One way to control this is by setting the
  317. //
  318. // Returns:
  319. // -
  320. //
  321. //-----------------------------------------------------------------------------
  322. void CConnMgr::ReleaseConnection(CSMTPConn *pSMTPConn,
  323. BOOL *pfForceCheckForDSNGeneration)
  324. {
  325. TraceFunctEnterEx((LPARAM) this, "CConnMgr::ReleaseConnection");
  326. DWORD dwDomainInfoFlags = 0;
  327. DWORD dwLinkStateFlags = 0;
  328. DWORD dwConnectionStatus = pSMTPConn->dwGetConnectionStatus();
  329. DWORD dwConnectionFailureCount = 0;
  330. HRESULT hr = S_OK;
  331. CLinkMsgQueue *plmq = NULL;
  332. DWORD cbDomain = 0;
  333. LPSTR szDomain = NULL;
  334. BOOL fCanRetry = FALSE;
  335. BOOL fLocked = FALSE;
  336. DWORD cConnections = 0;
  337. CInternalDomainInfo *pIntDomainInfo= NULL;
  338. CAQScheduleID *paqsched = NULL;
  339. FILETIME ftNextRetry;
  340. BOOL fShouldNotify = FALSE;
  341. BOOL fMayNDRAllMessages = FALSE;
  342. GUID guidRouting = GUID_NULL;
  343. DWORD cMessages = 0;
  344. ZeroMemory(&ftNextRetry, sizeof(FILETIME));
  345. _ASSERT(pfForceCheckForDSNGeneration);
  346. if (pfForceCheckForDSNGeneration)
  347. *pfForceCheckForDSNGeneration = FALSE;
  348. plmq = pSMTPConn->plmqGetLink();
  349. _ASSERT(plmq); //connection must be associated with a link
  350. paqsched = plmq->paqschedGetScheduleID();
  351. _ASSERT(paqsched);
  352. //Get the routing GUID
  353. paqsched->GetGUID(&guidRouting);
  354. hr = plmq->HrGetSMTPDomain(&cbDomain, &szDomain);
  355. if (FAILED(hr))
  356. {
  357. _ASSERT(0); //I need to unstand when this can happen
  358. DebugTrace((LPARAM) hr, "ERROR: HrGetSMTPDomain failed");
  359. goto Exit;
  360. }
  361. if (!fTryShutdownLock())
  362. {
  363. hr = AQUEUE_E_SHUTDOWN;
  364. goto Exit;
  365. }
  366. else
  367. {
  368. fLocked = TRUE;
  369. _ASSERT(m_paqinst);
  370. hr = plmq->HrGetDomainInfo(&cbDomain, &szDomain, &pIntDomainInfo);
  371. if (FAILED(hr))
  372. {
  373. //It must match the "*" domain at least
  374. _ASSERT(AQUEUE_E_INVALID_DOMAIN != hr);
  375. DebugTrace((LPARAM) hr, "ERROR: HrGetInternalDomainInfo");
  376. goto Exit;
  377. }
  378. _ASSERT(pIntDomainInfo);
  379. dwDomainInfoFlags = pIntDomainInfo->m_DomainInfo.dwDomainInfoFlags ;
  380. cConnections = plmq->cGetConnections();
  381. //NK** Update the link with the number of messages tried, failed, sent etc
  382. //If the remaining count goes to 0 and trigger is set, we will disable the trigger
  383. cMessages = plmq->cGetTotalMsgCount();
  384. //If we no more messages on the link, we need to disable
  385. //flags that caused one time triggering
  386. if(!cMessages)
  387. {
  388. //No more messages on the link - we may need to unset some flags on the link
  389. //If someone set this bit... then we should continue to notify them
  390. if (plmq->dwGetLinkState() & LINK_STATE_CONNECT_IF_NO_MSGS)
  391. fShouldNotify = TRUE;
  392. if(dwDomainInfoFlags & DOMAIN_INFO_TURN_ONLY || dwDomainInfoFlags & DOMAIN_INFO_ETRN_ONLY )
  393. dwLinkStateFlags |= LINK_STATE_PRIV_ETRN_ENABLED | LINK_STATE_PRIV_TURN_ENABLED;
  394. }
  395. //Disable the admin forced connection
  396. // 2/1/99 - MikeSwa - We need to do this check every time, or we will
  397. // continue to create connections for this link
  398. if (plmq->dwGetLinkState() & LINK_STATE_ADMIN_FORCE_CONN)
  399. dwLinkStateFlags |= LINK_STATE_ADMIN_FORCE_CONN;
  400. //Call link function to *unset* flags
  401. if (dwLinkStateFlags)
  402. plmq->dwModifyLinkState(LINK_STATE_NO_ACTION, dwLinkStateFlags);
  403. //The connection failed and this is the last outstanding connection to this domain
  404. //Increment the failure count
  405. dwConnectionFailureCount = plmq->cGetMessageFailureCount();
  406. if(cConnections == 1 && (CONNECTION_STATUS_OK != dwConnectionStatus))
  407. {
  408. dwConnectionFailureCount = plmq->IncrementFailureCounts();
  409. }
  410. if (CONNECTION_STATUS_FAILED_LOOPBACK & dwConnectionStatus)
  411. {
  412. ErrorTrace((LPARAM) this,
  413. "Loopback detected for domain %s (smarthost %s)",
  414. pIntDomainInfo->m_DomainInfo.szDomainName,
  415. pIntDomainInfo->m_DomainInfo.szSmartHostDomainName);
  416. fMayNDRAllMessages = TRUE;
  417. }
  418. if (CONNECTION_STATUS_FAILED_NDR_UNDELIVERED & dwConnectionStatus)
  419. {
  420. //
  421. // If not set treat the failure as retryable - see detailed
  422. // comments in GenerateDSNsIfNecessary() in linkmsgq.cpp for
  423. // details comments
  424. //
  425. dwLinkStateFlags = plmq->dwGetLinkState();
  426. if (!(LINK_STATE_RETRY_ALL_DNS_FAILURES & dwLinkStateFlags))
  427. {
  428. ErrorTrace((LPARAM) plmq,
  429. "hard failure for %s (smarthost %s) - flags 0x%08X",
  430. pIntDomainInfo->m_DomainInfo.szDomainName,
  431. pIntDomainInfo->m_DomainInfo.szSmartHostDomainName,
  432. dwLinkStateFlags);
  433. fMayNDRAllMessages = TRUE;
  434. }
  435. }
  436. //
  437. // Flag the link so that we generate DSNs on it and fall down to
  438. // the retry handler sink. This link will be marked retry so that
  439. // no new connections are created. Ultimately, the DSN generation
  440. // code/MsgRef will make the final determination if we need to NDR,
  441. // but this being set means that:
  442. // - We think we have hit an NDR-able error
  443. // - We will use the glitch retry (so new messages
  444. // can be retried as well).
  445. //
  446. if (fMayNDRAllMessages)
  447. {
  448. if(pfForceCheckForDSNGeneration)
  449. *pfForceCheckForDSNGeneration = TRUE;
  450. //
  451. // Trick the retry sink so it always uses the glitch retry
  452. //
  453. dwConnectionFailureCount = 1;
  454. }
  455. _ASSERT(m_pDefaultRetryHandler);
  456. DebugTrace((LPARAM) this,
  457. "INFO: ConnectionRelease for domain %s: %d failed, %d tried, status 0x%08X",
  458. szDomain, pSMTPConn->cGetFailedMsgCount(),
  459. pSMTPConn->cGetTriedMsgCount(),
  460. pSMTPConn->dwGetConnectionStatus());
  461. hr = m_pDefaultRetryHandler->ConnectionReleased(cbDomain, szDomain,
  462. dwDomainInfoFlags, paqsched->dwGetScheduleID(),
  463. guidRouting, dwConnectionStatus,
  464. pSMTPConn->cGetFailedMsgCount(),
  465. pSMTPConn->cGetTriedMsgCount(),
  466. dwConnectionFailureCount, &fCanRetry, &ftNextRetry);
  467. if (FAILED(hr))
  468. {
  469. DebugTrace((LPARAM) hr,
  470. "ERROR: Failed to deal with released connection");
  471. }
  472. //Make sure that the proper flags are set WRT retry
  473. if (fCanRetry)
  474. {
  475. if (dwConnectionStatus == CONNECTION_STATUS_OK)
  476. plmq->ResetConnectionFailureCount();
  477. //If this is a TURN/ETRN domain, we do not want to enable it unless
  478. //another TURN/ETRN request comes... or a retry request is scheduled for
  479. //later. The reason for this, is that we don't want to retry TURN/ETRN
  480. //domains in the conventional sense, so the defaul retry sink ignores
  481. //them except for "glitch" retries
  482. if(dwDomainInfoFlags & (DOMAIN_INFO_TURN_ONLY | DOMAIN_INFO_ETRN_ONLY))
  483. dwLinkStateFlags = LINK_STATE_PRIV_ETRN_ENABLED | LINK_STATE_PRIV_TURN_ENABLED;
  484. else
  485. dwLinkStateFlags = LINK_STATE_NO_ACTION;
  486. dwLinkStateFlags = plmq->dwModifyLinkState(LINK_STATE_RETRY_ENABLED,
  487. dwLinkStateFlags);
  488. //Check for state change
  489. if (!(LINK_STATE_RETRY_ENABLED & dwLinkStateFlags))
  490. fShouldNotify = TRUE;
  491. }
  492. else
  493. {
  494. DebugTrace((LPARAM) this,"ASSERT_RETRY : ReleaseConnection about to clear flag for link 0x%08X", plmq);
  495. dwLinkStateFlags = plmq->dwModifyLinkState(LINK_STATE_NO_ACTION,
  496. LINK_STATE_RETRY_ENABLED);
  497. DebugTrace((LPARAM) this,"ASSERT_RETRY : ReleaseConnection has cleared the flag for link 0x%08X", plmq);
  498. //Check for state change
  499. if (LINK_STATE_RETRY_ENABLED & dwLinkStateFlags)
  500. fShouldNotify = TRUE;
  501. if (ftNextRetry.dwHighDateTime || ftNextRetry.dwLowDateTime)
  502. {
  503. //Retry is telling us a retry time... report that.
  504. //Set the next retry time that the retry sink tells us about
  505. plmq->SetNextRetry(&ftNextRetry);
  506. }
  507. //
  508. // Log something useful
  509. //
  510. LogConnectionFailedEvent(pSMTPConn, plmq, szDomain);
  511. if (dwConnectionStatus == CONNECTION_STATUS_OK)
  512. plmq->IncrementFailureCounts(); //we had a false positive
  513. }
  514. //Notify router/scheduler of any changes
  515. if (fShouldNotify)
  516. plmq->SendLinkStateNotification();
  517. if (cConnections < m_cMaxConnections)
  518. {
  519. if (!SetEvent(m_hNextConnectionEvent))
  520. DebugTrace((LPARAM) HRESULT_FROM_WIN32(GetLastError()), "Unable to set GetNextConnection Event");
  521. }
  522. }
  523. Exit:
  524. if (plmq)
  525. plmq->Release();
  526. //Decrement connection count
  527. cConnections = InterlockedDecrement((long *) &m_cConnections);
  528. DebugTrace((LPARAM) this, "INFO: Releasing Connection for link 0x%08X", plmq);
  529. if (fLocked)
  530. ShutdownUnlock();
  531. if (pIntDomainInfo)
  532. pIntDomainInfo->Release();
  533. TraceFunctLeave();
  534. }
  535. //---[ CConnMgr::LogConnectionFailedEvent ]------------------------------------
  536. //
  537. //
  538. // Description:
  539. // Logs the specific connection failure event.
  540. // Parameters:
  541. // IN pSMTPConn SMTP Connection Object to release
  542. // IN plmq ClinkMsgQueue for this link
  543. // IN szDomain The domain name
  544. // Returns:
  545. // -
  546. // History:
  547. // 11/29/2001 - Mikeswa created (moved from ReleaseConnection)
  548. //
  549. //-----------------------------------------------------------------------------
  550. void CConnMgr::LogConnectionFailedEvent(CSMTPConn *pSMTPConn,
  551. CLinkMsgQueue *plmq,
  552. LPSTR szDomain)
  553. {
  554. TraceFunctEnterEx((LPARAM)this, "CConnMgr::LogConnectionFailedEvent");
  555. char szDiagnosticVerb[1024] = "";
  556. char szDiagnosticError[1024] = "";
  557. DWORD iMessage = AQUEUE_REMOTE_DELIVERY_FAILED;
  558. HRESULT hrDiagnostic = PHATQ_E_CONNECTION_FAILED;
  559. LPSTR szConnectedIPAddress = pSMTPConn ? pSMTPConn->szGetConnectedIPAddress() : NULL;
  560. BOOL fLogIPAddress = FALSE;
  561. DWORD iSubstringDiagnosticIndex = 1; //index of dianostic in substring
  562. WORD wSubstringWordCount = 0;
  563. const char *rgszSubstrings[] = {
  564. szDomain,
  565. NULL /* error message */,
  566. szDiagnosticVerb,
  567. szDiagnosticError,
  568. };
  569. const char *rgszSubstringWithIP[] = {
  570. szConnectedIPAddress,
  571. szDomain,
  572. NULL /* error message */,
  573. szDiagnosticVerb,
  574. szDiagnosticError,
  575. };
  576. if (!plmq || !pSMTPConn || !m_paqinst)
  577. {
  578. ErrorTrace((LPARAM)this,
  579. "Bogus pointers plmq %p, pSMTPConn %p, m_paqinst %p",
  580. plmq, pSMTPConn, m_paqinst);
  581. goto Exit;
  582. }
  583. plmq->GetDiagnosticInfo(szDiagnosticVerb,
  584. sizeof(szDiagnosticVerb),
  585. szDiagnosticError,
  586. sizeof(szDiagnosticError),
  587. &hrDiagnostic);
  588. if (SUCCEEDED(hrDiagnostic))
  589. {
  590. //This means that the connection has failed,
  591. //but there is no diagnostic information... this could
  592. //be caused by several things, but we want to avoid
  593. //logging a potentially bogus event.
  594. //Set this error to something that looks useful, but
  595. //is actually the transport equivalent on E_FAIL. We
  596. //can use this to find when this was hit by looking at
  597. //the error logs on retail builds.
  598. hrDiagnostic = PHATQ_E_CONNECTION_FAILED;
  599. ErrorTrace((LPARAM) this,
  600. "Link Diagnostic was not set - defaulting");
  601. }
  602. //
  603. // There are 4 different events we can log at this point. Each as a
  604. // different number of words to substitute & a different place to put the
  605. // diagnostic. Each has a different message ID. The variations are
  606. // with/without SMTP protocol verbs and with/without IP address
  607. //
  608. // Failure fLogIPAddress Words Substring Array Diag Index
  609. // ======================================================================
  610. // no verb FALSE 2 rgszSubstrings 1
  611. // Verb FALSE 4 rgszSubstrings 1
  612. // no verb TRUE 3 rgszSubstringWithIP 2
  613. // Verb TRUE 5 rgszSubstringWithIP 2
  614. //
  615. //
  616. // Check if there is anything in the IP address string.
  617. // If there is, we will use it.
  618. //
  619. if (szConnectedIPAddress && szConnectedIPAddress[0]) {
  620. fLogIPAddress = TRUE;
  621. }
  622. //
  623. // Is there any protocol verb data? If so, use this.
  624. //
  625. if (*szDiagnosticVerb != 0 || *szDiagnosticError != 0)
  626. {
  627. if (fLogIPAddress)
  628. {
  629. wSubstringWordCount = 5;
  630. iSubstringDiagnosticIndex = 2;
  631. iMessage = AQUEUE_REMOTE_DELIVERY_TO_IP_FAILED_DIAGNOSTIC;
  632. }
  633. else
  634. {
  635. wSubstringWordCount = 4;
  636. iSubstringDiagnosticIndex = 1;
  637. iMessage = AQUEUE_REMOTE_DELIVERY_FAILED_DIAGNOSTIC;
  638. }
  639. }
  640. else
  641. {
  642. if (fLogIPAddress)
  643. {
  644. wSubstringWordCount = 3;
  645. iSubstringDiagnosticIndex = 2;
  646. iMessage = AQUEUE_REMOTE_DELIVERY_TO_IP_FAILED;
  647. }
  648. else
  649. {
  650. wSubstringWordCount = 2;
  651. iSubstringDiagnosticIndex = 1;
  652. iMessage = AQUEUE_REMOTE_DELIVERY_FAILED;
  653. }
  654. }
  655. m_paqinst->HrTriggerLogEvent(
  656. iMessage, // Message ID
  657. TRAN_CAT_CONNECTION_MANAGER, // Category
  658. wSubstringWordCount, // Word count of substring
  659. fLogIPAddress ? rgszSubstringWithIP : rgszSubstrings,
  660. EVENTLOG_WARNING_TYPE, // Type of the message
  661. hrDiagnostic, // error code
  662. LOGEVENT_LEVEL_MEDIUM, // Logging level
  663. szDomain, // Key to identify this event
  664. LOGEVENT_FLAG_PERIODIC, // Event logging option
  665. iSubstringDiagnosticIndex, // format string's index in substring
  666. GetModuleHandle(AQ_MODULE_NAME) // module handle to format a message
  667. );
  668. Exit:
  669. TraceFunctLeave();
  670. }
  671. //---[ CConnMgr::QueryInterface ]------------------------------------------
  672. //
  673. //
  674. // Description:
  675. // QueryInterface for IAdvQueue
  676. // Parameters:
  677. //
  678. // Returns:
  679. // S_OK on success
  680. //
  681. // Notes:
  682. // This implementation makes it possible for any server component to get
  683. // the IConnectionManager interface.
  684. //
  685. //-----------------------------------------------------------------------------
  686. STDMETHODIMP CConnMgr::QueryInterface(REFIID riid, LPVOID * ppvObj)
  687. {
  688. HRESULT hr = S_OK;
  689. if (!ppvObj)
  690. {
  691. hr = E_INVALIDARG;
  692. goto Exit;
  693. }
  694. if (IID_IUnknown == riid)
  695. {
  696. *ppvObj = static_cast<IConnectionRetryManager *>(this);
  697. }
  698. else if (IID_IConnectionRetryManager == riid)
  699. {
  700. *ppvObj = static_cast<IConnectionRetryManager *>(this);
  701. }
  702. else if (IID_IConnectionManager == riid)
  703. {
  704. *ppvObj = static_cast<IConnectionManager *>(this);
  705. }
  706. else
  707. {
  708. *ppvObj = NULL;
  709. hr = E_NOINTERFACE;
  710. goto Exit;
  711. }
  712. static_cast<IUnknown *>(*ppvObj)->AddRef();
  713. Exit:
  714. return hr;
  715. }
  716. //---[ CConnMgr::GetNextConnection ]-------------------------------------------
  717. //
  718. //
  719. // Description:
  720. // Implementation of IConnectionManager::GetNextConnection()
  721. //
  722. // Returns the next available connection. Will create a connection object
  723. // and associate it with a link. If we are already at max connections, or
  724. // no link needs a connection, then this call will block until a an
  725. // appropriate connection can be made.
  726. // Parameters:
  727. // OUT pismtpconn SMTP Connection interface
  728. // Returns:
  729. // S_OK on success
  730. //
  731. //-----------------------------------------------------------------------------
  732. STDMETHODIMP CConnMgr::GetNextConnection(ISMTPConnection ** ppISMTPConnection)
  733. {
  734. TraceFunctEnterEx((LPARAM) this, "CConnMgr::GetNextConnection");
  735. const DWORD NUM_CONNECTION_OBJECTS = 3;
  736. //Release event is the last event in array
  737. const DWORD WAIT_OBJECT_RELEASE_EVENT = WAIT_OBJECT_0 + NUM_CONNECTION_OBJECTS -1;
  738. HRESULT hr = S_OK;
  739. DWORD cLinksToTry = 0;
  740. DWORD cConnections = 0;
  741. CLinkMsgQueue *plmq = NULL;
  742. CSMTPConn *pSMTPConn = NULL;
  743. bool fForceWait = false; //temporarily force thread to wait
  744. bool fLocked = false;
  745. DWORD cbDomain = 0;
  746. LPSTR szDomain = NULL;
  747. HANDLE rghWaitEvents[NUM_CONNECTION_OBJECTS] = {m_hShutdownEvent, m_hNextConnectionEvent, m_hReleaseAllEvent};
  748. DWORD dwWaitResult;
  749. DWORD cMaxConnections = 0;
  750. DWORD cGetNextConnectionWaitTime = 30000; //make sure we never start in a busy wait loop
  751. DWORD cMaxLinkConnections = 0;
  752. DWORD cMinMessagesPerConnection = 0;
  753. DWORD cMaxMessagesPerConnection = 0;
  754. DWORD dwConfigVersion;
  755. LONG cTimesQueued = 0; //# of times a link has been queue'd
  756. BOOL fOwnConnectionCount = FALSE;
  757. BOOL fMembersUnsafe = FALSE; //set to TRUE during shutdown situations
  758. if (NULL == ppISMTPConnection)
  759. {
  760. hr = E_INVALIDARG;
  761. goto Exit;
  762. }
  763. //Get config data
  764. m_slPrivateData.ShareLock();
  765. cMaxLinkConnections = m_cMaxLinkConnections;
  766. cMaxMessagesPerConnection = m_cMaxMessagesPerConnection;
  767. //Handle unlimited case
  768. if (m_cMinMessagesPerConnection)
  769. cMinMessagesPerConnection = m_cMinMessagesPerConnection;
  770. else
  771. cMinMessagesPerConnection = UNLIMITED_MSGS_PER_CONNECTION;
  772. cMaxConnections = m_cMaxConnections;
  773. cGetNextConnectionWaitTime = m_cGetNextConnectionWaitTime;
  774. dwConfigVersion = m_dwConfigVersion;
  775. m_slPrivateData.ShareUnlock();
  776. cConnections = InterlockedIncrement((PLONG) &m_cConnections);
  777. fOwnConnectionCount = TRUE;
  778. cLinksToTry = m_pqol->cGetCount();
  779. while (true)
  780. {
  781. //Use CSyncShutdown locking to prevent shutdown from happening under us
  782. if (!fLocked)
  783. {
  784. if (!fTryShutdownLock())
  785. {
  786. hr = AQUEUE_E_SHUTDOWN;
  787. goto Exit;
  788. }
  789. m_paqinst->RoutingShareLock();
  790. fLocked = TRUE;
  791. }
  792. if (m_dwConfigVersion != dwConfigVersion)
  793. {
  794. //Config data has/is being updated aquire lock & get new data
  795. m_slPrivateData.ShareLock();
  796. cMaxLinkConnections = m_cMaxLinkConnections;
  797. cMaxMessagesPerConnection = m_cMaxMessagesPerConnection;
  798. //Handle unlimited case
  799. if (m_cMinMessagesPerConnection)
  800. cMinMessagesPerConnection = m_cMinMessagesPerConnection;
  801. else
  802. cMinMessagesPerConnection = UNLIMITED_MSGS_PER_CONNECTION;
  803. cMaxConnections = m_cMaxConnections;
  804. cGetNextConnectionWaitTime = m_cGetNextConnectionWaitTime;
  805. dwConfigVersion = m_dwConfigVersion;
  806. m_slPrivateData.ShareUnlock();
  807. }
  808. //$$REVIEW: If there is more than 1 thread waiting on GetNextConnection,
  809. //then all threads will cycle through all availalbe links (if none are
  810. //available for connections). However, it is very unlikely that this
  811. //run through the queue will be neccessary after Milestone #1.
  812. while ((0 == cLinksToTry) ||
  813. (cConnections > cMaxConnections) || fForceWait ||
  814. fConnectionsStoppedByAdmin())
  815. {
  816. InterlockedDecrement((PLONG) &m_cConnections);
  817. fOwnConnectionCount = FALSE;
  818. //Release lock for wait function
  819. fLocked = false;
  820. m_paqinst->RoutingShareUnlock();
  821. ShutdownUnlock();
  822. DebugTrace((LPARAM) m_cConnections, "INFO: Waiting in GetNextConnection");
  823. _ASSERT(m_cGetNextConnectionWaitTime && "Configured for busy wait loop");
  824. dwWaitResult = WaitForMultipleObjects(NUM_CONNECTION_OBJECTS,
  825. rghWaitEvents, FALSE, cGetNextConnectionWaitTime);
  826. //NOTE: We *cannot* touch member variables until we determine that
  827. //we are not shutting down, because SMTP may have a thread in here
  828. //after this object is destroyed.
  829. DebugTrace((LPARAM) this, "INFO: Waking up in GetNextConnection");
  830. if (WAIT_FAILED == dwWaitResult)
  831. {
  832. hr = HRESULT_FROM_WIN32(GetLastError());
  833. goto Exit;
  834. }
  835. else if (WAIT_OBJECT_0 == dwWaitResult) //shutdown event fired
  836. {
  837. DebugTrace((LPARAM) this, "INFO: Leaving GetNextConnection because of Shutdown event");
  838. fMembersUnsafe = TRUE;
  839. hr = AQUEUE_E_SHUTDOWN;
  840. goto Exit;
  841. }
  842. else if (WAIT_OBJECT_RELEASE_EVENT == dwWaitResult)
  843. {
  844. DebugTrace((LPARAM) this, "INFO: Leaving GetNextConnection because ReleaseAllWaitingThreads called");
  845. //Caller asked that all threads be released
  846. *ppISMTPConnection = NULL;
  847. hr = AQUEUE_E_SHUTDOWN;
  848. fMembersUnsafe = TRUE;
  849. goto Exit;
  850. }
  851. _ASSERT((WAIT_OBJECT_0 == dwWaitResult - 1) || (WAIT_TIMEOUT == dwWaitResult));
  852. //Re-aquire lock
  853. if (!fTryShutdownLock())
  854. {
  855. hr = AQUEUE_E_SHUTDOWN;
  856. goto Exit;
  857. }
  858. else
  859. {
  860. m_paqinst->RoutingShareLock();
  861. fLocked = true;
  862. }
  863. cLinksToTry = m_pqol->cGetCount();
  864. fForceWait = false; //only force wait once in a row
  865. cConnections = InterlockedIncrement((PLONG) &m_cConnections);
  866. fOwnConnectionCount = TRUE;
  867. }
  868. _ASSERT(cConnections <= cMaxConnections);
  869. cLinksToTry--;
  870. //NK**Insted of Dequeue we should lock and peek to see if the link
  871. //needs to be dequed
  872. //If the peek is quick it will be better than dequeing and then
  873. //enquing it in order
  874. //Move this complete check into peek
  875. hr = m_pqol->HrDequeue(&plmq);
  876. if (FAILED(hr))
  877. {
  878. if (AQUEUE_E_QUEUE_EMPTY == hr) //not really an error
  879. {
  880. hr = S_OK;
  881. fForceWait = true;
  882. continue;
  883. }
  884. else
  885. goto Exit; //need to handle case of empty queues a little better
  886. }
  887. hr = plmq->HrCreateConnectionIfNeeded(cMaxLinkConnections,
  888. cMinMessagesPerConnection, cMaxMessagesPerConnection,
  889. this, &pSMTPConn);
  890. if (FAILED(hr))
  891. {
  892. ErrorTrace((LPARAM) this,
  893. "ERROR: HrCreateConnectionIfNeeded failed - hr 0x%08X", hr);
  894. goto Exit;
  895. }
  896. if (S_OK == hr)
  897. {
  898. _ASSERT(pSMTPConn);
  899. //take this opportunity to see if it need queueing
  900. cTimesQueued = plmq->DecrementConnMgrCount();
  901. if (!cTimesQueued)
  902. {
  903. plmq->IncrementConnMgrCount();
  904. hr = m_pqol->HrEnqueue(plmq);
  905. //If we fail here, we are in serious trouble...
  906. //A link has been lost - we should probably log an event $$TODO
  907. if (FAILED(hr))
  908. {
  909. plmq->DecrementConnMgrCount();
  910. DebugTrace((LPARAM) hr, "ERROR: Unable to requeue link 0x%8X", plmq);
  911. goto Exit;
  912. }
  913. }
  914. hr = plmq->HrGetSMTPDomain(&cbDomain, &szDomain);
  915. if (FAILED(hr))
  916. goto Exit;
  917. DebugTrace((LPARAM) plmq, "INFO: Allocating new connection for domain %s", szDomain);
  918. break;
  919. }
  920. else
  921. {
  922. _ASSERT(!pSMTPConn);
  923. //The link does not need a connection - queue the link and look at
  924. //the next in line.
  925. //Check if this link can be delete (will increment ConnMgrCount if
  926. //it can
  927. plmq->RemoveLinkIfEmpty();
  928. cTimesQueued = plmq->DecrementConnMgrCount();
  929. if (!cTimesQueued)
  930. {
  931. plmq->IncrementConnMgrCount();
  932. hr = m_pqol->HrEnqueue(plmq);
  933. if (FAILED(hr))
  934. {
  935. plmq->DecrementConnMgrCount();
  936. DebugTrace((LPARAM) hr,
  937. "ERROR: Unable to requeue link 0x%8X", plmq);
  938. goto Exit;
  939. }
  940. }
  941. plmq->Release();
  942. plmq = NULL;
  943. }
  944. _ASSERT(fLocked);
  945. m_paqinst->RoutingShareUnlock();
  946. ShutdownUnlock();
  947. fLocked = false;
  948. }
  949. *ppISMTPConnection = (ISMTPConnection *) pSMTPConn;
  950. fOwnConnectionCount = FALSE;
  951. Exit:
  952. //NOTE: We *cannot* touch member variables until we determine that
  953. //we are not shutting down, because SMTP may have a thread in here
  954. //after this object is destroyed.
  955. //make sure connection count is correct if we couldn't create a connection
  956. if (fOwnConnectionCount)
  957. {
  958. _ASSERT(!fMembersUnsafe);
  959. InterlockedDecrement((PLONG) &m_cConnections);
  960. }
  961. if (NULL != plmq)
  962. plmq->Release();
  963. if (fLocked)
  964. {
  965. _ASSERT(!fMembersUnsafe);
  966. m_paqinst->RoutingShareUnlock();
  967. ShutdownUnlock();
  968. }
  969. if (FAILED(hr) && pSMTPConn)
  970. {
  971. if (hr != AQUEUE_E_SHUTDOWN)
  972. ErrorTrace((LPARAM) this, "ERROR: GetNextConnection failed - hr 0x%08X", hr);
  973. if (pSMTPConn)
  974. {
  975. pSMTPConn->Release();
  976. *ppISMTPConnection = NULL;
  977. }
  978. }
  979. TraceFunctLeave();
  980. return hr;
  981. }
  982. //---[ ConnMgr::GetNamedConnection ]-------------------------------------------
  983. //
  984. //
  985. // Description:
  986. // Implements IConnectionManager::GetNamedConnection
  987. //
  988. // Returns a connection for the specifically requested connection (if it
  989. // exists). Unlike GetNextConnection, this call will not block, it will
  990. // immediately succeed or fail.
  991. // Parameters:
  992. // IN cbSMTPDomain Length of domain name (strlen)
  993. // IN szSMTPDomain SMTP Domain of requested connection
  994. // OUT ppismtpconn Returned SMTP Connection interface
  995. // Returns:
  996. // S_OK on success
  997. // AQUEUE_E_INVALID_DOMAIN if no link exists for the domain
  998. // AQUEUE_E_QUEUE_EMPTY if link exists but there are no messages on it
  999. //
  1000. //-----------------------------------------------------------------------------
  1001. STDMETHODIMP CConnMgr::GetNamedConnection(
  1002. IN DWORD cbSMTPDomain,
  1003. IN char szSMTPDomain[],
  1004. OUT ISMTPConnection **ppISMTPConnection)
  1005. {
  1006. TraceFunctEnterEx((LPARAM) this, "CConnMgr::GetNamedConnection");
  1007. HRESULT hr = S_OK;
  1008. CDomainEntry *pdentry = NULL;
  1009. CAQScheduleID aqsched;
  1010. CSMTPConn *pSMTPConn = NULL;
  1011. CLinkMsgQueue *plmq = NULL;
  1012. CDomainEntryLinkIterator delit;
  1013. DWORD cMessages = 0;
  1014. DWORD cConnectionsOnLink = 0;
  1015. _ASSERT(ppISMTPConnection);
  1016. *ppISMTPConnection = NULL;
  1017. if (fConnectionsStoppedByAdmin()) //Can't create connections
  1018. {
  1019. hr = S_OK;
  1020. goto Exit;
  1021. }
  1022. //Check if it has a queue in DMT for it
  1023. hr = m_paqinst->HrGetDomainEntry(cbSMTPDomain, szSMTPDomain, &pdentry);
  1024. if (FAILED(hr))
  1025. {
  1026. //If we do not have a DMQ corresponding to it
  1027. //we should respond with zero message
  1028. if( hr != AQUEUE_E_INVALID_DOMAIN && hr != DOMHASH_E_NO_SUCH_DOMAIN)
  1029. {
  1030. hr = AQ_E_SMTP_ETRN_INTERNAL_ERROR;
  1031. }
  1032. else
  1033. {
  1034. hr = S_OK;
  1035. }
  1036. goto Exit;
  1037. }
  1038. //NK** : Can we live with this single call
  1039. //The assumption being that domain configured for TURN will
  1040. //always have only one link associated with it
  1041. hr = delit.HrInitialize(pdentry);
  1042. if (FAILED(hr))
  1043. {
  1044. //Treat as no-link case
  1045. ErrorTrace((LPARAM) this, "Initializing link iterator failed - hr 0x%08X", hr);
  1046. hr = S_OK;
  1047. goto Exit;
  1048. }
  1049. plmq = delit.plmqGetNextLinkMsgQueue(plmq);
  1050. if (!plmq)
  1051. {
  1052. //If we do not have a link corresponding to it
  1053. //we should report the error back to SMTP
  1054. hr = S_OK;
  1055. goto Exit;
  1056. }
  1057. //Check if there are connections for this link that exist
  1058. //
  1059. cConnectionsOnLink = plmq->cGetConnections();
  1060. if(cConnectionsOnLink)
  1061. {
  1062. //Do not allow multiple connections on TURN domains
  1063. //It does not make much sense
  1064. hr = S_OK;
  1065. goto Exit;
  1066. }
  1067. //get the msg count from the dmq
  1068. cMessages = plmq->cGetTotalMsgCount();
  1069. if(cMessages)
  1070. {
  1071. //Create the connection with no message limit
  1072. pSMTPConn = new CSMTPConn(this, plmq, 0);
  1073. if (NULL == pSMTPConn)
  1074. {
  1075. hr = E_OUTOFMEMORY;
  1076. goto Exit;
  1077. }
  1078. plmq->AddConnection(pSMTPConn);
  1079. *ppISMTPConnection = (ISMTPConnection *) pSMTPConn;
  1080. InterlockedIncrement((PLONG) &m_cConnections);
  1081. //Now enable the link for turned connections
  1082. plmq->dwModifyLinkState(LINK_STATE_PRIV_TURN_ENABLED, LINK_STATE_NO_ACTION);
  1083. goto Exit;
  1084. }
  1085. else
  1086. {
  1087. hr = S_OK;
  1088. goto Exit;
  1089. }
  1090. Exit:
  1091. if (pdentry)
  1092. pdentry->Release();
  1093. if(plmq)
  1094. plmq->Release();
  1095. TraceFunctLeave();
  1096. return hr;
  1097. }
  1098. //---[ CConnMgr::ReleaseWaitingThreads ]---------------------------------------
  1099. //
  1100. //
  1101. // Description:
  1102. // Releases all threads waiting on get next connection.
  1103. // Parameters:
  1104. // -
  1105. // Returns:
  1106. // AQUEUE_E_NOT_INITIALIZED if event handle does not exist
  1107. //
  1108. //-----------------------------------------------------------------------------
  1109. STDMETHODIMP CConnMgr::ReleaseWaitingThreads()
  1110. {
  1111. HRESULT hr = S_OK;
  1112. if (m_paqinst)
  1113. m_paqinst->SetShutdownHint();
  1114. if (NULL == m_hReleaseAllEvent)
  1115. {
  1116. hr = AQUEUE_E_NOT_INITIALIZED;
  1117. goto Exit;
  1118. }
  1119. //Since this is an manual-reset event, we will need to Set the Event
  1120. //NOTE: Using PulseEvent here would sometimes cause the system to hang
  1121. //on shutdown.
  1122. if (!SetEvent(m_hReleaseAllEvent))
  1123. hr = HRESULT_FROM_WIN32(GetLastError());
  1124. Exit:
  1125. return hr;
  1126. }
  1127. //---[ CreateETRNDomainList ]-----------------------------------------------------------
  1128. //
  1129. //
  1130. // Description:
  1131. // Implements CreateETRNDomainList. A function passed to the
  1132. // DCT iterator to create a list of subdomains corresponding to the ETRN requests
  1133. // of type @domain
  1134. //
  1135. // Parameters:
  1136. //
  1137. // Returns:
  1138. //
  1139. //
  1140. //---------------------------------------------------------------------------------
  1141. VOID CreateETRNDomainList(PVOID pvContext, PVOID pvData, BOOL fWildcard,
  1142. BOOL *pfContinue, BOOL *pfDelete)
  1143. {
  1144. CInternalDomainInfo *pIntDomainInfo = (CInternalDomainInfo*)pvData;
  1145. ETRNCTX *pETRNCtx = (ETRNCTX*) pvContext;
  1146. *pfContinue = TRUE;
  1147. *pfDelete = FALSE;
  1148. HRESULT hr = S_OK;
  1149. TraceFunctEnterEx((LPARAM) NULL, "ETRNSubDomains");
  1150. //We simply create a list of domains in DMT that match our pattern
  1151. //IDI stands for InternalDomainInfo
  1152. if( pETRNCtx && pIntDomainInfo)
  1153. {
  1154. //We add it to the array and add a reference to it
  1155. pETRNCtx->rIDIList[pETRNCtx->cIDICount] = pIntDomainInfo;
  1156. pIntDomainInfo->AddRef();
  1157. if(++pETRNCtx->cIDICount >= MAX_ETRNDOMAIN_PER_COMMAND)
  1158. {
  1159. _ASSERT(0);
  1160. pETRNCtx->hr = AQUEUE_E_ETRN_TOO_MANY_DOMAINS;
  1161. *pfContinue = FALSE;
  1162. }
  1163. }
  1164. else
  1165. {
  1166. if (pETRNCtx)
  1167. pETRNCtx->hr = AQ_E_SMTP_ETRN_INTERNAL_ERROR;
  1168. *pfContinue = FALSE;
  1169. }
  1170. TraceFunctLeave();
  1171. return;
  1172. }
  1173. //---[ LookupQueueforETRN ]--------------------------------------------------
  1174. //
  1175. //
  1176. // Description:
  1177. // Implements LookupQueueforETRN. A function passed to the
  1178. // DMT iterator to lookup all queues for a wild card domain
  1179. //
  1180. // Parameters:
  1181. // Returns:
  1182. //
  1183. //
  1184. //---------------------------------------------------------------------------------
  1185. VOID LookupQueueforETRN(PVOID pvContext, PVOID pvData, BOOL fWildcard,
  1186. BOOL *pfContinue, BOOL *pfDelete)
  1187. {
  1188. CDomainEntry *pdentry = (CDomainEntry*)pvData;
  1189. CLinkMsgQueue *plmq = NULL;
  1190. CDomainEntryLinkIterator delit;
  1191. CInternalDomainInfo *pIntDomainInfo =NULL;
  1192. ETRNCTX *pETRNCtx = (ETRNCTX*) pvContext;
  1193. char *szSMTPDomain = NULL;
  1194. DWORD cbSMTPDomain = 0;
  1195. DWORD cMessages = 0;
  1196. HRESULT hr = S_OK;
  1197. *pfContinue = TRUE;
  1198. *pfDelete = FALSE;
  1199. TraceFunctEnterEx((LPARAM) NULL, "ETRNSubDomains");
  1200. //If the Domain has messages it is candidate for ETRN
  1201. //Get the link msg queue from the DMT entry
  1202. hr = delit.HrInitialize(pdentry);
  1203. if (FAILED(hr))
  1204. goto Exit;
  1205. while (plmq = delit.plmqGetNextLinkMsgQueue(plmq))
  1206. {
  1207. //get the msg count from the dmq
  1208. cMessages = plmq->cGetTotalMsgCount();
  1209. if(cMessages)
  1210. {
  1211. //get the name of the domain we are currently considering
  1212. hr = pdentry->HrGetDomainName(&szSMTPDomain);
  1213. if (FAILED(hr))
  1214. {
  1215. //we had some internal error we need to stop iterating
  1216. //Set the Hr in context
  1217. DebugTrace((LPARAM) NULL, "Failed to get message count for %s", szSMTPDomain);
  1218. *pfContinue = FALSE;
  1219. pETRNCtx->hr = AQ_E_SMTP_ETRN_INTERNAL_ERROR;
  1220. goto Exit;
  1221. }
  1222. cbSMTPDomain = lstrlen(szSMTPDomain);
  1223. //Lookup it up in the DCT to see if there is an entry that conflicts with this
  1224. //If there is no exact match the lookup will comeup with the closest configured
  1225. //ancestor
  1226. hr = pETRNCtx->paqinst->HrGetInternalDomainInfo(cbSMTPDomain, szSMTPDomain, &pIntDomainInfo);
  1227. if (FAILED(hr))
  1228. {
  1229. //It must match the "*" domain at least
  1230. //Otherwise we had some internal error we need to stop iterating
  1231. //Set the Hr in context
  1232. *pfContinue = FALSE;
  1233. pETRNCtx->hr = AQ_E_SMTP_ETRN_INTERNAL_ERROR;
  1234. goto Exit;
  1235. }
  1236. else
  1237. {
  1238. _ASSERT(pIntDomainInfo);
  1239. //If that ancestor configured for ETRN and it is not the root, we enable it
  1240. //else we skip domain
  1241. //
  1242. if ((pIntDomainInfo->m_DomainInfo.dwDomainInfoFlags & DOMAIN_INFO_ETRN_ONLY) &&
  1243. pIntDomainInfo->m_DomainInfo.cbDomainNameLength != 1)
  1244. {
  1245. pETRNCtx->cMessages += cMessages;
  1246. cMessages = 0;
  1247. //If it does - trigger the links.
  1248. DebugTrace((LPARAM) NULL, "Enabling ETRN for domain %s", szSMTPDomain);
  1249. plmq->dwModifyLinkState(
  1250. LINK_STATE_PRIV_ETRN_ENABLED | LINK_STATE_RETRY_ENABLED,
  1251. LINK_STATE_NO_ACTION);
  1252. }
  1253. } //If we have a valid IntDomainInfo
  1254. } //Message count is zero
  1255. } //looping over lmq's for entry
  1256. Exit:
  1257. if (pIntDomainInfo)
  1258. pIntDomainInfo->Release();
  1259. if (szSMTPDomain)
  1260. FreePv(szSMTPDomain);
  1261. if (plmq)
  1262. plmq->Release();
  1263. return;
  1264. }
  1265. //---[ CConnMgr::ETRNDomainList ]--------------------------------------------------
  1266. //
  1267. //
  1268. // Description:
  1269. // Implements IConnectionManager:ETRNDomainList. Used to ETRN appropriate
  1270. // domains based on the list of CInternalDomainInfo passed in
  1271. // Parameters:
  1272. //
  1273. // Returns:
  1274. //
  1275. //
  1276. //-----------------------------------------------------------------------------
  1277. HRESULT CConnMgr::ETRNDomainList(ETRNCTX *pETRNCtx)
  1278. {
  1279. CInternalDomainInfo *pIntDomainInfo = NULL;
  1280. BOOL fWildcard = FALSE;
  1281. HRESULT hr = S_OK;
  1282. DWORD i = 0;
  1283. TraceFunctEnterEx((LPARAM) this, "CConnMgr::ETRNDomain");
  1284. //NK** Do I need to sort the pointers for duplicates ?
  1285. if(!pETRNCtx->cIDICount)
  1286. {
  1287. //We have nothing in our list
  1288. //
  1289. hr = AQUEUE_E_INVALID_DOMAIN;
  1290. goto Exit;
  1291. }
  1292. for(; i < pETRNCtx->cIDICount; i++)
  1293. {
  1294. if(!(pIntDomainInfo = pETRNCtx->rIDIList[i]))
  1295. {
  1296. //Error happend
  1297. pETRNCtx->hr = AQ_E_SMTP_ETRN_INTERNAL_ERROR;
  1298. break;
  1299. }
  1300. //We go ahead only if the domain is marked for ETRN
  1301. if ((pIntDomainInfo->m_DomainInfo.dwDomainInfoFlags & DOMAIN_INFO_ETRN_ONLY))
  1302. {
  1303. //check if this is wild card domain
  1304. fWildcard = FALSE;
  1305. if( pIntDomainInfo->m_DomainInfo.szDomainName[0] == '*' &&
  1306. pIntDomainInfo->m_DomainInfo.cbDomainNameLength != 1)
  1307. {
  1308. fWildcard = TRUE;
  1309. }
  1310. //If the domain in the list is a wild card entry then
  1311. if(fWildcard)
  1312. {
  1313. //So we have atleast one matching ETRN domain configured
  1314. if(pETRNCtx->hr == S_OK)
  1315. pETRNCtx->hr = AQ_S_SMTP_WILD_CARD_NODE;
  1316. //Lookup this domain and all its subdomains in the DMT
  1317. //skip over the leading "*."
  1318. hr = pETRNCtx->paqinst->HrIterateDMTSubDomains(pIntDomainInfo->m_DomainInfo.szDomainName + 2,
  1319. pIntDomainInfo->m_DomainInfo.cbDomainNameLength - 2,
  1320. (DOMAIN_ITR_FN)LookupQueueforETRN, pETRNCtx);
  1321. if (FAILED(hr) && hr != DOMHASH_E_NO_SUCH_DOMAIN && hr != AQUEUE_E_INVALID_DOMAIN)
  1322. {
  1323. DebugTrace((LPARAM) NULL, "ERROR calling HrIterateDMTSubDomains");
  1324. goto Exit;
  1325. }
  1326. } // wild card DCT entry
  1327. else
  1328. {
  1329. //Start the queue for the entry
  1330. hr = StartETRNQueue(pIntDomainInfo->m_DomainInfo.cbDomainNameLength,
  1331. pIntDomainInfo->m_DomainInfo.szDomainName,
  1332. pETRNCtx);
  1333. if (FAILED(hr))
  1334. {
  1335. //NK** This actually may not be an error
  1336. //If we do not have a DMQ corresponding to it
  1337. //we should respond with zero message
  1338. if( hr != AQUEUE_E_INVALID_DOMAIN && hr != DOMHASH_E_NO_SUCH_DOMAIN)
  1339. {
  1340. pETRNCtx->hr = AQ_E_SMTP_ETRN_INTERNAL_ERROR;
  1341. goto Exit;
  1342. }
  1343. else
  1344. continue;
  1345. }
  1346. } //not a wild card DCT entry
  1347. }
  1348. }
  1349. Exit:
  1350. TraceFunctLeave();
  1351. return hr;
  1352. }
  1353. //---[ CConnMgr::StartETRNQueue ]--------------------------------------------------
  1354. //
  1355. //
  1356. // Description:
  1357. // Implements CConnMgr::StartETRNQueuet. Used to start the queue for any
  1358. // domain configured for ETRN
  1359. // Parameters:
  1360. //
  1361. // Returns:
  1362. //
  1363. //
  1364. //-----------------------------------------------------------------------------
  1365. HRESULT CConnMgr::StartETRNQueue(IN DWORD cbSMTPDomain,
  1366. IN char szSMTPDomain[],
  1367. ETRNCTX *pETRNCtx)
  1368. {
  1369. CDomainEntry *pdentry = NULL;
  1370. CDomainEntryLinkIterator delit;
  1371. CLinkMsgQueue *plmq = NULL;
  1372. CAQSvrInst *paqinst = pETRNCtx->paqinst;
  1373. DWORD cMessages = 0;
  1374. HRESULT hr = S_OK;
  1375. TraceFunctEnterEx((LPARAM) this, "CConnMgr::ETRNDomain");
  1376. //So we have a domain configured for ETRN by this name
  1377. if( pETRNCtx->hr == S_OK)
  1378. pETRNCtx->hr = AQ_S_SMTP_VALID_ETRN_DOMAIN;
  1379. //Check if it has a queue in DMT for it
  1380. hr = pETRNCtx->paqinst->HrGetDomainEntry(cbSMTPDomain, szSMTPDomain, &pdentry);
  1381. if (FAILED(hr))
  1382. {
  1383. //If we do not have a DMQ corresponding to it
  1384. //we should respond with zero message
  1385. if( hr != AQUEUE_E_INVALID_DOMAIN && hr != DOMHASH_E_NO_SUCH_DOMAIN)
  1386. {
  1387. pETRNCtx->hr = AQ_E_SMTP_ETRN_INTERNAL_ERROR;
  1388. }
  1389. goto Exit;
  1390. }
  1391. hr = delit.HrInitialize(pdentry);
  1392. if (FAILED(hr))
  1393. goto Exit;
  1394. while (plmq = delit.plmqGetNextLinkMsgQueue(plmq))
  1395. {
  1396. //get the msg count from the dmq
  1397. cMessages = plmq->cGetTotalMsgCount();
  1398. if(cMessages)
  1399. {
  1400. pETRNCtx->cMessages += cMessages;
  1401. cMessages = 0;
  1402. //If it does - trigger the link.
  1403. DebugTrace((LPARAM) NULL, "Enabling ETRN for domain %s", szSMTPDomain);
  1404. plmq->dwModifyLinkState(
  1405. LINK_STATE_PRIV_ETRN_ENABLED | LINK_STATE_RETRY_ENABLED,
  1406. LINK_STATE_NO_ACTION);
  1407. }
  1408. }
  1409. Exit:
  1410. if (pdentry)
  1411. pdentry->Release();
  1412. if (plmq)
  1413. plmq->Release();
  1414. TraceFunctLeave();
  1415. return hr;
  1416. }
  1417. //---[ CConnMgr::ETRNDomain ]----------------------------------------------------
  1418. //
  1419. //
  1420. // Description:
  1421. // Implements IConnectionManager:ETRNDomain. Used to reqeust that a
  1422. // domain be ETRN'd (enabled for outbound connections).
  1423. // Parameters:
  1424. // IN cbSMTPDomain String length of domain name
  1425. // IN szSMTPDomain SMTP Domain name. Wildcarded names start with
  1426. // a "@" (eg "@foo.com");
  1427. // OUT pcMessages # of Messages queued for ETRN domain
  1428. // Returns:
  1429. // Remarks:
  1430. // If the received domain is wildcarded '@' then we follow this logic :
  1431. // Lookup this node and every subnode of this node in DCT. The lookup is done
  1432. // using table iterator and iterator function CreateETRNDomainList. For every entry
  1433. // that is found with ETRN flag set, lookup if any queues exist in DMT. If the
  1434. // queue exist and have messages in them then we enable the corresponding links.
  1435. // If the lookup in DCT yields a domain that is configured as wild card '*.",
  1436. // then we lookup all queus corresponding to all sub domains of that domain. We do
  1437. // this using the iterator and iterator function LookupQueueforETRN. For every queue
  1438. // found by iterator the function checks back in DMT if the domain is configured for
  1439. // ETRN. This is to take care of situations where one specific subdomain of a wild
  1440. // card configured domain may be not configured for etrn.
  1441. // Eg : *.foo.com => ETRN, but 1.foo.com => NO_ETRN
  1442. //
  1443. // Both calls to iterate are covered with reader locks. The lock stays valid for
  1444. // the duration of all iterations.
  1445. // The iterator function used during DCT iterations also adds reference to every
  1446. // InternalDomainInfo as we need the data to stay valid after the table lock is released.
  1447. //----------------------------------------------------------------------------------
  1448. STDMETHODIMP CConnMgr::ETRNDomain(
  1449. IN DWORD cbSMTPDomain,
  1450. IN char szSMTPDomain[],
  1451. OUT DWORD *pcMessages)
  1452. {
  1453. HRESULT hr = S_OK;
  1454. BOOL fLocked = FALSE;
  1455. CDomainEntry *pdentry = NULL;
  1456. CDestMsgQueue *pdmq = NULL;
  1457. CInternalDomainInfo *pIntDomainInfo =NULL;
  1458. BOOL fETRNSubDomains = FALSE;
  1459. char * szTmpDomain = szSMTPDomain;
  1460. ETRNCTX EtrnCtx;
  1461. EtrnCtx.hr = S_OK;
  1462. EtrnCtx.cMessages = 0;
  1463. EtrnCtx.paqinst = NULL;
  1464. EtrnCtx.cIDICount = 0;
  1465. EtrnCtx.rIDIList[0] = NULL;
  1466. TraceFunctEnterEx((LPARAM) this, "CConnMgr::ETRNDomain");
  1467. DWORD cMessages = 0;
  1468. *pcMessages = 0; //$$TODO - Get real values
  1469. if (!fTryShutdownLock())
  1470. {
  1471. hr = AQUEUE_E_SHUTDOWN;
  1472. goto Exit;
  1473. }
  1474. m_paqinst->RoutingShareLock();
  1475. fLocked = TRUE;
  1476. EtrnCtx.paqinst = m_paqinst;
  1477. //do we have a '@' request
  1478. if(*szTmpDomain == '@')
  1479. fETRNSubDomains = TRUE;
  1480. //If we do have '@' request, we need to skip the first chararcter
  1481. //and then look for every sub domain of the domain in the DCT
  1482. //For every subdomain that we find with ETRN flag, we will lookup the
  1483. //DMT to see if there is a queue
  1484. //If the entry we find in DCT is of wildcard type, we will lookup all
  1485. //subdomains of that domain in DMT looking for all queues destined for
  1486. //subdomains of the DCT entry.
  1487. if(fETRNSubDomains)
  1488. {
  1489. ++szTmpDomain;
  1490. //Create a list of all subdomains of this domain in the DCT
  1491. hr = m_paqinst->HrIterateDCTSubDomains(szTmpDomain, lstrlen(szTmpDomain),
  1492. (DOMAIN_ITR_FN)CreateETRNDomainList, &EtrnCtx);
  1493. //If we fail to look up single domain
  1494. if (FAILED(hr))
  1495. {
  1496. if(hr == AQUEUE_E_INVALID_DOMAIN || hr == DOMHASH_E_NO_SUCH_DOMAIN)
  1497. {
  1498. DebugTrace((LPARAM)this, "ERROR calling HrIterateDCTSubdomains");
  1499. hr = hr = AQ_E_SMTP_ETRN_NODE_INVALID;
  1500. }
  1501. else
  1502. {
  1503. DebugTrace((LPARAM)this, "ERROR calling HrIterateDCTSubdomains");
  1504. hr = AQ_E_SMTP_ETRN_INTERNAL_ERROR;
  1505. }
  1506. goto Exit;
  1507. }
  1508. //Check if the lookup got us anything
  1509. if(!FAILED(EtrnCtx.hr))
  1510. {
  1511. //Check if any queus can be started for domains in the list
  1512. //Start if possible
  1513. hr = ETRNDomainList(&EtrnCtx);
  1514. if (FAILED(hr))
  1515. {
  1516. if(hr != AQUEUE_E_INVALID_DOMAIN && hr != DOMHASH_E_NO_SUCH_DOMAIN)
  1517. {
  1518. DebugTrace((LPARAM)this, "ERROR calling ETRNSubDomain");
  1519. hr = AQ_E_SMTP_ETRN_INTERNAL_ERROR;
  1520. goto Exit;
  1521. }
  1522. }
  1523. //If we saw atleast one matching domain
  1524. if(EtrnCtx.hr == AQ_S_SMTP_VALID_ETRN_DOMAIN || EtrnCtx.hr == AQ_S_SMTP_WILD_CARD_NODE)
  1525. {
  1526. *pcMessages = EtrnCtx.cMessages;
  1527. hr = EtrnCtx.hr;
  1528. }
  1529. else
  1530. hr = AQ_E_SMTP_ETRN_NODE_INVALID;
  1531. }
  1532. else
  1533. hr = AQ_E_SMTP_ETRN_INTERNAL_ERROR;
  1534. goto Exit;
  1535. }
  1536. else
  1537. {
  1538. //Lookup the domain in the domain cfg table and see if it has ETRN bit set
  1539. _ASSERT(m_paqinst);
  1540. hr = m_paqinst->HrGetInternalDomainInfo(cbSMTPDomain, szSMTPDomain, &pIntDomainInfo);
  1541. if (FAILED(hr))
  1542. {
  1543. //It must match the "*" domain at least
  1544. _ASSERT(AQUEUE_E_INVALID_DOMAIN != hr);
  1545. hr = AQ_E_SMTP_ETRN_INTERNAL_ERROR;
  1546. goto Exit;
  1547. }
  1548. else
  1549. {
  1550. _ASSERT(pIntDomainInfo);
  1551. EtrnCtx.rIDIList[0] = pIntDomainInfo;
  1552. EtrnCtx.cIDICount = 1;
  1553. //We will not ETRN if the closest ancestor is Root or two level
  1554. //NK** implement search for two level
  1555. if( pIntDomainInfo->m_DomainInfo.cbDomainNameLength == 1)
  1556. {
  1557. //Cannot ETRN based on the root domain
  1558. hr = AQ_E_SMTP_ETRN_NODE_INVALID;
  1559. goto Exit;
  1560. }
  1561. if ((pIntDomainInfo->m_DomainInfo.dwDomainInfoFlags & DOMAIN_INFO_ETRN_ONLY))
  1562. {
  1563. //Start the queue if exists for this domain
  1564. hr = StartETRNQueue(cbSMTPDomain, szSMTPDomain,&EtrnCtx);
  1565. if (FAILED(hr))
  1566. {
  1567. if(hr != AQUEUE_E_INVALID_DOMAIN && hr != DOMHASH_E_NO_SUCH_DOMAIN)
  1568. {
  1569. DebugTrace((LPARAM)this, "ERROR calling ETRNSubDomain");
  1570. hr = AQ_E_SMTP_ETRN_INTERNAL_ERROR;
  1571. goto Exit;
  1572. }
  1573. }
  1574. //If we saw atleast one matching domain
  1575. if(EtrnCtx.hr == AQ_S_SMTP_VALID_ETRN_DOMAIN || EtrnCtx.hr == AQ_S_SMTP_WILD_CARD_NODE)
  1576. {
  1577. *pcMessages = EtrnCtx.cMessages;
  1578. hr = AQ_S_SMTP_VALID_ETRN_DOMAIN;
  1579. }
  1580. else
  1581. hr = AQ_E_SMTP_ETRN_NODE_INVALID;
  1582. goto Exit;
  1583. }
  1584. else
  1585. {
  1586. //Cannot ETRN based on the root domain
  1587. hr = AQ_E_SMTP_ETRN_NODE_INVALID;
  1588. goto Exit;
  1589. }
  1590. }
  1591. }
  1592. Exit:
  1593. //wake up thread in GetNextConnection
  1594. if (SUCCEEDED(hr) &&SUCCEEDED(EtrnCtx.hr) && EtrnCtx.cMessages)
  1595. _VERIFY(SetEvent(m_hNextConnectionEvent));
  1596. if (fLocked)
  1597. {
  1598. m_paqinst->RoutingShareUnlock();
  1599. ShutdownUnlock();
  1600. }
  1601. //free up all InternalDomainInfo
  1602. for(DWORD i=0; i < EtrnCtx.cIDICount; i++)
  1603. if (EtrnCtx.rIDIList[i])
  1604. EtrnCtx.rIDIList[i]->Release();
  1605. if (pdentry)
  1606. pdentry->Release();
  1607. TraceFunctLeave();
  1608. return hr;
  1609. }
  1610. //---[ CConnMgr::ModifyLinkState ]---------------------------------------------
  1611. //
  1612. //
  1613. // Description:
  1614. // Link state can change so that connections can(not) be created for a link.
  1615. // Parameters:
  1616. // IN cbDomainName String length of domain name
  1617. // IN szDomainName Domain Name to enable
  1618. // IN dwScheduleID ScheduleID of <domain, schedule> pair
  1619. // IN rguidTransportSink GUID of router associated with link
  1620. // IN dwFlagsToSet Link State Flags to set
  1621. // IN dwFlagsToUnset Link State Flags to unset
  1622. // Returns:
  1623. // S_OK on success
  1624. // AQUEUE_E_INVALID_DOMAIN if domain does not exist
  1625. //
  1626. //-----------------------------------------------------------------------------
  1627. HRESULT CConnMgr::ModifyLinkState(
  1628. IN DWORD cbDomainName,
  1629. IN char szDomainName[],
  1630. IN DWORD dwScheduleID,
  1631. IN GUID rguidTransportSink,
  1632. IN DWORD dwFlagsToSet,
  1633. IN DWORD dwFlagsToUnset)
  1634. {
  1635. HRESULT hr = S_OK;
  1636. BOOL fLocked = FALSE;
  1637. CDomainEntry *pdentry = NULL;
  1638. CLinkMsgQueue *plmq = NULL;
  1639. CAQScheduleID aqsched(rguidTransportSink, dwScheduleID);
  1640. if (!cbDomainName || !szDomainName)
  1641. {
  1642. hr = E_INVALIDARG;
  1643. goto Exit;
  1644. }
  1645. if (!fTryShutdownLock())
  1646. {
  1647. hr = AQUEUE_E_SHUTDOWN;
  1648. goto Exit;
  1649. }
  1650. fLocked = TRUE;
  1651. _ASSERT(m_paqinst);
  1652. hr = m_paqinst->HrGetDomainEntry(cbDomainName, szDomainName, &pdentry);
  1653. if (FAILED(hr))
  1654. goto Exit;
  1655. hr = pdentry->HrGetLinkMsgQueue(&aqsched, &plmq);
  1656. if (FAILED(hr))
  1657. goto Exit;
  1658. _ASSERT(plmq);
  1659. //filter out the reserved bits for this "public" API
  1660. plmq->dwModifyLinkState(~LINK_STATE_RESERVED & dwFlagsToSet,
  1661. ~LINK_STATE_RESERVED & dwFlagsToUnset);
  1662. Exit:
  1663. if (fLocked)
  1664. ShutdownUnlock();
  1665. if (pdentry)
  1666. pdentry->Release();
  1667. if (plmq)
  1668. plmq->Release();
  1669. return hr;
  1670. }
  1671. //---[ CConnMgr::UpdateConfigData ]-------------------------------------------
  1672. //
  1673. //
  1674. // Description:
  1675. // Will be used by catmsgq to update the metabase changes
  1676. //
  1677. // Parameters:
  1678. //
  1679. // Returns:
  1680. //
  1681. //-----------------------------------------------------------------------------
  1682. //
  1683. void CConnMgr::UpdateConfigData(IN AQConfigInfo *pAQConfigInfo)
  1684. {
  1685. BOOL fUpdated = FALSE;
  1686. RETRYCONFIG RetryConfig;
  1687. RetryConfig.dwRetryThreshold = g_dwRetryThreshold;
  1688. RetryConfig.dwGlitchRetrySeconds = g_dwGlitchRetrySeconds;
  1689. //
  1690. // This is registry configurable... make sure we have a sane
  1691. // value
  1692. //
  1693. if (!RetryConfig.dwGlitchRetrySeconds)
  1694. RetryConfig.dwGlitchRetrySeconds = 60;
  1695. RetryConfig.dwFirstRetrySeconds = g_dwFirstTierRetrySeconds;
  1696. RetryConfig.dwSecondRetrySeconds = g_dwSecondTierRetrySeconds;
  1697. RetryConfig.dwThirdRetrySeconds = g_dwThirdTierRetrySeconds;
  1698. RetryConfig.dwFourthRetrySeconds = g_dwFourthTierRetrySeconds;
  1699. m_slPrivateData.ExclusiveLock();
  1700. if (pAQConfigInfo->dwAQConfigInfoFlags & AQ_CONFIG_INFO_MAX_CON &&
  1701. MEMBER_OK(pAQConfigInfo, cMaxConnections))
  1702. {
  1703. if ((m_cMaxConnections != pAQConfigInfo->cMaxConnections))
  1704. {
  1705. fUpdated = TRUE;
  1706. //g_cMaxConnections is the number connection objects we
  1707. //reserve with CPool... we can't go above that.
  1708. if (g_cMaxConnections < pAQConfigInfo->cMaxConnections)
  1709. m_cMaxConnections = g_cMaxConnections;
  1710. else
  1711. m_cMaxConnections = pAQConfigInfo->cMaxConnections;
  1712. }
  1713. }
  1714. if (pAQConfigInfo->dwAQConfigInfoFlags & AQ_CONFIG_INFO_MAX_LINK &&
  1715. MEMBER_OK(pAQConfigInfo, cMaxLinkConnections))
  1716. {
  1717. if (m_cMaxLinkConnections != pAQConfigInfo->cMaxLinkConnections)
  1718. {
  1719. fUpdated = TRUE;
  1720. //g_cMaxConnections is the number connection objects we
  1721. //reserve with CPool... we can't go above that.
  1722. if (!pAQConfigInfo->cMaxLinkConnections ||
  1723. (g_cMaxConnections < pAQConfigInfo->cMaxLinkConnections))
  1724. m_cMaxLinkConnections = g_cMaxConnections;
  1725. else
  1726. m_cMaxLinkConnections = pAQConfigInfo->cMaxLinkConnections;
  1727. }
  1728. }
  1729. if (pAQConfigInfo->dwAQConfigInfoFlags & AQ_CONFIG_INFO_MIN_MSG &&
  1730. MEMBER_OK(pAQConfigInfo, cMinMessagesPerConnection))
  1731. {
  1732. if (m_cMinMessagesPerConnection != pAQConfigInfo->cMinMessagesPerConnection)
  1733. {
  1734. fUpdated = TRUE;
  1735. m_cMinMessagesPerConnection = pAQConfigInfo->cMinMessagesPerConnection;
  1736. //Currently we set both these values based on the batching value from SMTP
  1737. m_cMaxMessagesPerConnection = pAQConfigInfo->cMinMessagesPerConnection;
  1738. }
  1739. }
  1740. if (pAQConfigInfo->dwAQConfigInfoFlags & AQ_CONFIG_INFO_CON_WAIT &&
  1741. MEMBER_OK(pAQConfigInfo, dwConnectionWaitMilliseconds))
  1742. {
  1743. if (m_cGetNextConnectionWaitTime != pAQConfigInfo->dwConnectionWaitMilliseconds)
  1744. {
  1745. fUpdated = TRUE;
  1746. m_cGetNextConnectionWaitTime = pAQConfigInfo->dwConnectionWaitMilliseconds;
  1747. }
  1748. }
  1749. if (fUpdated) //only force updated when really required
  1750. InterlockedIncrement((PLONG) &m_dwConfigVersion);
  1751. m_slPrivateData.ExclusiveUnlock();
  1752. fUpdated = FALSE;
  1753. //Retry related config data
  1754. if (pAQConfigInfo->dwAQConfigInfoFlags & AQ_CONFIG_INFO_CON_RETRY &&
  1755. MEMBER_OK(pAQConfigInfo, dwRetryThreshold))
  1756. {
  1757. fUpdated = TRUE;
  1758. RetryConfig.dwRetryThreshold = pAQConfigInfo->dwRetryThreshold;
  1759. }
  1760. if (pAQConfigInfo->dwAQConfigInfoFlags & AQ_CONFIG_INFO_CON_RETRY &&
  1761. MEMBER_OK(pAQConfigInfo, dwFirstRetrySeconds))
  1762. {
  1763. fUpdated = TRUE;
  1764. RetryConfig.dwFirstRetrySeconds = pAQConfigInfo->dwFirstRetrySeconds;
  1765. }
  1766. if (pAQConfigInfo->dwAQConfigInfoFlags & AQ_CONFIG_INFO_CON_RETRY &&
  1767. MEMBER_OK(pAQConfigInfo, dwSecondRetrySeconds))
  1768. {
  1769. fUpdated = TRUE;
  1770. RetryConfig.dwSecondRetrySeconds = pAQConfigInfo->dwSecondRetrySeconds;
  1771. }
  1772. if (pAQConfigInfo->dwAQConfigInfoFlags & AQ_CONFIG_INFO_CON_RETRY &&
  1773. MEMBER_OK(pAQConfigInfo, dwThirdRetrySeconds))
  1774. {
  1775. fUpdated = TRUE;
  1776. RetryConfig.dwThirdRetrySeconds = pAQConfigInfo->dwThirdRetrySeconds;
  1777. }
  1778. if (pAQConfigInfo->dwAQConfigInfoFlags & AQ_CONFIG_INFO_CON_RETRY &&
  1779. MEMBER_OK(pAQConfigInfo, dwFourthRetrySeconds))
  1780. {
  1781. fUpdated = TRUE;
  1782. RetryConfig.dwFourthRetrySeconds = pAQConfigInfo->dwFourthRetrySeconds;
  1783. }
  1784. if (pAQConfigInfo->dwAQConfigInfoFlags & AQ_CONFIG_INFO_CON_RETRY &&
  1785. fUpdated )
  1786. m_pDefaultRetryHandler->UpdateRetryData(&RetryConfig);
  1787. }
  1788. //---[ CConnMgr::RetryLink ]---------------------------------------------------
  1789. //
  1790. //
  1791. // Description:
  1792. // Implements IConnectionRetryManager::RetryLink, which enables the retry
  1793. // sink to enable a link for retry.
  1794. // Parameters:
  1795. // IN cbDomainName String length of domain name
  1796. // IN szDomainName Domain Name to enable
  1797. // IN dwScheduleID ScheduleID of <domain, schedule> pair
  1798. // IN rguidTransportSink GUID of router associated with link
  1799. // Returns:
  1800. // S_OK on success
  1801. // AQUEUE_E_INVALID_DOMAIN if domain does not exist
  1802. // History:
  1803. // 1/9/99 - MikeSwa Created (simplified routing sink)
  1804. //
  1805. //-----------------------------------------------------------------------------
  1806. STDMETHODIMP CConnMgr::RetryLink(
  1807. IN DWORD cbDomainName,
  1808. IN char szDomainName[],
  1809. IN DWORD dwScheduleID,
  1810. IN GUID rguidTransportSink)
  1811. {
  1812. HRESULT hr = S_OK;
  1813. hr = ModifyLinkState(cbDomainName, szDomainName, dwScheduleID,
  1814. rguidTransportSink, LINK_STATE_RETRY_ENABLED,
  1815. LINK_STATE_NO_ACTION);
  1816. //
  1817. // Kick the connections so we know to make one
  1818. //
  1819. KickConnections();
  1820. return hr;
  1821. }