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.

2820 lines
92 KiB

  1. //-----------------------------------------------------------------------------
  2. //
  3. //
  4. // File: linkmsgq.cpp
  5. //
  6. // Description: Implementation of CLinkMsgQueue object.
  7. //
  8. // Author: mikeswa
  9. //
  10. // Copyright (C) 1997 Microsoft Corporation
  11. //
  12. //-----------------------------------------------------------------------------
  13. #include "aqprecmp.h"
  14. #include "dcontext.h"
  15. #include "aqnotify.h"
  16. #include "connmgr.h"
  17. #include "domcfg.h"
  18. #include "smtpconn.h"
  19. #include "smproute.h"
  20. #define CONNECTION_BUFFER_SIZE 10
  21. LinkFlags li; //encourage symbols to appear debug versions
  22. //---[ CLinkMsgQueue::RestartDSNGenerationIfNecessary ]------------------------
  23. //
  24. //
  25. // Description:
  26. // Static wrapper to continue generating DSNs after we have hit our
  27. // limit of time spent in DSNs generation
  28. // Parameters:
  29. // pvContext - "this" pointer for CLinkMsgQueue
  30. // dwStatus - Completion Status
  31. // Returns:
  32. // TRUE always
  33. // History:
  34. // 11/10/1999 - MikeSwa Created
  35. //
  36. //-----------------------------------------------------------------------------
  37. BOOL CLinkMsgQueue::fRestartDSNGenerationIfNecessary(PVOID pvContext,
  38. DWORD dwStatus)
  39. {
  40. TraceFunctEnterEx((LPARAM) pvContext, "CLinkMsgQueue::fRestartDSNGenerationIfNecessary");
  41. CLinkMsgQueue *plmq = (CLinkMsgQueue *) pvContext;
  42. BOOL fHasShutdownLock = FALSE;
  43. BOOL fHasRoutingLock = FALSE;
  44. _ASSERT(plmq);
  45. _ASSERT(LINK_MSGQ_SIG == plmq->m_dwSignature);
  46. DebugTrace((LPARAM) plmq, "Attempting to restart DSN generation");
  47. //Don't try DSN generation if this is not a normal completion
  48. if (dwStatus != ASYNC_WORK_QUEUE_NORMAL)
  49. goto Exit;
  50. //Only attempt to continue DSN genration if we cannot create a connection
  51. //now and have no current connections
  52. if (plmq->m_cConnections)
  53. {
  54. DebugTrace((LPARAM) plmq,
  55. "We have %d connections... skipping DSN generation",
  56. plmq->m_cConnections);
  57. goto Exit;
  58. }
  59. if (fFlagsAllowConnection(plmq->m_dwLinkStateFlags))
  60. {
  61. DebugTrace((LPARAM) plmq,
  62. "We can create a connection, skipping DSN generation - flags 0x%X",
  63. plmq->m_dwLinkStateFlags);
  64. goto Exit;
  65. }
  66. //We need to grab the shutdown and routing lock... just like
  67. //normal DSN generation.
  68. if (!plmq->m_paqinst->fTryShutdownLock())
  69. goto Exit;
  70. fHasShutdownLock = TRUE;
  71. if (!plmq->m_paqinst->fTryRoutingShareLock())
  72. goto Exit;
  73. fHasRoutingLock = TRUE;
  74. //Call to generate DSNs... pass in parameters to always check the
  75. //queues and walk for DSN generation (not just remerge).
  76. plmq->GenerateDSNsIfNecessary(TRUE, FALSE);
  77. Exit:
  78. if (fHasRoutingLock)
  79. plmq->m_paqinst->RoutingShareUnlock();
  80. if (fHasShutdownLock)
  81. plmq->m_paqinst->ShutdownUnlock();
  82. plmq->Release();
  83. TraceFunctLeave();
  84. return TRUE;
  85. }
  86. //---[ CLinkMsgQueue::HrGetInternalInfo ]---------------------------------------
  87. //
  88. //
  89. // Description:
  90. // Private function to get cached link info, and update cached data if
  91. // needed.
  92. //
  93. // NOTE: This is the only way the cached data should be access (other than
  94. // startup and shutdown).
  95. // Parameters:
  96. // OUT ppIntDomainInfo (can be NULL)
  97. // Returns:
  98. // S_OK on success
  99. // AQUEUE_E_SHUTDOWN if queue is shutting down.
  100. //
  101. //-----------------------------------------------------------------------------
  102. HRESULT CLinkMsgQueue::HrGetInternalInfo(OUT CInternalDomainInfo **ppIntDomainInfo)
  103. {
  104. HRESULT hr = S_OK;
  105. _ASSERT(m_cbSMTPDomain);
  106. _ASSERT(m_szSMTPDomain);
  107. if (ppIntDomainInfo)
  108. *ppIntDomainInfo = NULL;
  109. //If we don't currently have domain info and it was not a failure
  110. //condition... don't reget domain info
  111. if (!m_pIntDomainInfo && !(eLinkFlagsGetInfoFailed & m_dwLinkFlags))
  112. goto Exit;
  113. m_slInfo.ShareLock();
  114. //Verify Domain Config Info
  115. while (!m_pIntDomainInfo ||
  116. (m_pIntDomainInfo->m_dwIntDomainInfoFlags & INT_DOMAIN_INFO_INVALID))
  117. {
  118. m_slInfo.ShareUnlock();
  119. m_slInfo.ExclusiveLock();
  120. //another may have gotten exclusive lock in meantime
  121. if (m_pIntDomainInfo &&
  122. !(m_pIntDomainInfo->m_dwIntDomainInfoFlags & INT_DOMAIN_INFO_INVALID))
  123. {
  124. //another thread has updated info
  125. m_slInfo.ExclusiveUnlock();
  126. m_slInfo.ShareLock();
  127. continue;
  128. }
  129. //Domain info is no longer valid at this point
  130. if (m_pIntDomainInfo)
  131. {
  132. m_pIntDomainInfo->Release();
  133. m_pIntDomainInfo = NULL;
  134. }
  135. if (m_dwLinkFlags & eLinkFlagsExternalSMTPLinkInfo) {
  136. hr = m_paqinst->HrGetInternalDomainInfo(m_cbSMTPDomain, m_szSMTPDomain,
  137. &m_pIntDomainInfo);
  138. } else {
  139. hr = m_paqinst->HrGetDefaultDomainInfo(&m_pIntDomainInfo);
  140. }
  141. if (FAILED(hr))
  142. {
  143. dwInterlockedSetBits(&m_dwLinkFlags, eLinkFlagsGetInfoFailed);
  144. m_slInfo.ExclusiveUnlock();
  145. _ASSERT(AQUEUE_E_SHUTDOWN == hr);
  146. goto Exit;
  147. }
  148. _ASSERT(m_pIntDomainInfo);
  149. //Handle change of TURN/ETRN
  150. if (m_pIntDomainInfo->m_DomainInfo.dwDomainInfoFlags &
  151. (DOMAIN_INFO_TURN_ONLY | DOMAIN_INFO_ETRN_ONLY))
  152. {
  153. if (!(m_dwLinkStateFlags & LINK_STATE_PRIV_CONFIG_TURN_ETRN))
  154. {
  155. //Modify link flags to account for TURN/ETRN
  156. dwInterlockedSetBits(&m_dwLinkStateFlags, LINK_STATE_PRIV_CONFIG_TURN_ETRN);
  157. }
  158. }
  159. else if (m_dwLinkStateFlags & LINK_STATE_PRIV_CONFIG_TURN_ETRN)
  160. {
  161. //We used to be TURN/ETRN, but are no-longer configured as such
  162. dwInterlockedUnsetBits(&m_dwLinkStateFlags, LINK_STATE_PRIV_CONFIG_TURN_ETRN);
  163. }
  164. m_slInfo.ExclusiveUnlock();
  165. m_slInfo.ShareLock();
  166. }
  167. //Now we have info... set out param and addref
  168. if (ppIntDomainInfo)
  169. {
  170. *ppIntDomainInfo = m_pIntDomainInfo;
  171. m_pIntDomainInfo->AddRef();
  172. }
  173. //Clear failure bit if set
  174. if (eLinkFlagsGetInfoFailed & m_dwLinkFlags)
  175. dwInterlockedUnsetBits(&m_dwLinkFlags, eLinkFlagsGetInfoFailed);
  176. m_slInfo.ShareUnlock();
  177. Exit:
  178. return hr;
  179. }
  180. //---[ CLinkMsgQueue::InternalInit ]------------------------------------------
  181. //
  182. //
  183. // Description:
  184. // Default constructor for CLinkMsgQueue.
  185. // Parameters:
  186. // -
  187. // Returns:
  188. // -
  189. // History:
  190. // 1/25/99 - MikeSwa Created
  191. //
  192. //-----------------------------------------------------------------------------
  193. void CLinkMsgQueue::InternalInit()
  194. {
  195. TraceFunctEnterEx((LPARAM) this, "CLinkMsgQueue::InternalInit");
  196. m_dwSignature = LINK_MSGQ_SIG;
  197. m_dwLinkFlags = eLinkFlagsClear;
  198. m_dwLinkStateFlags = LINK_STATE_SCHED_ENABLED |
  199. LINK_STATE_RETRY_ENABLED |
  200. LINK_STATE_PRIV_IGNORE_DELETE_IF_EMPTY;
  201. m_paqinst = NULL;
  202. m_cQueues = 0;
  203. m_cConnections = 0;
  204. m_szSMTPDomain = NULL;
  205. m_cbSMTPDomain = 0;
  206. m_pIntDomainInfo = NULL;
  207. m_pdentryLink = NULL;
  208. m_lConnMgrCount = 0;
  209. m_lConsecutiveConnectionFailureCount = 0;
  210. m_lConsecutiveMessageFailureCount = 0;
  211. m_liLinks.Flink = NULL;
  212. m_liLinks.Blink = NULL;
  213. m_szConnectorName = NULL;
  214. m_dwRoundRobinIndex = 0;
  215. m_pILinkStateNotify = NULL;
  216. m_hrDiagnosticError = S_OK;
  217. m_szDiagnosticVerb[0] = '\0';
  218. m_szDiagnosticResponse[0]= '\0';
  219. m_hrLastConnectionFailure= S_OK;
  220. ZeroMemory(&m_ftNextRetry, sizeof(FILETIME));
  221. ZeroMemory(&m_ftNextScheduledCallback, sizeof(FILETIME));
  222. ZeroMemory(&m_ftEmptyExpireTime, sizeof(FILETIME));
  223. AssertPrivateLinkStateFlags();
  224. //normally links are for remote delivery, in special cases like the currently unreachable
  225. //queue they are not. so we need a type field to differentiate between links, so that some
  226. //actions can be performed differently for the special links.
  227. SetLinkType(LI_TYPE_REMOTE_DELIVERY);
  228. //all actions are supported by default, but special links like currently unreachable may
  229. //set this bitmask to specify that certain actions are unsupported. when such an unsupported
  230. //action is commanded, nothing happens.
  231. SetSupportedActions(LA_KICK | LA_FREEZE | LA_THAW);
  232. InitializeListHead(&m_liConnections);
  233. TraceFunctLeave();
  234. }
  235. //---[ CLinkMsgQueue::CLinkMsgQueue ]------------------------------------------
  236. //
  237. //
  238. // Description:
  239. // Class constuctor
  240. // Parameters:
  241. // IN dwScheduleID Schedule ID to associate with link
  242. // IN pIMessageRouter Router for this link
  243. // IN pILinkStateNotify Scheduler Interface for this link
  244. // Returns:
  245. // -
  246. //-----------------------------------------------------------------------------
  247. CLinkMsgQueue::CLinkMsgQueue(DWORD dwScheduleID,
  248. IMessageRouter *pIMessageRouter,
  249. IMessageRouterLinkStateNotification *pILinkStateNotify)
  250. : m_aqsched(pIMessageRouter, dwScheduleID),
  251. m_slQueues("CLinkMsgQueue::m_slQueues"),
  252. m_slConnections("CLinkMsgQueue::m_slConnections"),
  253. m_slInfo("CLinkMsgQueue::m_slInfo")
  254. {
  255. TraceFunctEnterEx((LPARAM) this, "CLinkMsgQueue::CLinkMsgQueue2");
  256. InternalInit();
  257. m_pILinkStateNotify = pILinkStateNotify;
  258. if (m_pILinkStateNotify)
  259. m_pILinkStateNotify->AddRef();
  260. TraceFunctLeave();
  261. }
  262. //---[ CLinkMsgQueue::~CLinkMsgQueue ]------------------------------------------------------------
  263. //
  264. //
  265. // Description:
  266. // Class destructor
  267. // Parameters:
  268. // -
  269. // Returns:
  270. // -
  271. //
  272. //-----------------------------------------------------------------------------
  273. CLinkMsgQueue::~CLinkMsgQueue()
  274. {
  275. // tell routing that this link is going away
  276. DWORD dw = dwModifyLinkState(LINK_STATE_LINK_NO_LONGER_USED, 0);
  277. if (!(dw & LINK_STATE_LINK_NO_LONGER_USED))
  278. SendLinkStateNotification();
  279. if (NULL != m_paqinst)
  280. {
  281. m_paqinst->DecNextHopCount();
  282. m_paqinst->Release();
  283. }
  284. if (NULL != m_pIntDomainInfo)
  285. m_pIntDomainInfo->Release();
  286. if (NULL != m_pdentryLink)
  287. m_pdentryLink->Release();
  288. if (m_szConnectorName)
  289. FreePv(m_szConnectorName);
  290. if (m_szSMTPDomain)
  291. FreePv(m_szSMTPDomain);
  292. if (m_pILinkStateNotify)
  293. m_pILinkStateNotify->Release();
  294. _ASSERT(IsListEmpty(&m_liConnections) && "Leaked connections");
  295. }
  296. //---[ CLinkMsgQueue::HrInitialize ]-----------------------------------------
  297. //
  298. //
  299. // Description: Performs initialization that may return an error code
  300. //
  301. // Parameters:
  302. // IN paqinst Server Instance Object
  303. // IN pdmap Domain Mapping of SMTP Domain this link is for
  304. // IN cbSMTPDomain
  305. // IN szSMTPDomain SMTP Domain that link is being created for
  306. // IN paqsched Schedule ID returned by routing sink
  307. // IN szConnectorName
  308. // Returns: S_OK on success
  309. //
  310. //
  311. //-----------------------------------------------------------------------------
  312. HRESULT CLinkMsgQueue::HrInitialize(CAQSvrInst *paqinst,
  313. CDomainEntry *pdentryLink, DWORD cbSMTPDomain,
  314. LPSTR szSMTPDomain,
  315. LinkFlags lf,
  316. LPSTR szConnectorName)
  317. {
  318. TraceFunctEnterEx((LPARAM) this, "CLinkMsgQueue::HrInitialize");
  319. HRESULT hr = S_OK;
  320. DWORD cbConnectorName = 0;
  321. _ASSERT(szSMTPDomain);
  322. _ASSERT(cbSMTPDomain);
  323. _ASSERT(paqinst);
  324. m_paqinst = paqinst;
  325. if (m_paqinst)
  326. {
  327. m_paqinst->AddRef();
  328. m_paqinst->IncNextHopCount();
  329. }
  330. m_pdentryLink = pdentryLink;
  331. if (m_pdentryLink)
  332. m_pdentryLink->AddRef();
  333. m_szSMTPDomain = (LPSTR) pvMalloc(cbSMTPDomain+sizeof(CHAR));
  334. if (!m_szSMTPDomain)
  335. {
  336. hr = E_OUTOFMEMORY;
  337. ErrorTrace((LPARAM) this, "Error unable to allocate m_szSMTPDomain");
  338. goto Exit;
  339. }
  340. strcpy(m_szSMTPDomain, szSMTPDomain);
  341. m_cbSMTPDomain = cbSMTPDomain;
  342. if (szConnectorName)
  343. {
  344. cbConnectorName = lstrlen(szConnectorName) + sizeof(CHAR);
  345. m_szConnectorName = (LPSTR) pvMalloc(cbConnectorName);
  346. if (!m_szConnectorName)
  347. {
  348. hr = E_OUTOFMEMORY;
  349. ErrorTrace((LPARAM) this, "Error unable to allocate m_szConnectorName");
  350. goto Exit;
  351. }
  352. strcpy(m_szConnectorName, szConnectorName);
  353. }
  354. if (lf == eLinkFlagsInternalSMTPLinkInfo) {
  355. hr = m_paqinst->HrGetDefaultDomainInfo(&m_pIntDomainInfo);
  356. } else if (lf == eLinkFlagsExternalSMTPLinkInfo) {
  357. hr = m_paqinst->HrGetInternalDomainInfo(
  358. cbSMTPDomain,
  359. szSMTPDomain,
  360. &m_pIntDomainInfo);
  361. } else {
  362. // linkInfoType can only be one of these 3 bits. Since we tested for
  363. // the above two, assert that it is the third type.
  364. _ASSERT(lf == eLinkFlagsAQSpecialLinkInfo);
  365. }
  366. m_dwLinkFlags |= lf;
  367. if (m_pIntDomainInfo &&
  368. m_pIntDomainInfo->m_DomainInfo.dwDomainInfoFlags &
  369. (DOMAIN_INFO_TURN_ONLY | DOMAIN_INFO_ETRN_ONLY))
  370. {
  371. //Modify link flags to account for TURN/ETRN
  372. dwInterlockedSetBits(&m_dwLinkStateFlags, LINK_STATE_PRIV_CONFIG_TURN_ETRN);
  373. }
  374. Exit:
  375. //Turn off notifications if we failed
  376. if (FAILED(hr))
  377. dwModifyLinkState(LINK_STATE_LINK_NO_LONGER_USED, LINK_STATE_NO_ACTION);
  378. TraceFunctLeave();
  379. return hr;
  380. }
  381. //---[ CLinkMsgQueue::HrDeinitialize ]-----------------------------------------
  382. //
  383. //
  384. // Description: Release link to m_paqinst object.
  385. //
  386. // Parameters: -
  387. //
  388. // Returns: S_OK on success
  389. //
  390. //
  391. //-----------------------------------------------------------------------------
  392. HRESULT CLinkMsgQueue::HrDeinitialize()
  393. {
  394. TraceFunctEnterEx((LPARAM) this, "CLinkMsgQueue::HrDeinitialize");
  395. HRESULT hr = S_OK;
  396. dwModifyLinkState(LINK_STATE_NO_ACTION,
  397. LINK_STATE_PRIV_IGNORE_DELETE_IF_EMPTY);
  398. RemoveAllQueues();
  399. TraceFunctLeave();
  400. return hr;
  401. }
  402. //---[ CLinkMsgQueue::RemovedFromDMT ]-----------------------------------------
  403. //
  404. //
  405. // Description: Notification to the link that the DMT is removing it
  406. //
  407. // Parameters: -
  408. //
  409. // Returns: -
  410. //
  411. //-----------------------------------------------------------------------------
  412. void CLinkMsgQueue::RemovedFromDMT()
  413. {
  414. TraceFunctEnter("CLinkMsgQueue::RemovedFromDMT");
  415. // tell routing that this link is going away
  416. DWORD dw = dwModifyLinkState(LINK_STATE_LINK_NO_LONGER_USED, 0);
  417. if (!(dw & LINK_STATE_LINK_NO_LONGER_USED))
  418. SendLinkStateNotification();
  419. TraceFunctLeave();
  420. }
  421. //---[ CLinkMsgQueue::AddConnection ]----------------------------------------
  422. //
  423. //
  424. // Description:
  425. // Add a connection instance to this link
  426. // Parameters:
  427. // IN pSMTPConn Connection to add to link
  428. // Returns:
  429. // -
  430. //
  431. //-----------------------------------------------------------------------------
  432. void CLinkMsgQueue::AddConnection(CSMTPConn *pSMTPConn)
  433. {
  434. TraceFunctEnterEx((LPARAM) this, "CLinkMsgQueue::HrAddConnection");
  435. _ASSERT(pSMTPConn);
  436. _ASSERT(!(m_dwLinkStateFlags & LINK_STATE_PRIV_NO_CONNECTION));
  437. InterlockedIncrement((PLONG) &m_cConnections);
  438. m_slConnections.ExclusiveLock();
  439. pSMTPConn->InsertConnectionInList(&m_liConnections);
  440. m_slConnections.ExclusiveUnlock();
  441. DebugTrace((LPARAM) this, "Adding connection #%d to link", m_cConnections);
  442. TraceFunctLeave();
  443. }
  444. //---[ CLinkMsgQueue::RemoveConnection ]---------------------------------------
  445. //
  446. //
  447. // Description:
  448. // Remove a connection from the link
  449. // Parameters:
  450. // IN pSMTPConn Connection to remove from link
  451. // IN fForceDSNGeneration Force DSN generation
  452. // Returns:
  453. // - Always succeeds
  454. //-----------------------------------------------------------------------------
  455. void CLinkMsgQueue::RemoveConnection(IN CSMTPConn *pSMTPConn,
  456. IN BOOL fForceDSNGeneration)
  457. {
  458. TraceFunctEnterEx((LPARAM) this, "CLinkMsgQueue::RemoveConnection");
  459. BOOL fNoConnections = FALSE;
  460. BOOL fMergeOnly = fFlagsAllowConnection(m_dwLinkStateFlags) &&
  461. !fForceDSNGeneration;
  462. _ASSERT(pSMTPConn);
  463. _ASSERT(!(m_dwLinkStateFlags & LINK_STATE_PRIV_NO_CONNECTION));
  464. if (!m_paqinst)
  465. return;
  466. m_paqinst->RoutingShareLock();
  467. m_slConnections.ExclusiveLock();
  468. pSMTPConn->RemoveConnectionFromList();
  469. InterlockedDecrement((PLONG) &m_cConnections);
  470. fNoConnections = IsListEmpty(&m_liConnections);
  471. m_slConnections.ExclusiveUnlock();
  472. //Only generate DSNs if we have been kicked into retry
  473. if (fNoConnections)
  474. {
  475. //Generate DSNs if we cannot connect connect
  476. GenerateDSNsIfNecessary(TRUE, fMergeOnly);
  477. dwInterlockedUnsetBits(&m_dwLinkFlags, eLinkFlagsConnectionVerifed);
  478. }
  479. m_paqinst->RoutingShareUnlock();
  480. DebugTrace((LPARAM) this, "Removing connection #%d from link", m_cConnections);
  481. TraceFunctLeave();
  482. }
  483. //---[ CLinkMsgQueue::GenerateDSNsIfNecessary ]--------------------------------
  484. //
  485. //
  486. // Description:
  487. // Walks queues and generates DSNs if necessary.
  488. // Parameters:
  489. // BOOL fCheckIfEmpty - check queues even if we think we are empty
  490. // This is an optimization that should be used
  491. // when we know there are no messages in the
  492. // retry queues (like the Unreachable or
  493. // CurrentlyUnreachable link)
  494. // BOOL fMergeOnly - Only merge retry queues, do not walk for DSNs
  495. // This improves perf for the cases were we have not
  496. // had a connection error and really don't need to
  497. // walk the queues.
  498. // Returns:
  499. // -
  500. // History:
  501. // 1/27/99 - MikeSwa Created (pulled from RemoveConnection)
  502. // 2/2/99 - MikeSwa Added fMergeOnly flag
  503. // 11/10/1999 - MikeSwa Updated to be more release locks after generating
  504. // a max number of DSNs.
  505. //
  506. //-----------------------------------------------------------------------------
  507. void CLinkMsgQueue::GenerateDSNsIfNecessary(BOOL fCheckIfEmpty, BOOL fMergeOnly)
  508. {
  509. TraceFunctEnterEx((LPARAM) this, "CLinkMsgQueue::GenerateDSNsIfNecessary");
  510. DWORD iQueues = 0;
  511. CDestMsgQueue *pdmq = NULL;
  512. PVOID pvContext = NULL;
  513. HRESULT hrDSN = m_hrLastConnectionFailure;
  514. HRESULT hr = S_OK;
  515. BOOL fRestartLater = FALSE;
  516. DWORD dwDSNContext = 0;
  517. //If this link is configured as a TURN/ETRN link, we do not want to
  518. //immediately NDR the domain because if subject us to DOS attacks.
  519. //We only want to generate expire DSNs
  520. if (LINK_STATE_PRIV_CONFIG_TURN_ETRN & m_dwLinkStateFlags)
  521. hrDSN = AQUEUE_E_HOST_NOT_RESPONDING;
  522. //
  523. // For servers within an orginization, there is an expectation that
  524. // that authoritative DNS failures are network issues that need to be
  525. // resolved by email administrators (as opposed to user errors in
  526. // typing the email address).
  527. //
  528. // This link state flag allows the router to control behavior in the
  529. // face of authoritative DNS failures.
  530. //
  531. // If the next hop is something derived from a user address (like
  532. // direct DNS routing to external machines or a direct-DNS connector),
  533. // then a router can use this flag to tell queuing to treat
  534. // authoritative DNS failures as retryable.
  535. //
  536. // The default behavior is to treat authoritative DNS failures as
  537. // fatal errors (and NDR the messages).
  538. //
  539. if ((AQUEUE_E_SMTP_GENERIC_ERROR == hrDSN) &&
  540. (LINK_STATE_RETRY_ALL_DNS_FAILURES & m_dwLinkStateFlags))
  541. {
  542. hrDSN = AQUEUE_E_HOST_NOT_RESPONDING;
  543. ErrorTrace((LPARAM) this,
  544. "hard failure (DNS) made retryable for %s (flags 0x%08X)",
  545. m_szSMTPDomain, m_dwLinkStateFlags);
  546. }
  547. // m_cMsgs doesn't include # of msgs in retry queue.
  548. if (!fCheckIfEmpty && !m_aqstats.m_cMsgs && !m_aqstats.m_cRetryMsgs)
  549. return;
  550. if (!(LINK_STATE_PRIV_GENERATING_DSNS &
  551. dwInterlockedSetBits(&m_dwLinkStateFlags, LINK_STATE_PRIV_GENERATING_DSNS)))
  552. {
  553. if (m_paqinst && m_paqinst->fTryShutdownLock())
  554. {
  555. //Don't attempt to requeue if we are shutting down
  556. m_slQueues.ShareLock();
  557. pdmq = (CDestMsgQueue *) m_qlstQueues.pvGetItem(iQueues, &pvContext);
  558. while (pdmq)
  559. {
  560. pdmq->AssertSignature();
  561. if (fMergeOnly)
  562. pdmq->MergeRetryQueue();
  563. else
  564. hr = pdmq->HrGenerateDSNsIfNecessary(&m_qlstQueues, hrDSN, &dwDSNContext);
  565. if (FAILED(hr) && (HRESULT_FROM_WIN32(E_PENDING) == hr))
  566. {
  567. fRestartLater = TRUE;
  568. break;
  569. }
  570. iQueues++;
  571. pdmq = (CDestMsgQueue *) m_qlstQueues.pvGetItem(iQueues, &pvContext);
  572. _ASSERT(iQueues <= m_cQueues);
  573. }
  574. m_slQueues.ShareUnlock();
  575. m_paqinst->ShutdownUnlock();
  576. }
  577. if (fRestartLater)
  578. {
  579. //We have hit our limit on the number of messages to process
  580. //at one time. Schedule a callback to process more later
  581. DebugTrace((LPARAM) this,
  582. "Will continue DSN generation at a later time - 0x%X", hr);
  583. AddRef(); //Completion function will release on failure
  584. m_paqinst->HrQueueWorkItem(this,
  585. CLinkMsgQueue::fRestartDSNGenerationIfNecessary);
  586. }
  587. dwInterlockedUnsetBits(&m_dwLinkStateFlags, LINK_STATE_PRIV_GENERATING_DSNS);
  588. }
  589. TraceFunctLeave();
  590. }
  591. //---[ CLinkMsgQueue::HrGetDomainInfo ]----------------------------------------
  592. //
  593. //
  594. // Description:
  595. // Returns domain info for SMTP connection.
  596. // Parameters:
  597. // OUT pcbSMTPDomain String length of domain name
  598. // OUT pszSMTPDomain String containing domain info (memory managed byt DMT)
  599. // OUT ppIntDomainInfo Internal Domain Info for link's next hop
  600. // Returns:
  601. // S_OK on success
  602. // AQUEUE_E_LINK_INVALID if link is no longer valud
  603. //
  604. //-----------------------------------------------------------------------------
  605. HRESULT CLinkMsgQueue::HrGetDomainInfo(OUT DWORD *pcbSMTPDomain,
  606. OUT LPSTR *pszSMTPDomain,
  607. OUT CInternalDomainInfo **ppIntDomainInfo)
  608. {
  609. HRESULT hr = S_OK;
  610. _ASSERT(pcbSMTPDomain);
  611. _ASSERT(pszSMTPDomain);
  612. _ASSERT(ppIntDomainInfo);
  613. hr = HrGetInternalInfo(ppIntDomainInfo);
  614. if (FAILED(hr))
  615. {
  616. goto Exit;
  617. }
  618. else if (!*ppIntDomainInfo)
  619. {
  620. //If HrGetInternalInfoFails the first time, it will return NULL
  621. //subsequent times. Make sure we do not return success and a
  622. //NULL pointer. When this happens, the link will go into retry
  623. hr = E_FAIL;
  624. goto Exit;
  625. }
  626. *pcbSMTPDomain = m_cbSMTPDomain;
  627. *pszSMTPDomain = m_szSMTPDomain;
  628. Exit:
  629. return hr;
  630. }
  631. //---[ CLinkMsgQueue::HrGetSMTPDomain ]----------------------------------------
  632. //
  633. //
  634. // Description:
  635. // Returns the SMTP Domain for this link.
  636. // Parameters:
  637. // OUT pcbSMTPDomain String length of returned domain
  638. // OUT pszSMTPDomain Returned SMTP Domain string. The Link will manager
  639. // The memory for this, and will remain valid as long
  640. // as the link is in existance.
  641. // Returns:
  642. // S_OK on success
  643. //
  644. //-----------------------------------------------------------------------------
  645. HRESULT CLinkMsgQueue::HrGetSMTPDomain(OUT DWORD *pcbSMTPDomain,
  646. OUT LPSTR *pszSMTPDomain)
  647. {
  648. TraceFunctEnterEx((LPARAM) this, "CLinkMsgQueue::HrGetSMTPDomain");
  649. HRESULT hr = S_OK;
  650. _ASSERT(pcbSMTPDomain);
  651. _ASSERT(pszSMTPDomain);
  652. if (m_dwLinkFlags & eLinkFlagsInvalid)
  653. {
  654. hr = AQUEUE_E_LINK_INVALID;
  655. goto Exit;
  656. }
  657. *pcbSMTPDomain = m_cbSMTPDomain;
  658. *pszSMTPDomain = m_szSMTPDomain;
  659. if (NULL == m_szSMTPDomain)
  660. {
  661. hr = AQUEUE_E_LINK_INVALID;
  662. }
  663. Exit:
  664. TraceFunctLeave();
  665. return hr;
  666. }
  667. //---[ CLinkMsgQueue::HrAddQueue ]---------------------------------------------
  668. //
  669. //
  670. // Description:
  671. // Add DestMsgQueues to the link.
  672. // Parameters:
  673. //
  674. // Returns:
  675. // S_OK on success
  676. // E_OUTOFMEMORY if unable to allocate space to store queue
  677. // AQUEUE_E_LINK_INVALID Failed to add queue to link (link is invalid)
  678. //
  679. //-----------------------------------------------------------------------------
  680. HRESULT CLinkMsgQueue::HrAddQueue(IN CDestMsgQueue *pdmqNew)
  681. {
  682. TraceFunctEnterEx((LPARAM) this, "CLinkMsgQueue::HrAddQueue");
  683. HRESULT hr = S_OK;
  684. DWORD dwIndex = 0;
  685. CDestMsgQueue *pdmqOld = NULL;
  686. PVOID pvContext = NULL;
  687. m_slQueues.ExclusiveLock();
  688. // Verify that this link is still in use (there exists a window
  689. // where we have decided to use this link but it still gets removed)
  690. if (LINK_STATE_LINK_NO_LONGER_USED & m_dwLinkStateFlags)
  691. {
  692. // Keep track of how often this failure occurs because it results
  693. // in a reset routes every time ...
  694. InterlockedIncrement((PLONG) &g_cFailedToAddQueueToRemovedLink);
  695. ErrorTrace((LPARAM) this, "Failed to add queue to removed link - this has occured %d times since startup", g_cFailedToAddQueueToRemovedLink);
  696. // Fail here and let the caller try putting the queue elsewhere
  697. hr = AQUEUE_E_LINK_INVALID;
  698. goto Exit;
  699. }
  700. //
  701. // Clear the marked as empty bit (if set)
  702. //
  703. dwInterlockedUnsetBits(&m_dwLinkFlags, eLinkFlagsMarkedAsEmpty);
  704. _ASSERT(pdmqNew);
  705. #ifdef DEBUG
  706. // We have seen cases where it looks like a DMQ has been added to a link
  707. // multiple times (via this call). We need to make assert that this
  708. // is not the case here.
  709. for (dwIndex = 0; dwIndex < m_cQueues; dwIndex++)
  710. {
  711. pdmqOld = (CDestMsgQueue *) m_qlstQueues.pvGetItem(dwIndex, &pvContext);
  712. //If these match, it means that someone is adding this queue twice...
  713. if (pdmqOld == pdmqNew)
  714. {
  715. _ASSERT(0 && "Adding queue twice to link... get mikeswa");
  716. }
  717. }
  718. #endif //DEBUG
  719. dwIndex = 0;
  720. pdmqOld = NULL;
  721. pvContext = NULL;
  722. pdmqNew->AddRef();
  723. hr = m_qlstQueues.HrAppendItem(pdmqNew, &dwIndex);
  724. if (FAILED(hr))
  725. goto Exit;
  726. //Set DMQ's link context to index inserted in quick list
  727. pdmqNew->SetLinkContext(ULongToPtr(dwIndex));
  728. m_cQueues++;
  729. Exit:
  730. m_slQueues.ExclusiveUnlock();
  731. //Now that the first queue has been added, this can be deleted if empty
  732. dwModifyLinkState(LINK_STATE_NO_ACTION,
  733. LINK_STATE_PRIV_IGNORE_DELETE_IF_EMPTY);
  734. TraceFunctLeave();
  735. return hr;
  736. }
  737. //---[ CLinkMsgQueue::RemoveQueue ]--------------------------------------------
  738. //
  739. //
  740. // Description:
  741. // Removes a given queue from the link. Queue *must* be associated with
  742. // link (this will be asserted).
  743. // Parameters:
  744. // IN pdmq DMQ to remove from link
  745. // IN paqstats Stats associated with DMQ
  746. // Returns:
  747. // -
  748. // History:
  749. // 9/14/98 - MikeSwa Created
  750. // 5/14/99 - MikeSwa Removed code to automatically remove link
  751. // from DMT if there are no queues. This is now done in
  752. // CLinkMsgQueue::RemoveLinkIfEmpty
  753. // 8/10/99 - MikeSwa added check of pdmqOther. While operartions on
  754. // the quick list are thread safe, there is nothing procting us
  755. // from another thread calling RemoveQueue or RemoveAllQueues
  756. // before we get the lock. If this is the case, then we have
  757. // the change to double-decrement m_cQueues, which could lead to
  758. // an AV in GetNextMessage
  759. //
  760. //-----------------------------------------------------------------------------
  761. void CLinkMsgQueue::RemoveQueue(IN CDestMsgQueue *pdmq, IN CAQStats *paqstats)
  762. {
  763. TraceFunctEnterEx((LPARAM) this, "RemoveQueue");
  764. _ASSERT(pdmq);
  765. CDestMsgQueue *pdmqOther = NULL;
  766. CDestMsgQueue *pdmqCheck = NULL;
  767. PVOID pvContext = NULL;
  768. DWORD dwIndex = 0;
  769. BOOL fFoundQueue = FALSE;
  770. //Aquire exclusive lock and remove DMQ from list
  771. m_slQueues.ExclusiveLock();
  772. //While the follow line *may* produce a sundown warning is is 100% correct
  773. //The context is created and "owned" by this object. Currently it is an
  774. //array index, but eventually it may be a pointer to more interesting context
  775. //structure.
  776. dwIndex = (DWORD) (DWORD_PTR)pdmq->pvGetLinkContext();
  777. pdmqOther = (CDestMsgQueue *) m_qlstQueues.pvGetItem(dwIndex, &pvContext);
  778. if (pdmqOther && (pdmqOther == pdmq))
  779. {
  780. fFoundQueue = TRUE;
  781. //Now that we found it... remove it from the link
  782. pdmqCheck = (CDestMsgQueue *) m_qlstQueues.pvDeleteItem(dwIndex, &pvContext);
  783. m_cQueues--;
  784. //The link context should be the index of the DMQ
  785. _ASSERT(pdmqCheck == pdmqOther);
  786. //Get new item at old index & update context
  787. pdmqOther = (CDestMsgQueue *) m_qlstQueues.pvGetItem(dwIndex, &pvContext);
  788. //If pdmqOther is NULL, then we have no more queues
  789. //(or it was the last in the list)
  790. _ASSERT(pdmqOther || !m_cQueues || (dwIndex == m_cQueues));
  791. //Update change in stats
  792. m_aqstats.UpdateStats(paqstats, FALSE);
  793. if (m_cQueues)
  794. {
  795. if (pdmqOther)
  796. pdmqOther->SetLinkContext(ULongToPtr(dwIndex));
  797. }
  798. }
  799. else
  800. {
  801. //While not technically an error, this means that another thread has removed
  802. //this (or all) queues, and it is not in the link
  803. ErrorTrace((LPARAM) this,
  804. "Found Queue 0x%0X instead of 0x%08X at index %d",
  805. (DWORD_PTR) pdmqOther, (DWORD_PTR) pdmq, dwIndex);
  806. }
  807. m_slQueues.ExclusiveUnlock();
  808. //Release reference to DMQ
  809. if (fFoundQueue)
  810. pdmq->Release();
  811. TraceFunctLeave();
  812. }
  813. //---[ CLinkMsgQueue::HrGetQueueListSnapshot ]---------------------------------
  814. //
  815. //
  816. // Description:
  817. // Gets a snapshot of the queue list - the caller is responsible for
  818. // deleting this list
  819. // Parameters:
  820. // IN / OUT : ppql - pp to place the new CQuickList in
  821. // Returns:
  822. // S_OK on success
  823. // History:
  824. // 11/9/2000 - dbraun - created
  825. //
  826. //-----------------------------------------------------------------------------
  827. HRESULT CLinkMsgQueue::HrGetQueueListSnapshot(CQuickList **ppql)
  828. {
  829. HRESULT hr = S_OK;
  830. // Lock the queue list
  831. m_slQueues.ShareLock();
  832. // Get a clone of the queue list
  833. hr = m_qlstQueues.Clone(ppql);
  834. // Unlock the queue list
  835. m_slQueues.ShareUnlock();
  836. return hr;
  837. }
  838. //---[ CLinkMsgQueue::RemoveLinkIfEmpty ]--------------------------------------
  839. //
  840. //
  841. // Description:
  842. // Removes a link from the DomainEntry if it is empty. This behavior
  843. // used to be part of RemoveQueue, but was removed because it could
  844. // lead to a link being removed from the DMT hash table, but still
  845. // creating connections.
  846. // Parameters:
  847. // -
  848. // Returns:
  849. // -
  850. // History:
  851. // 5/14/99 - MikeSwa Created (as potential Windows2000 Beta3 QFE fix)
  852. //
  853. //-----------------------------------------------------------------------------
  854. void CLinkMsgQueue::RemoveLinkIfEmpty()
  855. {
  856. TraceFunctEnterEx((LPARAM) this, "CLinkMsgQueue::RemoveLinkIfEmpty");
  857. DWORD dwLinkFlags = 0;
  858. DWORD dwRoutingInterestedFlags = LINK_STATE_CONNECT_IF_NO_MSGS |
  859. LINK_STATE_DO_NOT_DELETE |
  860. LINK_STATE_PRIV_IGNORE_DELETE_IF_EMPTY |
  861. LINK_STATE_ADMIN_HALT |
  862. LINK_STATE_DO_NOT_DELETE_UNTIL_NEXT_NOTIFY;
  863. //Bail early if we know we don't need to grab the lock
  864. if (m_cQueues || !m_pdentryLink)
  865. return;
  866. if (m_slQueues.TryExclusiveLock())
  867. {
  868. if (!m_cQueues &&
  869. !(dwRoutingInterestedFlags & m_dwLinkStateFlags))
  870. {
  871. //It might be prudent to to delete this link if:
  872. // - There are no messages
  873. // - Routing has not shown a interest in this queue
  874. // - This link has also expired
  875. //
  876. // Mark link as empty
  877. //
  878. dwLinkFlags = dwInterlockedSetBits(&m_dwLinkFlags, eLinkFlagsMarkedAsEmpty);
  879. //
  880. // If we set the flag, then set the expire timer. Otherwise remove
  881. // the link.
  882. //
  883. if (!(eLinkFlagsMarkedAsEmpty & dwLinkFlags))
  884. {
  885. m_paqinst->GetExpireTime(EMPTY_LMQ_EXPIRE_TIME_MINUTES,
  886. &m_ftEmptyExpireTime, NULL);
  887. }
  888. else if (m_paqinst->fInPast(&m_ftEmptyExpireTime, NULL))
  889. {
  890. if (m_pdentryLink)
  891. {
  892. DebugTrace((LPARAM) this,
  893. "Removing empty link %s with flags 0x%08X",
  894. (m_szSMTPDomain ? m_szSMTPDomain : "(NULL)"),
  895. m_dwLinkStateFlags);
  896. // INSTRUMENTATION : Sleep to slow link removal
  897. if (g_fEnableTestSettings && g_cDelayLinkRemovalSeconds)
  898. {
  899. // Delay the deletion to open up the window for
  900. // adding a queue to this about-to-be removed link
  901. StateTrace((LPARAM) this,
  902. "Link expiring - delaying %d seconds", g_cDelayLinkRemovalSeconds);
  903. Sleep (g_cDelayLinkRemovalSeconds * 1000);
  904. StateTrace((LPARAM) this,
  905. "Link expiring - proceeding with delete");
  906. }
  907. // END INSTRUMENTATION
  908. m_pdentryLink->RemoveLinkMsgQueue(this);
  909. m_pdentryLink->Release();
  910. m_pdentryLink = NULL;
  911. }
  912. //We need to artificially increase the connection manager count
  913. //so it will not be enqueue'd in the connmgr again
  914. IncrementConnMgrCount();
  915. }
  916. }
  917. m_slQueues.ExclusiveUnlock();
  918. }
  919. TraceFunctLeave();
  920. }
  921. //---[ CLinkMsgQueue::HrGetNextMsg ]-------------------------------------------
  922. //
  923. //
  924. // Description:
  925. // Gets the next message from the queue
  926. // Parameters:
  927. // IN OUT CDeliveryContext *pdcntxt - delivery context for connection
  928. // OUT IMailMsgProperties **ppIMailMsgProperties - IMsg dequeued
  929. // OUT DWORD *pcIndexes - size of array
  930. // OUT DWORD **prgdwRecipIndex - Array of recipient indexes
  931. // Returns:
  932. // S_OK on success
  933. // E_INVALIDARG if invalid parameters are given
  934. //
  935. // History:
  936. // 6/17/98 - MikeSwa Modified to use connection's delivery context
  937. //
  938. //-----------------------------------------------------------------------------
  939. HRESULT CLinkMsgQueue::HrGetNextMsg(IN OUT CDeliveryContext *pdcntxt,
  940. OUT IMailMsgProperties **ppIMailMsgProperties,
  941. OUT DWORD *pcIndexes, OUT DWORD **prgdwRecipIndex)
  942. {
  943. TraceFunctEnterEx((LPARAM) this, "CLinkMsgQueue::HrGetNextMsg");
  944. Assert(ppIMailMsgProperties);
  945. Assert(pdcntxt);
  946. Assert(prgdwRecipIndex);
  947. Assert(pcIndexes);
  948. HRESULT hr = S_OK;
  949. CMsgRef *pmsgref = NULL;
  950. CMsgBitMap *pmbmap = NULL;
  951. DWORD cDomains = 0;
  952. BOOL fLockedShutdown = FALSE;
  953. BOOL fLockedQueues = FALSE;
  954. DWORD iQueues = 0;
  955. DWORD dwCurrentRoundRobinIndex = m_dwRoundRobinIndex;
  956. CDestMsgQueue *pdmq = NULL;
  957. CDestMsgRetryQueue *pdmrq = NULL;
  958. PVOID pvContext = NULL;
  959. BOOL fDoneWithQueue = FALSE;
  960. DWORD dwCurrentPriority = eEffPriHigh;
  961. if (m_dwLinkFlags & eLinkFlagsInvalid)
  962. {
  963. hr = AQUEUE_E_LINK_INVALID;
  964. goto Exit;
  965. }
  966. //Don't even bother to wait for queue lock if routing change is pending
  967. if (m_dwLinkFlags & eLinkFlagsRouteChangePending)
  968. {
  969. hr = AQUEUE_E_QUEUE_EMPTY;
  970. goto Exit;
  971. }
  972. //Make sure domain info is updated & we should still be sending messages
  973. //If we can't schedule... we still might be allowed to send messages because
  974. //of TURN.
  975. if (!fCanSchedule() && !(m_dwLinkStateFlags & LINK_STATE_PRIV_TURN_ENABLED))
  976. {
  977. hr = AQUEUE_E_QUEUE_EMPTY;
  978. goto Exit;
  979. }
  980. if (!m_paqinst->fTryShutdownLock())
  981. {
  982. hr = AQUEUE_E_SHUTDOWN;
  983. goto Exit;
  984. }
  985. m_paqinst->RoutingShareLock();
  986. fLockedShutdown = TRUE;
  987. m_slQueues.ShareLock();
  988. fLockedQueues = TRUE;
  989. if (m_cQueues == 0)
  990. {
  991. //There are currently no queue associated with this link
  992. hr = AQUEUE_E_QUEUE_EMPTY;
  993. goto Exit;
  994. }
  995. //
  996. // A priority ordering is imposed on these queues by first requesting
  997. // only messages of a certain priority and then requesting lower priorities
  998. //
  999. do
  1000. {
  1001. //
  1002. // Sanity check our current priority
  1003. //
  1004. _ASSERT(dwCurrentPriority < NUM_PRIORITIES);
  1005. for (iQueues = 0; iQueues < m_cQueues && SUCCEEDED(hr) && !pmsgref; iQueues++)
  1006. {
  1007. pmsgref = NULL;
  1008. pdmq = (CDestMsgQueue *) m_qlstQueues.pvGetItem(
  1009. (iQueues+dwCurrentRoundRobinIndex)%m_cQueues, &pvContext);
  1010. _ASSERT(pdmq);
  1011. pdmq->AssertSignature();
  1012. //Loop until the queue is empty or we get a message that hasn't
  1013. //already been delivered by another queue on this link
  1014. do
  1015. {
  1016. //Usually, we only want to attempt to dequeue from a queue once.
  1017. fDoneWithQueue = TRUE;
  1018. //Release retry interface if we have one
  1019. if (pdmrq)
  1020. {
  1021. pdmrq->Release();
  1022. pdmrq = NULL;
  1023. }
  1024. //get msg reference
  1025. hr = pdmq->HrDequeueMsg(dwCurrentPriority, &pmsgref, &pdmrq);
  1026. if (FAILED(hr))
  1027. {
  1028. if (AQUEUE_E_QUEUE_EMPTY == hr)
  1029. {
  1030. hr = S_OK;
  1031. continue; //get message from next queue
  1032. }
  1033. else
  1034. {
  1035. goto Exit;
  1036. }
  1037. }
  1038. //prepare for delivery and generate delivery context
  1039. hr = pmsgref->HrPrepareDelivery(FALSE /*remote only */,
  1040. FALSE /*not a Delay DSN */,
  1041. &m_qlstQueues, pdmrq,
  1042. pdcntxt, pcIndexes, prgdwRecipIndex);
  1043. if (AQUEUE_E_MESSAGE_HANDLED == hr)
  1044. {
  1045. //the message has already been handled for this queue
  1046. pmsgref->Release();
  1047. pmsgref = NULL;
  1048. hr = S_OK;
  1049. //We want to stay on this queue until it is empty
  1050. fDoneWithQueue = FALSE;
  1051. }
  1052. else if ((AQUEUE_E_MESSAGE_PENDING == hr) ||
  1053. ((FAILED(hr)) && pmsgref->fShouldRetry()))
  1054. {
  1055. //AQUEUE_E_MESSAGE_PENDING means that the message
  1056. //is currently pending delivery for another connection
  1057. //on this link. We will requeue it, and remove only after it
  1058. //has been completly delivered for this link
  1059. hr = pdmrq->HrRetryMsg(pmsgref);
  1060. if (FAILED(hr))
  1061. pmsgref->RetryOnDelete();
  1062. pmsgref->Release();
  1063. pmsgref = NULL;
  1064. hr = S_OK;
  1065. //We want to stay on this queue until it is empty
  1066. fDoneWithQueue = FALSE;
  1067. }
  1068. else if (FAILED(hr))
  1069. {
  1070. //The message has been deleted out from underneath us
  1071. pmsgref->Release();
  1072. pmsgref = NULL;
  1073. hr = S_OK;
  1074. //We want to stay on this queue until it is empty
  1075. fDoneWithQueue = FALSE;
  1076. }
  1077. } while (!fDoneWithQueue);
  1078. }
  1079. //
  1080. // On to the next priority
  1081. //
  1082. if (dwCurrentPriority == eEffPriLow)
  1083. break;
  1084. dwCurrentPriority --;
  1085. } while (dwCurrentPriority < NUM_PRIORITIES);
  1086. //Visit a new queue on every GetNextMsg
  1087. InterlockedIncrement((PLONG) &m_dwRoundRobinIndex);
  1088. if (pmsgref && SUCCEEDED(hr)) //we got a message
  1089. {
  1090. *ppIMailMsgProperties = pmsgref->pimsgGetIMsg();
  1091. pmsgref = NULL;
  1092. }
  1093. else //We have failed or do not have a message
  1094. {
  1095. *ppIMailMsgProperties = NULL;
  1096. if (SUCCEEDED(hr)) //don't overwrite other error
  1097. hr = AQUEUE_E_QUEUE_EMPTY;
  1098. else
  1099. ErrorTrace((LPARAM) this, "GetNextMsg returning hr - 0x%08X", hr);
  1100. }
  1101. Exit:
  1102. if (pdmrq)
  1103. pdmrq->Release();
  1104. if (NULL != pmsgref)
  1105. pmsgref->Release();
  1106. if (fLockedQueues)
  1107. m_slQueues.ShareUnlock();
  1108. if (fLockedShutdown)
  1109. {
  1110. m_paqinst->RoutingShareUnlock();
  1111. m_paqinst->ShutdownUnlock();
  1112. }
  1113. TraceFunctLeave();
  1114. return hr;
  1115. }
  1116. //---[ CLinkMsgQueue::HrAckMsg ]-------------------------------------------------
  1117. //
  1118. //
  1119. // Description:
  1120. // Acknowledges the delivery of a message (success/error codes are put in
  1121. // the envelope by the transport).
  1122. //
  1123. // Parameters:
  1124. // IN pIMsg IMsg to acknowledge
  1125. // IN dwMsgContext Context that was returned by GetNextMessage
  1126. // IN eMsgStatus Summary of Delivery status of message
  1127. // IN dwStatusCode Status code returned by protocol
  1128. // IN cbExtendedStatus Size of extended status buffer
  1129. // IN szExtendedStatus String containing extended status returned by
  1130. // remote server
  1131. // Returns:
  1132. // S_OK on success
  1133. // E_INVALIDARG if dwMsgContext is invalid
  1134. //
  1135. //-----------------------------------------------------------------------------
  1136. HRESULT CLinkMsgQueue::HrAckMsg(MessageAck *pMsgAck)
  1137. {
  1138. HRESULT hr = S_OK;
  1139. CInternalDomainInfo *pIntDomainInfo = NULL;
  1140. _ASSERT(m_paqinst);
  1141. if (NULL == pMsgAck->pvMsgContext)
  1142. {
  1143. hr = E_INVALIDARG;
  1144. goto Exit;
  1145. }
  1146. if (MESSAGE_STATUS_ALL_DELIVERED & pMsgAck->dwMsgStatus)
  1147. {
  1148. m_lConsecutiveMessageFailureCount = 0;
  1149. if (!(m_dwLinkFlags & eLinkFlagsConnectionVerifed))
  1150. dwInterlockedSetBits(&m_dwLinkFlags, eLinkFlagsConnectionVerifed);
  1151. if (LINK_STATE_PRIV_CONFIG_TURN_ETRN & m_dwLinkStateFlags)
  1152. {
  1153. //We delivered successfully as TURN/ETRN... we need to update count
  1154. m_paqinst->IncTURNETRNDelivered();
  1155. }
  1156. }
  1157. hr = HrGetInternalInfo(&pIntDomainInfo);
  1158. if (SUCCEEDED(hr) && pIntDomainInfo)
  1159. {
  1160. if (DOMAIN_INFO_LOCAL_DROP &
  1161. pIntDomainInfo->m_DomainInfo.dwDomainInfoFlags)
  1162. {
  1163. pMsgAck->dwMsgStatus |= MESSAGE_STATUS_DROP_DIRECTORY;
  1164. }
  1165. pIntDomainInfo->Release();
  1166. }
  1167. hr = m_paqinst->HrAckMsg(pMsgAck);
  1168. Exit:
  1169. return hr;
  1170. }
  1171. //---[ CLinkMsgQueue::HrNotify ]----------------------------------------------
  1172. //
  1173. //
  1174. // Description:
  1175. // Recieve notification from one of our DestMsgQueues.
  1176. // Parameters:
  1177. // IN paqstats Notification object sent
  1178. // Returns:
  1179. // S_OK on success
  1180. //
  1181. //-----------------------------------------------------------------------------
  1182. HRESULT CLinkMsgQueue::HrNotify(IN CAQStats *paqstats, BOOL fAdd)
  1183. {
  1184. HRESULT hr = S_OK;
  1185. DWORD dwTmp = 0;
  1186. DWORD dwNotifyType = 0;
  1187. BOOL fCheckIfNotifyShouldContinue = FALSE;
  1188. _ASSERT(paqstats);
  1189. //Update our own version of stats
  1190. m_aqstats.UpdateStats(paqstats, fAdd);
  1191. //Don't notify if we're configured not to
  1192. if (LINK_STATE_PRIV_NO_NOTIFY & m_dwLinkStateFlags)
  1193. return hr;
  1194. //See if new message update
  1195. if (paqstats->m_dwNotifyType & NotifyTypeDestMsgQueue)
  1196. {
  1197. //$$NOTE:
  1198. //At some point it may be interesting to use the information passed by the
  1199. //DMQ to adjust it's place in the queue (ie. priority). Currently, we
  1200. //don't care.
  1201. fCheckIfNotifyShouldContinue = TRUE;
  1202. }
  1203. //
  1204. // If this is a reroute... this may be a new link (with no messages), we
  1205. // should make sure we add it to the connection manager.
  1206. //
  1207. if (paqstats->m_dwNotifyType & NotifyTypeReroute)
  1208. fCheckIfNotifyShouldContinue = TRUE;
  1209. if (fCheckIfNotifyShouldContinue && fAdd)
  1210. {
  1211. //Wait until we have messages or are rerouting before sending a
  1212. //notification that might add this link to the connection manager
  1213. if ((m_aqstats.m_cMsgs ||
  1214. (paqstats->m_dwNotifyType & NotifyTypeReroute)) &&
  1215. !(eLinkFlagsSentNewNotification & m_dwLinkFlags))
  1216. {
  1217. //Attempt to set first notification flag
  1218. dwTmp = m_dwLinkFlags; //if already set before while, make sure IF fails
  1219. while (!(eLinkFlagsSentNewNotification & m_dwLinkFlags))
  1220. {
  1221. dwTmp = m_dwLinkFlags;
  1222. dwTmp = InterlockedCompareExchange((PLONG) &m_dwLinkFlags,
  1223. (LONG) (dwTmp | eLinkFlagsSentNewNotification),
  1224. (LONG) dwTmp);
  1225. }
  1226. if (!(dwTmp & eLinkFlagsSentNewNotification)) //this thread set it
  1227. {
  1228. // Set the type to notify new link so it will be added to
  1229. // the connection manager
  1230. dwNotifyType |= NotifyTypeNewLink;
  1231. }
  1232. }
  1233. }
  1234. if (fAdd) //only send notifcation on when we are adding a new message
  1235. {
  1236. //If we are adding messages... this should not be set
  1237. _ASSERT(!(LINK_STATE_LINK_NO_LONGER_USED & m_dwLinkStateFlags));
  1238. //Change into link notification
  1239. //Connection manager needs to know, in case this link deserves another
  1240. //connection
  1241. paqstats->m_dwNotifyType = dwNotifyType | NotifyTypeLinkMsgQueue;
  1242. paqstats->m_plmq = this;
  1243. hr = m_paqinst->HrNotify(paqstats, fAdd);
  1244. }
  1245. return hr;
  1246. }
  1247. //---[ CLinkMsgQueue::HrNotify ]----------------------------------------------
  1248. //
  1249. //
  1250. // Description:
  1251. // Recieve notification from one of our DestMsgQueues that (only) retry queue has been enqueue/dequeue'd.
  1252. // Unlike HrNotify, here we only need to update our stat without notifying m_paqinst and connection manager.
  1253. // Parameters:
  1254. // IN BOOL fAdd: add or remove
  1255. // Returns:
  1256. // S_OK on success
  1257. //
  1258. //-----------------------------------------------------------------------------
  1259. HRESULT CLinkMsgQueue::HrNotifyRetryStatChange(BOOL fAdd)
  1260. {
  1261. m_aqstats.UpdateRetryStats(fAdd);
  1262. return S_OK;
  1263. }
  1264. //---[ CLinkMsgQueue::dwModifyLinkState ]--------------------------------------
  1265. //
  1266. //
  1267. // Description:
  1268. // Sets and unsets state flags for this link
  1269. // Parameters:
  1270. // IN dwLinkStateToSet Combination of flags to set
  1271. // IN dwLinkStateToUnset Combination of flags to unset
  1272. //
  1273. // NOTE: dwLinkStateToSet and dwLinkStateToUnset should not overlap
  1274. // Returns:
  1275. // Original state of links
  1276. // History:
  1277. // 9/22/98 - MikeSwa Created
  1278. //
  1279. //-----------------------------------------------------------------------------
  1280. DWORD CLinkMsgQueue::dwModifyLinkState(IN DWORD dwLinkStateToSet,
  1281. IN DWORD dwLinkStateToUnset)
  1282. {
  1283. TraceFunctEnterEx((LPARAM) this, "CLinkMsgQueue::dwModifyLinkState");
  1284. DWORD dwOrigState = m_dwLinkStateFlags;
  1285. DWORD dwIntermState = m_dwLinkStateFlags;
  1286. DWORD dwSetBits = dwLinkStateToSet & ~dwLinkStateToUnset;
  1287. DWORD dwUnsetBits = dwLinkStateToUnset & ~dwLinkStateToSet;
  1288. //we shouldn't do this internally... lets make the operations cancel each other
  1289. _ASSERT(!(dwLinkStateToSet & dwLinkStateToUnset));
  1290. _ASSERT(dwSetBits == dwLinkStateToSet);
  1291. _ASSERT(dwUnsetBits == dwLinkStateToUnset);
  1292. //If info is being updated, we should let it set config-related bits
  1293. m_slInfo.ShareLock();
  1294. if (dwSetBits)
  1295. dwOrigState = dwInterlockedSetBits(&m_dwLinkStateFlags, dwSetBits);
  1296. if (dwUnsetBits)
  1297. dwIntermState = dwInterlockedUnsetBits(&m_dwLinkStateFlags, dwUnsetBits);
  1298. //Make sure we return the correct return value
  1299. if (dwUnsetBits && !dwSetBits)
  1300. dwOrigState = dwIntermState;
  1301. m_slInfo.ShareUnlock();
  1302. DebugTrace((LPARAM) this,
  1303. "ModifyLinkState set:%08X unset:%08X orig:%08X new:%08X",
  1304. dwLinkStateToSet, dwLinkStateToUnset, dwOrigState, m_dwLinkStateFlags);
  1305. TraceFunctLeave();
  1306. return dwOrigState;
  1307. }
  1308. //---[ CLinkMsgQueue::ScheduledCallback ]--------------------------------------
  1309. //
  1310. //
  1311. // Description:
  1312. // Callback function for scheduled connection callbacks
  1313. // Parameters:
  1314. // pvContext this pointer for CLinkMsgQueue
  1315. // Returns:
  1316. // -
  1317. // History:
  1318. // 1/16/99 - MikeSwa Created
  1319. //
  1320. //-----------------------------------------------------------------------------
  1321. void CLinkMsgQueue::ScheduledCallback(PVOID pvContext)
  1322. {
  1323. CLinkMsgQueue *plmq = (CLinkMsgQueue *) pvContext;
  1324. HRESULT hr = S_OK;
  1325. IConnectionManager *pIConnectionManager = NULL;
  1326. CConnMgr *pConnMgr = NULL;
  1327. DWORD dwLinkState = 0;
  1328. _ASSERT(plmq);
  1329. _ASSERT(LINK_MSGQ_SIG == plmq->m_dwSignature);
  1330. plmq->SendLinkStateNotification();
  1331. dwLinkState = plmq->m_dwLinkStateFlags;
  1332. //If connections are now allowed... we should kick the connection manager
  1333. _ASSERT(plmq->m_paqinst);
  1334. if (plmq->m_paqinst && plmq->fFlagsAllowConnection(dwLinkState))
  1335. {
  1336. hr = plmq->m_paqinst->HrGetIConnectionManager(&pIConnectionManager);
  1337. if (SUCCEEDED(hr))
  1338. {
  1339. _ASSERT(pIConnectionManager);
  1340. pConnMgr = (CConnMgr *) pIConnectionManager;
  1341. if (pConnMgr)
  1342. pConnMgr->KickConnections();
  1343. }
  1344. }
  1345. //Release AddRef from callback
  1346. plmq->Release();
  1347. }
  1348. //---[ CLinkMsgQueue::SendLinkStateNotification ]------------------------------
  1349. //
  1350. //
  1351. // Description:
  1352. // Sends notification if to scheduler/routing sink
  1353. // Parameters:
  1354. // -
  1355. // Returns:
  1356. // -
  1357. // History:
  1358. // 1/11/99 - MikeSwa Created
  1359. //
  1360. //-----------------------------------------------------------------------------
  1361. void CLinkMsgQueue::SendLinkStateNotification()
  1362. {
  1363. TraceFunctEnterEx((LPARAM) this, "CLinkMsgQueue::SendLinkStateNotification");
  1364. HRESULT hr = S_OK;
  1365. GUID guidRouter = GUID_NULL;
  1366. DWORD dwStateToSet = LINK_STATE_NO_ACTION;
  1367. DWORD dwStateToUnset = LINK_STATE_NO_ACTION;
  1368. DWORD dwResultingLinkState = LINK_STATE_NO_ACTION;
  1369. DWORD dwOriginalLinkState = LINK_STATE_NO_ACTION;
  1370. DWORD dwHiddenStateMask = ~(LINK_STATE_RESERVED |
  1371. LINK_STATE_CONNECT_IF_NO_MSGS |
  1372. LINK_STATE_DO_NOT_DELETE_UNTIL_NEXT_NOTIFY);
  1373. FILETIME ftNextAttempt;
  1374. BOOL fSendNotify = TRUE;
  1375. DWORD dwCurrentLinkState = m_dwLinkStateFlags;
  1376. //
  1377. // We should not send any notifications after we have notified routing
  1378. // that we are going away. We copy dwCurrentLinkState to a stack
  1379. // variable so that this is thread safe. If another thread sets
  1380. // LINK_STATE_LINK_NO_LONGER_USED after this check, we will still pass
  1381. // in the original value.
  1382. //
  1383. if (dwCurrentLinkState & LINK_STATE_PRIV_HAVE_SENT_NO_LONGER_USED)
  1384. {
  1385. fSendNotify = FALSE;
  1386. }
  1387. else if (dwCurrentLinkState & LINK_STATE_LINK_NO_LONGER_USED)
  1388. {
  1389. //
  1390. // Try to be the first thread to set this. If we are then we can
  1391. // continue with the notification
  1392. //
  1393. dwOriginalLinkState = dwModifyLinkState(LINK_STATE_PRIV_HAVE_SENT_NO_LONGER_USED,
  1394. LINK_STATE_NO_ACTION);
  1395. if (dwOriginalLinkState & LINK_STATE_PRIV_HAVE_SENT_NO_LONGER_USED)
  1396. fSendNotify = FALSE;
  1397. }
  1398. if (m_pILinkStateNotify && fSendNotify)
  1399. {
  1400. ZeroMemory(&ftNextAttempt, sizeof(FILETIME));
  1401. m_aqsched.GetGUID(&guidRouter);
  1402. hr = m_pILinkStateNotify->LinkStateNotify(m_szSMTPDomain, guidRouter,
  1403. m_aqsched.dwGetScheduleID(), m_szConnectorName,
  1404. (dwHiddenStateMask & dwCurrentLinkState),
  1405. (DWORD) m_lConsecutiveConnectionFailureCount, &ftNextAttempt,
  1406. &dwStateToSet, &dwStateToUnset);
  1407. DebugTrace((LPARAM) this,
  1408. "LinkStateNotify set:0x%08X unset:0x%08X hr:0x%08x",
  1409. dwStateToSet, dwStateToUnset, hr);
  1410. //Modify link state only on success and when we aren't deleting it
  1411. if (SUCCEEDED(hr) &&
  1412. !(m_dwLinkStateFlags & LINK_STATE_LINK_NO_LONGER_USED))
  1413. {
  1414. // schedule a callback if one was requested
  1415. if (ftNextAttempt.dwLowDateTime != 0 ||
  1416. ftNextAttempt.dwHighDateTime != 0)
  1417. {
  1418. DebugTrace((LPARAM) this,
  1419. "Schedule with FileTime %x:%x provided",
  1420. ftNextAttempt.dwLowDateTime,
  1421. ftNextAttempt.dwHighDateTime);
  1422. InternalUpdateFileTime(&m_ftNextScheduledCallback,
  1423. &ftNextAttempt);
  1424. //callback with next attempt
  1425. AddRef(); //Addref self as context
  1426. hr = m_paqinst->SetCallbackTime(
  1427. CLinkMsgQueue::ScheduledCallback,
  1428. this,
  1429. &ftNextAttempt);
  1430. if (FAILED(hr))
  1431. Release(); //callback will not happen... release context
  1432. }
  1433. if (!(LINK_STATE_CONNECT_IF_NO_MSGS & dwStateToSet))
  1434. {
  1435. //Routing has not explicitly set LINK_STATE_CONNECT_IF_NO_MSGS.
  1436. //We must unset it because we hid it from routing. The reason
  1437. //we do this is to allow Routers that are not interested in
  1438. //link-lifetime managment to set LINK_STATE_CONNECT_IF_NO_MSGS
  1439. //and not have to worry about race conditions that could
  1440. //cause a link to be delted while they are requesting a ping
  1441. dwStateToUnset |= LINK_STATE_CONNECT_IF_NO_MSGS;
  1442. }
  1443. //
  1444. // If not explicitly set, this bit is reset. Similar reasons
  1445. // as above.
  1446. //
  1447. if (!(LINK_STATE_DO_NOT_DELETE_UNTIL_NEXT_NOTIFY & dwStateToSet))
  1448. {
  1449. dwStateToUnset |= LINK_STATE_DO_NOT_DELETE_UNTIL_NEXT_NOTIFY;
  1450. }
  1451. if (!(m_dwLinkStateFlags & LINK_STATE_PRIV_HAVE_SENT_NOTIFICATION)) {
  1452. dwStateToSet |= LINK_STATE_PRIV_HAVE_SENT_NOTIFICATION;
  1453. }
  1454. }
  1455. }
  1456. else if (!m_pILinkStateNotify)
  1457. {
  1458. // Even if we don't have an ILinkStateNotify interface, we still
  1459. // need to reset this flag to prevent looping on connect
  1460. if (!(LINK_STATE_CONNECT_IF_NO_MSGS & dwStateToSet))
  1461. {
  1462. dwStateToUnset |= LINK_STATE_CONNECT_IF_NO_MSGS;
  1463. }
  1464. }
  1465. // Update the link state flags if we have any changes to make
  1466. if ((LINK_STATE_NO_ACTION != dwStateToSet) ||
  1467. (LINK_STATE_NO_ACTION != dwStateToUnset))
  1468. {
  1469. dwModifyLinkState(dwStateToSet, dwStateToUnset);
  1470. }
  1471. TraceFunctLeave();
  1472. }
  1473. //---[ CLinkMsgQueue::fShouldConnect ------------------------------------------
  1474. //
  1475. //
  1476. // Description:
  1477. // Function that is used to determine if a connection should be
  1478. // made.
  1479. // Uses a heursitic to decide if a connection should be made if multiple
  1480. // queues are routed to this link.. and thus the message count may be
  1481. // larger than it should be (since a message is counted once for each
  1482. // DMQ it is on).
  1483. // Parameters:
  1484. // IN cMaxLinkConnections Maximum # of connections per link
  1485. // IN cMinMessagesPerConnection Minimum # of messages per link before
  1486. // creating an additional connection.
  1487. // Returns:
  1488. // TRUE If a connection should be created
  1489. // FALSE Otherwise
  1490. // History:
  1491. // 11/5/98 - MikeSwa Created
  1492. //
  1493. //-----------------------------------------------------------------------------
  1494. BOOL CLinkMsgQueue::fShouldConnect(IN DWORD cMaxLinkConnections,
  1495. IN DWORD cMinMessagesPerConnection)
  1496. {
  1497. BOOL fConnect = FALSE;
  1498. DWORD cHeuristicMsgs = 0;
  1499. DWORD cHeuristicCheck = 0;
  1500. DWORD cCurrentMsgs = m_aqstats.m_cMsgs;
  1501. DWORD cTotalQueues = m_paqinst->cGetDestQueueCount();
  1502. DWORD cQueues = m_cQueues; //so it doesn't change on us
  1503. //If we have more than 1 queue and there is a total of more than 1 queue
  1504. //we can use a heurisitc to estimate actual # of messages to send, but
  1505. //don't bother with Heuristic if we are already over our max # of
  1506. //connections.
  1507. if ((m_cConnections < cMaxLinkConnections) &&
  1508. (1 < cTotalQueues) && (1 < cQueues) && cCurrentMsgs)
  1509. {
  1510. //m_aqstats.m_cOtherDomainsMsgSpread is the total # of *other* DMQs
  1511. //a message is associated with (per DMQ). If all of the DMQs for all
  1512. //messages are on this link then:
  1513. // m_cOtherDomainsMsgSpread
  1514. //is equal to:
  1515. // m_cMsgs*m_cMsgs*(cQueues-1).
  1516. //The following function... uses a probabilistic estimate to
  1517. //determine the number of messages that will be sent out this link.
  1518. //Since we cannot always assume that all messages on this link were
  1519. //queued to DMQ's assoicated with this link... we adjust the
  1520. //counted value by a factor of:
  1521. // ((cQueues-1)/(cTotalQueues-1))
  1522. //To determine the average # of domains per message (and hence the
  1523. //# of times is it counted), we use:
  1524. // (m_cOtherDomainsMsgSpread+m_cMsgs)/m_cMsgs
  1525. //To get a more accurate average, we modify the m_cOtherDomainsMsgSpread
  1526. //by the probability factor above.
  1527. //Finally:
  1528. //To get our heuristic, we divide the number of msgs by the average
  1529. //number of domains.
  1530. cHeuristicCheck = cCurrentMsgs +
  1531. m_aqstats.m_cOtherDomainsMsgSpread *
  1532. ((cQueues-1)/(cTotalQueues-1));
  1533. //This should be non-zero... but can happen if the counts are wrong and
  1534. //m_aqstats.m_cOtherDomainsMsgSpread is negative
  1535. _ASSERT(cHeuristicCheck);
  1536. if (cHeuristicCheck) //but we might as well be defensive
  1537. {
  1538. cHeuristicMsgs = (cCurrentMsgs*cCurrentMsgs)/cHeuristicCheck;
  1539. //Don't let the heuristic make us think there are no msgs to deliver
  1540. if (!cHeuristicMsgs && cCurrentMsgs)
  1541. cHeuristicMsgs = cCurrentMsgs;
  1542. }
  1543. else
  1544. {
  1545. cHeuristicMsgs = cCurrentMsgs;
  1546. }
  1547. }
  1548. else
  1549. {
  1550. cHeuristicMsgs = cCurrentMsgs;
  1551. }
  1552. if ((m_cConnections < cMaxLinkConnections) &&
  1553. (cHeuristicMsgs > m_cConnections*cMinMessagesPerConnection) &&
  1554. fCanSchedule())
  1555. {
  1556. //If we have no had a successful message only open 1 connection
  1557. if (!(m_dwLinkFlags & eLinkFlagsConnectionVerifed))
  1558. {
  1559. if (m_cConnections < 3)
  1560. fConnect = TRUE;
  1561. }
  1562. else
  1563. fConnect = TRUE;
  1564. }
  1565. else if (fCanSchedule() && !cHeuristicMsgs &&
  1566. ((LINK_STATE_CONNECT_IF_NO_MSGS & m_dwLinkStateFlags) &&
  1567. !m_cConnections))
  1568. {
  1569. //We want to create a connection to probe link state
  1570. fConnect = TRUE;
  1571. }
  1572. return fConnect;
  1573. }
  1574. //---[ CLinkMsgQueue::HrCreateConnectionIfNeeded ]-----------------------------
  1575. //
  1576. //
  1577. // Description:
  1578. //
  1579. // Parameters:
  1580. // IN cMaxLinkConnections Maximum # of connections per link
  1581. // IN cMinMessagesPerConnection Minimum # of messages per link before
  1582. // creating an additional connection.
  1583. // IN cMaxMessagesPerConnection Max messages to send on a single
  1584. // connection (0 is unlimited)
  1585. // IN pConnMgr Ptr to instance connection manger
  1586. // OUT pSMTPConn New connection object for this link
  1587. // Returns:
  1588. // S_OK on success if connection is needed
  1589. // S_FALSE on success if connection is not needed
  1590. // E_OUTOFMEMORY if connection object could not be created
  1591. // History:
  1592. // 11/5/98 - MikeSwa Created
  1593. // 12/7/1999 - MikeSwa Updated to make sure linkstate notify happens first
  1594. //
  1595. //-----------------------------------------------------------------------------
  1596. HRESULT CLinkMsgQueue::HrCreateConnectionIfNeeded(
  1597. IN DWORD cMaxLinkConnections,
  1598. IN DWORD cMinMessagesPerConnection,
  1599. IN DWORD cMaxMessagesPerConnection,
  1600. IN CConnMgr *pConnMgr,
  1601. OUT CSMTPConn **ppSMTPConn)
  1602. {
  1603. TraceFunctEnterEx((LPARAM) this, "CLinkMsgQueue::HrCreateConnectionIfNeeded");
  1604. HRESULT hr = S_FALSE;
  1605. _ASSERT(ppSMTPConn);
  1606. *ppSMTPConn = NULL;
  1607. CSMTPConn *pSMTPConn = NULL;
  1608. //We cannot create a connection until we have done our link state
  1609. //notification, because routing needs to have an opportunity to
  1610. //set the schedule for a link
  1611. SendLinkStateNotificationIfNew();
  1612. //Should we create a connection?
  1613. if (!fShouldConnect(cMaxLinkConnections, cMinMessagesPerConnection))
  1614. goto Exit;
  1615. //Try and be the thread that can create a connection
  1616. if (((DWORD) InterlockedIncrement((PLONG) &m_cConnections)) > cMaxLinkConnections)
  1617. {
  1618. InterlockedDecrement((PLONG) &m_cConnections);
  1619. goto Exit;
  1620. }
  1621. *ppSMTPConn = new CSMTPConn(pConnMgr, this, cMaxMessagesPerConnection);
  1622. if (!*ppSMTPConn)
  1623. {
  1624. InterlockedDecrement((PLONG) &m_cConnections);
  1625. hr = E_OUTOFMEMORY;
  1626. goto Exit;
  1627. }
  1628. //Grab lock and insert into list
  1629. m_slConnections.ExclusiveLock();
  1630. (*ppSMTPConn)->InsertConnectionInList(&m_liConnections);
  1631. m_slConnections.ExclusiveUnlock();
  1632. Exit:
  1633. //Make sure our return result is correct.
  1634. if (SUCCEEDED(hr))
  1635. {
  1636. if (*ppSMTPConn)
  1637. {
  1638. DebugTrace((LPARAM) this,
  1639. "Creating connection - linkstate:0x%08X",
  1640. m_dwLinkStateFlags);
  1641. hr = S_OK;
  1642. }
  1643. else
  1644. {
  1645. DebugTrace((LPARAM) this,
  1646. "Not creating connection - linkstate 0x%08X",
  1647. m_dwLinkStateFlags);
  1648. hr = S_FALSE;
  1649. }
  1650. }
  1651. TraceFunctLeave();
  1652. return hr;
  1653. }
  1654. //---[ CLinkMsgQueue::RemoveAllQueues ]----------------------------------------
  1655. //
  1656. //
  1657. // Description:
  1658. // Removes all queues from a link, without deleting or invalidating a
  1659. // link.
  1660. // Parameters:
  1661. // -
  1662. // Returns:
  1663. // -
  1664. // History:
  1665. // 11/5/98 - MikeSwa Created
  1666. //
  1667. //-----------------------------------------------------------------------------
  1668. void CLinkMsgQueue::RemoveAllQueues()
  1669. {
  1670. TraceFunctEnterEx((LPARAM) this, "CLinkMsgQueue::RemoveAllQueues");
  1671. PVOID pvContext = NULL;
  1672. CDestMsgQueue *pdmq = NULL;
  1673. //Walk list of queues and release them
  1674. dwInterlockedSetBits(&m_dwLinkFlags, eLinkFlagsRouteChangePending);
  1675. m_slQueues.ExclusiveLock();
  1676. pdmq = (CDestMsgQueue *) m_qlstQueues.pvDeleteItem(0, &pvContext);
  1677. while (pdmq)
  1678. {
  1679. m_cQueues--;
  1680. pdmq->AssertSignature();
  1681. pdmq->RemoveDMQFromLink(FALSE);
  1682. pdmq->Release();
  1683. pdmq = (CDestMsgQueue *) m_qlstQueues.pvDeleteItem(0, &pvContext);
  1684. }
  1685. m_aqstats.Reset();
  1686. dwInterlockedUnsetBits(&m_dwLinkFlags, eLinkFlagsRouteChangePending);
  1687. m_slQueues.ExclusiveUnlock();
  1688. TraceFunctLeave();
  1689. }
  1690. //---[ CLinkMsgQueue::HrGetLinkInfo ]------------------------------------------
  1691. //
  1692. //
  1693. // Description:
  1694. // Fills in the details for a LINK_INFO struct. RPC is resonsible for
  1695. // freeing memory.
  1696. // Parameters:
  1697. // IN OUT pliLinkInfo Ptr to link info struct to fill
  1698. // Returns:
  1699. // S_OK if successful
  1700. // E_OUTOFMEMORY if unable to allocate memory
  1701. // History:
  1702. // 12/3/98 - MikeSwa Created
  1703. // 2/22/99 - MikeSwa Modified to be IQueueAdminLink method
  1704. // 6/10/99 - MikeSwa Modified to support new QueueAdmin functionality
  1705. // 7/1/99 - MikeSwa Added LinkDiagnostic
  1706. //
  1707. //-----------------------------------------------------------------------------
  1708. STDMETHODIMP CLinkMsgQueue::HrGetLinkInfo(LINK_INFO *pliLinkInfo,
  1709. HRESULT *phrLinkDiagnostic)
  1710. {
  1711. TraceFunctEnterEx((LPARAM) this, "CLinkMsgQueue::HrGetLinkInfo");
  1712. pliLinkInfo->cMessages = m_aqstats.m_cMsgs;
  1713. pliLinkInfo->fStateFlags = 0;
  1714. FILETIME ftCurrent;
  1715. FILETIME ftOldest;
  1716. FILETIME *pftNextConnection = NULL;
  1717. BOOL fFoundOldest = FALSE;
  1718. DWORD iQueues = 0;
  1719. PVOID pvContext = NULL;
  1720. CDestMsgQueue *pdmq = NULL;
  1721. HRESULT hr = S_OK;
  1722. //
  1723. //Determine the state... check in order of most to least important
  1724. //
  1725. if (GetLinkType() == LI_TYPE_CURRENTLY_UNREACHABLE)
  1726. pliLinkInfo->fStateFlags = LI_READY;
  1727. else if (LINK_STATE_ADMIN_HALT & m_dwLinkStateFlags)
  1728. pliLinkInfo->fStateFlags = LI_FROZEN;
  1729. else if (m_cConnections)
  1730. pliLinkInfo->fStateFlags = LI_ACTIVE;
  1731. else if (!(LINK_STATE_RETRY_ENABLED & m_dwLinkStateFlags))
  1732. pliLinkInfo->fStateFlags = LI_RETRY;
  1733. else if (!(LINK_STATE_SCHED_ENABLED & m_dwLinkStateFlags))
  1734. pliLinkInfo->fStateFlags = LI_SCHEDULED;
  1735. else if (m_lConsecutiveConnectionFailureCount)
  1736. pliLinkInfo->fStateFlags = LI_RETRY;
  1737. else if (LINK_STATE_PRIV_CONFIG_TURN_ETRN & m_dwLinkStateFlags)
  1738. pliLinkInfo->fStateFlags = LI_REMOTE;
  1739. else //default to ready
  1740. pliLinkInfo->fStateFlags = LI_READY;
  1741. pliLinkInfo->fStateFlags |= GetLinkType();
  1742. //Write diagnostic
  1743. if (phrLinkDiagnostic)
  1744. *phrLinkDiagnostic = m_hrDiagnosticError;
  1745. //Get Size
  1746. pliLinkInfo->cbLinkVolume.QuadPart = m_aqstats.m_uliVolume.QuadPart;
  1747. //Find stOldestMessage
  1748. m_slQueues.ShareLock();
  1749. for (iQueues = 0;iQueues < m_cQueues; iQueues++)
  1750. {
  1751. pdmq = (CDestMsgQueue *) m_qlstQueues.pvGetItem(iQueues, &pvContext);
  1752. if (!pdmq) continue;
  1753. pdmq->GetOldestMsg(&ftCurrent);
  1754. //
  1755. // If we got a valid time, and it is earlier than the time we have
  1756. // now, then use it as the oldest for the link
  1757. //
  1758. if ((ftCurrent.dwLowDateTime || ftCurrent.dwHighDateTime) &&
  1759. (!fFoundOldest || (0 < CompareFileTime(&ftOldest, &ftCurrent))))
  1760. {
  1761. memcpy(&ftOldest, &ftCurrent, sizeof(FILETIME));
  1762. fFoundOldest = TRUE;
  1763. }
  1764. // Also count the failed messages (they're counted separately in the DMQ)
  1765. pliLinkInfo->cMessages += pdmq->cGetFailedMsgs();
  1766. }
  1767. m_slQueues.ShareUnlock();
  1768. //If we have not found an oldest, and the time is non-zero
  1769. //and we have messages, then report it.
  1770. if (fFoundOldest &&
  1771. (ftOldest.dwLowDateTime || ftOldest.dwHighDateTime) &&
  1772. pliLinkInfo->cMessages)
  1773. {
  1774. QueueAdminFileTimeToSystemTime(&ftOldest, &pliLinkInfo->stOldestMessage);
  1775. }
  1776. else
  1777. {
  1778. ZeroMemory(&pliLinkInfo->stOldestMessage, sizeof(SYSTEMTIME));
  1779. }
  1780. //
  1781. // Get next connection attempt time based on the state we are reporting
  1782. //
  1783. if (LI_RETRY & pliLinkInfo->fStateFlags)
  1784. {
  1785. pftNextConnection = &m_ftNextRetry;
  1786. }
  1787. else if (LI_SCHEDULED & pliLinkInfo->fStateFlags)
  1788. {
  1789. pftNextConnection = &m_ftNextScheduledCallback;
  1790. }
  1791. //
  1792. // If we are reporting a time, and it is non-zero, convert it to
  1793. // a system time.
  1794. //
  1795. if (pftNextConnection &&
  1796. (pftNextConnection->dwHighDateTime || pftNextConnection->dwLowDateTime))
  1797. {
  1798. QueueAdminFileTimeToSystemTime(pftNextConnection,
  1799. &pliLinkInfo->stNextScheduledConnection);
  1800. if (LI_SCHEDULED & pliLinkInfo->fStateFlags)
  1801. {
  1802. //
  1803. // Currently times are displayed at :02, :17, :32:, and :47... we will
  1804. // fudge the display time to that it actually says :00, :15, :30, :45
  1805. // to give the admin a better "admin experience" by rouding off to
  1806. // the nearest 5 minutes.
  1807. //
  1808. pliLinkInfo->stNextScheduledConnection.wMinute -=
  1809. (pliLinkInfo->stNextScheduledConnection.wMinute % 5);
  1810. }
  1811. }
  1812. else
  1813. {
  1814. //
  1815. // In this case, we don't have a time.
  1816. //
  1817. ZeroMemory(&pliLinkInfo->stNextScheduledConnection, sizeof(SYSTEMTIME));
  1818. }
  1819. if (m_szConnectorName)
  1820. {
  1821. pliLinkInfo->szLinkDN = wszQueueAdminConvertToUnicode(m_szConnectorName, 0);
  1822. if (!pliLinkInfo->szLinkDN)
  1823. {
  1824. hr = E_OUTOFMEMORY;
  1825. goto Exit;
  1826. }
  1827. }
  1828. else
  1829. {
  1830. pliLinkInfo->szLinkDN = NULL;
  1831. }
  1832. //$$TODO - Fill in pliLinkInfo->szExtendedStateInfo as appropriate
  1833. pliLinkInfo->szExtendedStateInfo = NULL;
  1834. if (!fRPCCopyName(&pliLinkInfo->szLinkName))
  1835. {
  1836. hr = E_OUTOFMEMORY;
  1837. goto Exit;
  1838. }
  1839. pliLinkInfo->dwSupportedLinkActions = m_dwSupportedActions;
  1840. Exit:
  1841. if (FAILED(hr))
  1842. {
  1843. //Cleanup allocated memory
  1844. if (pliLinkInfo->szLinkDN)
  1845. {
  1846. QueueAdminFree(pliLinkInfo->szLinkDN);
  1847. pliLinkInfo->szLinkDN = NULL;
  1848. }
  1849. if (pliLinkInfo->szLinkName)
  1850. {
  1851. QueueAdminFree(pliLinkInfo->szLinkName);
  1852. pliLinkInfo->szLinkName = NULL;
  1853. }
  1854. if (pliLinkInfo->szExtendedStateInfo)
  1855. {
  1856. QueueAdminFree(pliLinkInfo->szExtendedStateInfo);
  1857. pliLinkInfo->szExtendedStateInfo = NULL;
  1858. }
  1859. }
  1860. //
  1861. // Sanity checks to make sure we aren't passing back a zero'd
  1862. // FILETIME converted to a system time (which would have a
  1863. // year of 1601).
  1864. //
  1865. _ASSERT(1601 != pliLinkInfo->stNextScheduledConnection.wYear);
  1866. _ASSERT(1601 != pliLinkInfo->stOldestMessage.wYear);
  1867. TraceFunctLeave();
  1868. return hr;
  1869. }
  1870. //---[ CLinkMsgQueue::HrGetLinkID ]---------------------------------------------
  1871. //
  1872. //
  1873. // Description:
  1874. // Fills in the QUEUELINK_ID structure for this link. Caller must free
  1875. // memory allocated for link name
  1876. // Parameters:
  1877. // IN OUT pLinkID Ptr to link id struct to fill in
  1878. // Returns:
  1879. // S_OK on success
  1880. // E_OUTOFMEMORY if memory allocation fails
  1881. // History:
  1882. // 12/3/98 - MikeSwa Created
  1883. // 2/22/99 - MikeSwa Modified to IQueueAdminLink method
  1884. //
  1885. //-----------------------------------------------------------------------------
  1886. HRESULT CLinkMsgQueue::HrGetLinkID(QUEUELINK_ID *pLinkID)
  1887. {
  1888. pLinkID->qltType = QLT_LINK;
  1889. pLinkID->dwId = m_aqsched.dwGetScheduleID();
  1890. m_aqsched.GetGUID(&pLinkID->uuid);
  1891. if (!fRPCCopyName(&pLinkID->szName))
  1892. return E_OUTOFMEMORY;
  1893. else
  1894. return S_OK;
  1895. }
  1896. //---[ CLinkMsgQueue::HrGetQueueIDs ]--------------------------------------------
  1897. //
  1898. //
  1899. // Description:
  1900. // Gets the Queue IDs for DMQs associated with this link. Used by Queue
  1901. // Admin.
  1902. // Parameters:
  1903. // IN OUT pcQueues Sizeof array/ number of queues found
  1904. // IN OUT rgQueues Array to dump queue info into
  1905. // Returns:
  1906. // S_OK on success
  1907. // E_OUTOFMEMORY on out of memory failure
  1908. // HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) if array is too small
  1909. // History:
  1910. // 12/3/98 - MikeSwa Created
  1911. // 2/22/99 - MikeSwa Updated to IQueueAdminLink function
  1912. //
  1913. //-----------------------------------------------------------------------------
  1914. STDMETHODIMP CLinkMsgQueue::HrGetQueueIDs(DWORD *pcQueues, QUEUELINK_ID *rgQueues)
  1915. {
  1916. _ASSERT(pcQueues);
  1917. _ASSERT(rgQueues);
  1918. HRESULT hr = S_OK;
  1919. DWORD iQueues = 0;
  1920. PVOID pvContext = NULL;
  1921. CDestMsgQueue *pdmq = NULL;
  1922. QUEUELINK_ID *pCurrentQueueID = rgQueues;
  1923. m_slQueues.ShareLock();
  1924. if (*pcQueues < m_cQueues)
  1925. {
  1926. hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
  1927. goto Exit;
  1928. }
  1929. *pcQueues = 0;
  1930. //Iterate over all queues and get IDs
  1931. for (iQueues = 0; iQueues < m_cQueues && SUCCEEDED(hr); iQueues++)
  1932. {
  1933. pdmq = (CDestMsgQueue *) m_qlstQueues.pvGetItem(iQueues, &pvContext);
  1934. _ASSERT(pdmq);
  1935. hr = pdmq->HrGetQueueID(pCurrentQueueID);
  1936. if (FAILED(hr))
  1937. goto Exit;
  1938. pCurrentQueueID++;
  1939. (*pcQueues)++;
  1940. }
  1941. Exit:
  1942. m_slQueues.ShareUnlock();
  1943. return hr;
  1944. }
  1945. //---[ CLinkMsgQueue::HrApplyQueueAdminFunction ]------------------------------
  1946. //
  1947. //
  1948. // Description:
  1949. // Used by queue admin to apply a function all queues on this link
  1950. // Parameters:
  1951. // IN pIQueueAdminMessageFilter
  1952. // Returns:
  1953. // S_OK on success
  1954. // History:
  1955. // 12/11/98 - MikeSwa Created
  1956. // 2/22/99 - MikeSwa Modified to IQueueAdminAction interface
  1957. //
  1958. //-----------------------------------------------------------------------------
  1959. STDMETHODIMP CLinkMsgQueue::HrApplyQueueAdminFunction(
  1960. IQueueAdminMessageFilter *pIQueueAdminMessageFilter)
  1961. {
  1962. HRESULT hr = S_OK;
  1963. DWORD iQueues = 0;
  1964. PVOID pvListContext = NULL;
  1965. CDestMsgQueue *pdmq = NULL;
  1966. IQueueAdminAction *pIQueueAdminAction = NULL;
  1967. m_slQueues.ShareLock();
  1968. //Iterate over all queues and get IDs
  1969. for (iQueues = 0; iQueues < m_cQueues && SUCCEEDED(hr); iQueues++)
  1970. {
  1971. pdmq = (CDestMsgQueue *) m_qlstQueues.pvGetItem(iQueues, &pvListContext);
  1972. _ASSERT(pdmq);
  1973. hr = pdmq->QueryInterface(IID_IQueueAdminAction,
  1974. (void **) &pIQueueAdminAction);
  1975. if (FAILED(hr))
  1976. goto Exit;
  1977. _ASSERT(pIQueueAdminAction);
  1978. hr = pIQueueAdminAction->HrApplyQueueAdminFunction(
  1979. pIQueueAdminMessageFilter);
  1980. if (FAILED(hr))
  1981. goto Exit;
  1982. }
  1983. Exit:
  1984. m_slQueues.ShareUnlock();
  1985. if (pIQueueAdminAction)
  1986. pIQueueAdminAction->Release();
  1987. return hr;
  1988. }
  1989. //---[ CLinkMsgQueue::InternalUpdateFileTime ]---------------------------------
  1990. //
  1991. //
  1992. // Description:
  1993. // Updates an internal filetime in a thread safe manner. This does not
  1994. // guarantee that the filetime will be updated, but does guarantee that
  1995. // if it is updated, the filetime is not corrupt.
  1996. //
  1997. // NOTE: these file times are used only for display purposes by the
  1998. // Queue Admin
  1999. // Parameters:
  2000. // pftDest Ptr to member variable to update
  2001. // pftSrc Pft to source filetime
  2002. // Returns:
  2003. // -
  2004. // History:
  2005. // 1/11/99 - MikeSwa Created
  2006. //
  2007. //-----------------------------------------------------------------------------
  2008. void CLinkMsgQueue::InternalUpdateFileTime(FILETIME *pftDest, FILETIME *pftSrc)
  2009. {
  2010. TraceFunctEnterEx((LPARAM) this, "CLinkMsgQueue::InternalUpdateFileTime");
  2011. if (pftDest && pftSrc)
  2012. {
  2013. DebugTrace((LPARAM) this,
  2014. "Updating filetime from %x:%x to %x:%x",
  2015. pftDest->dwLowDateTime, pftDest->dwHighDateTime,
  2016. pftSrc->dwLowDateTime, pftSrc->dwHighDateTime);
  2017. if (!(eLinkFlagsFileTimeSpinLock &
  2018. dwInterlockedSetBits(&m_dwLinkFlags, eLinkFlagsFileTimeSpinLock)))
  2019. {
  2020. //We got the spinlock
  2021. memcpy(pftDest, pftSrc, sizeof(FILETIME));
  2022. dwInterlockedUnsetBits(&m_dwLinkFlags, eLinkFlagsFileTimeSpinLock);
  2023. }
  2024. }
  2025. TraceFunctLeave();
  2026. }
  2027. //---[ CLinkMsgQueue::HrGetNextMsgRef ]----------------------------------------
  2028. //
  2029. //
  2030. // Description:
  2031. // Returns the next MsgRef to be delivered without doing the
  2032. // PrepareDelivery step. This is used in gateway delivery to route the
  2033. // message to the locally. Additionally, this will mark all a the
  2034. // gateway DMQ's as local on the msgref, so that a subsequent reroute
  2035. // will only affect messages that have not already been put in the
  2036. // local delivery queue.
  2037. // Parameters:
  2038. // IN fRoutingLockHeld TRUE is routing lock is already held
  2039. // OUT ppmsgref Returned Msg
  2040. // Returns:
  2041. // S_OK on success
  2042. // AQUEUE_E_QUEUE_EMPTY otherwise
  2043. // History:
  2044. // 1/26/99 - MikeSwa Created
  2045. // 3/25/99 - MikeSwa Added fRoutingLockHeld to fix deadlock
  2046. // 2/17/2000 - MikeSwa Modified for gateway delivery reroute
  2047. //
  2048. //-----------------------------------------------------------------------------
  2049. HRESULT CLinkMsgQueue::HrGetNextMsgRef(IN BOOL fRoutingLockHeld,
  2050. OUT CMsgRef **ppmsgref)
  2051. {
  2052. TraceFunctEnterEx((LPARAM) this, "CLinkMsgQueue::HrGetNextMsg");
  2053. _ASSERT(ppmsgref);
  2054. HRESULT hr = S_OK;
  2055. BOOL fLockedShutdown = FALSE;
  2056. BOOL fLockedQueues = FALSE;
  2057. DWORD iQueues = 0;
  2058. DWORD dwCurrentRoundRobinIndex = m_dwRoundRobinIndex;
  2059. CDestMsgQueue *pdmq = NULL;
  2060. PVOID pvContext = NULL;
  2061. if (m_dwLinkFlags & eLinkFlagsInvalid)
  2062. {
  2063. hr = AQUEUE_E_LINK_INVALID;
  2064. goto Exit;
  2065. }
  2066. //Don't even bother to wait for queue lock if routing change is pending
  2067. if ((m_dwLinkFlags & eLinkFlagsRouteChangePending) || !m_aqstats.m_cMsgs)
  2068. {
  2069. hr = AQUEUE_E_QUEUE_EMPTY;
  2070. goto Exit;
  2071. }
  2072. //Make sure domain info is updated & we should still be sending messages
  2073. if (!m_paqinst->fTryShutdownLock())
  2074. {
  2075. hr = AQUEUE_E_SHUTDOWN;
  2076. goto Exit;
  2077. }
  2078. //Current implementation of sharelocks are not share reentrant. Only
  2079. //grab lock if caller has not.
  2080. if (!fRoutingLockHeld)
  2081. m_paqinst->RoutingShareLock();
  2082. fLockedShutdown = TRUE;
  2083. m_slQueues.ShareLock();
  2084. fLockedQueues = TRUE;
  2085. if (m_cQueues == 0)
  2086. {
  2087. //There are currently no queue associated with this link
  2088. hr = AQUEUE_E_QUEUE_EMPTY;
  2089. goto Exit;
  2090. }
  2091. //$$TODO impose some ordering on these queues
  2092. for (iQueues = 0;
  2093. iQueues < m_cQueues && SUCCEEDED(hr) && !(*ppmsgref);
  2094. iQueues++)
  2095. {
  2096. *ppmsgref = NULL;
  2097. pdmq = (CDestMsgQueue *) m_qlstQueues.pvGetItem(
  2098. (iQueues+dwCurrentRoundRobinIndex)%m_cQueues, &pvContext);
  2099. _ASSERT(pdmq);
  2100. pdmq->AssertSignature();
  2101. //get msg reference
  2102. hr = pdmq->HrDequeueMsg(eEffPriLow, ppmsgref, NULL);
  2103. if (FAILED(hr))
  2104. {
  2105. if (AQUEUE_E_QUEUE_EMPTY == hr)
  2106. hr = S_OK;
  2107. else
  2108. goto Exit;
  2109. }
  2110. //
  2111. // Mark this as a local queue for this message
  2112. //
  2113. if (*ppmsgref)
  2114. (*ppmsgref)->MarkQueueAsLocal(pdmq);
  2115. }
  2116. //Visit a new queue on every GetNextMsg
  2117. InterlockedIncrement((PLONG) &m_dwRoundRobinIndex);
  2118. Exit:
  2119. if (fLockedQueues)
  2120. m_slQueues.ShareUnlock();
  2121. if (fLockedShutdown)
  2122. {
  2123. //If routing lock is not held by caller, then we must release it
  2124. if (!fRoutingLockHeld)
  2125. m_paqinst->RoutingShareUnlock();
  2126. m_paqinst->ShutdownUnlock();
  2127. }
  2128. if (!*ppmsgref)
  2129. hr = AQUEUE_E_QUEUE_EMPTY;
  2130. TraceFunctLeave();
  2131. return hr;
  2132. }
  2133. //---[ CLinkMsgQueue::HrPrepareDelivery ]--------------------------------------
  2134. //
  2135. //
  2136. // Description:
  2137. // Prepares delivery for a message for this link
  2138. // Parameters:
  2139. // IN pmsgref MsgRef to prepare for delivery
  2140. // IN fQueuesLock TRUE is m_slQueues is locked already
  2141. // IN fLocal Prepare delivery for all domains with NULL queues
  2142. // IN fDelayDSN Check/Set Delay bitmap (only send 1 Delay DSN).
  2143. // IN pqlstQueues QuickList of DMQ's
  2144. // IN OUT pdcntxt context that must be returned on Ack
  2145. // OUT pcRecips # of recips to deliver for
  2146. // OUT prgdwRecips Array of recipient indexes
  2147. // Returns:
  2148. // S_OK on success
  2149. // Failure code from CMsgRef::HrPrepareDelivery
  2150. // History:
  2151. // 1/26/99 - MikeSwa Created
  2152. //
  2153. //-----------------------------------------------------------------------------
  2154. HRESULT CLinkMsgQueue::HrInternalPrepareDelivery(
  2155. IN CMsgRef *pmsgref,
  2156. IN BOOL fQueuesLocked,
  2157. IN BOOL fLocal,
  2158. IN BOOL fDelayDSN,
  2159. IN OUT CDeliveryContext *pdcntxt,
  2160. OUT DWORD *pcRecips,
  2161. OUT DWORD **prgdwRecips)
  2162. {
  2163. HRESULT hr = S_OK;
  2164. BOOL fQueuesLockedByUs = FALSE;
  2165. _ASSERT(pmsgref);
  2166. if (!pmsgref)
  2167. {
  2168. hr = E_INVALIDARG;
  2169. goto Exit;
  2170. }
  2171. if (!fQueuesLocked)
  2172. {
  2173. m_slQueues.ShareLock();
  2174. fQueuesLockedByUs = TRUE;
  2175. }
  2176. hr = pmsgref->HrPrepareDelivery(fLocal, fDelayDSN, &m_qlstQueues, NULL,
  2177. pdcntxt, pcRecips, prgdwRecips);
  2178. if (FAILED(hr))
  2179. goto Exit;
  2180. Exit:
  2181. if (fQueuesLockedByUs)
  2182. m_slQueues.ShareUnlock();
  2183. return hr;
  2184. }
  2185. //---[ CLinkMsgQueue::SetDiagnosticInfo ]--------------------------------------
  2186. //
  2187. //
  2188. // Description:
  2189. // Sets the diagnostic information for this link
  2190. // Parameters:
  2191. // IN hrDiagnosticError Error code... if SUCCESS we thow away
  2192. // the rest of the information
  2193. // IN szDiagnosticVerb String pointing to the protocol
  2194. // verb that caused the failure.
  2195. // IN szDiagnosticResponse String that contains the remote
  2196. // servers response.
  2197. // Returns:
  2198. // -
  2199. // History:
  2200. // 2/18/99 - MikeSwa Created
  2201. //
  2202. //-----------------------------------------------------------------------------
  2203. void CLinkMsgQueue::SetDiagnosticInfo(
  2204. IN HRESULT hrDiagnosticError,
  2205. IN LPCSTR szDiagnosticVerb,
  2206. IN LPCSTR szDiagnosticResponse)
  2207. {
  2208. m_slInfo.ExclusiveLock();
  2209. m_hrDiagnosticError = hrDiagnosticError;
  2210. //zero original buffers
  2211. ZeroMemory(&m_szDiagnosticVerb, sizeof(m_szDiagnosticVerb));
  2212. ZeroMemory(&m_szDiagnosticResponse, sizeof(m_szDiagnosticResponse));
  2213. //copy buffers
  2214. if (szDiagnosticVerb)
  2215. strncpy(m_szDiagnosticVerb, szDiagnosticVerb,
  2216. sizeof(m_szDiagnosticVerb)-1);
  2217. if (szDiagnosticResponse)
  2218. strncpy(m_szDiagnosticResponse, szDiagnosticResponse,
  2219. sizeof(m_szDiagnosticResponse)-1);
  2220. m_slInfo.ExclusiveUnlock();
  2221. }
  2222. //---[ CLinkMsgQueue::GetDiagnosticInfo ]--------------------------------------
  2223. //
  2224. //
  2225. // Description:
  2226. // Gets the diagnostic information for this link
  2227. // Parameters:
  2228. // IN LPSTR szDiagnosticVerb - buffer to receive the verb that
  2229. // caused the error
  2230. // IN DWORD cDiagnosticVerb - length of the buffer
  2231. // IN LPSTR szDiagnosticResponse- buffer to recieve the response
  2232. // of the error
  2233. // IN DWORD cbDiagnosticResponse- length of buffer
  2234. // OUT HRESULT *phrDiagnosticError - HRESULT for error
  2235. // Returns:
  2236. // -
  2237. // History:
  2238. // 3/9/99 - AWetmore Created
  2239. // 8/2/99 - Mikeswa...updated to use m_slInfo.
  2240. //
  2241. //-----------------------------------------------------------------------------
  2242. void CLinkMsgQueue::GetDiagnosticInfo(
  2243. IN LPSTR szDiagnosticVerb,
  2244. IN DWORD cbDiagnosticVerb,
  2245. IN LPSTR szDiagnosticResponse,
  2246. IN DWORD cbDiagnosticResponse,
  2247. OUT HRESULT *phrDiagnosticError)
  2248. {
  2249. if (szDiagnosticVerb)
  2250. ZeroMemory(szDiagnosticVerb, cbDiagnosticVerb);
  2251. if (szDiagnosticResponse)
  2252. ZeroMemory(szDiagnosticResponse, cbDiagnosticResponse);
  2253. m_slInfo.ShareLock();
  2254. if (phrDiagnosticError)
  2255. *phrDiagnosticError = m_hrDiagnosticError;
  2256. //copy buffers
  2257. if (*m_szDiagnosticVerb && szDiagnosticVerb)
  2258. strncpy(szDiagnosticVerb, m_szDiagnosticVerb, cbDiagnosticVerb);
  2259. if (*m_szDiagnosticResponse && szDiagnosticResponse)
  2260. strncpy(szDiagnosticResponse,
  2261. m_szDiagnosticResponse,
  2262. cbDiagnosticResponse);
  2263. m_slInfo.ShareUnlock();
  2264. }
  2265. //---[ CLinkMsgQueue::HrApplyActionToMessage ]---------------------------------
  2266. //
  2267. //
  2268. // Description:
  2269. // Applies an action to this message for this queue. This will be called
  2270. // by the IQueueAdminMessageFilter during a queue enumeration function.
  2271. //
  2272. // This code path is currently not executed... eventually me may consider
  2273. // doing this to allow a DSN to be generated per link.
  2274. //
  2275. // For this to be called. CLinkMsgQueue::HrApplyActionToMessage would
  2276. // need to iterate over the DMQ's queues with it's own IQueueAdminAction
  2277. // iterface pointed to by the filter intead of the DMQ's.
  2278. //
  2279. // Parameters:
  2280. // IN *pIUnknownMsg ptr to message abstraction
  2281. // IN ma Message action to perform
  2282. // IN pvContext Context set on IQueueAdminFilter
  2283. // OUT pfShouldDelete TRUE if the message should be deleted
  2284. // Returns:
  2285. // S_OK on success
  2286. // History:
  2287. // 2/21/99 - MikeSwa Created
  2288. // 4/2/99 - MikeSwa Added context
  2289. //
  2290. //-----------------------------------------------------------------------------
  2291. STDMETHODIMP CLinkMsgQueue::HrApplyActionToMessage(
  2292. IUnknown *pIUnknownMsg,
  2293. MESSAGE_ACTION ma,
  2294. PVOID pvContext,
  2295. BOOL *pfShouldDelete)
  2296. {
  2297. _ASSERT(0 && "Not reachable");
  2298. return E_NOTIMPL;
  2299. }
  2300. //---[ CLinkMsgQueue::HrApplyActionToLink ]------------------------------------
  2301. //
  2302. //
  2303. // Description:
  2304. // Applies the specified QueueAdmin action to this link
  2305. // Parameters:
  2306. // IN la Link action to apply
  2307. // Returns:
  2308. // S_OK on success
  2309. // E_INVALIDARG if bogus action is given
  2310. // History:
  2311. // 2/22/99 - MikeSwa Created (moved most of code from
  2312. // CAQSvrInst::SetLinkState)
  2313. //
  2314. //-----------------------------------------------------------------------------
  2315. STDMETHODIMP CLinkMsgQueue::HrApplyActionToLink(LINK_ACTION la)
  2316. {
  2317. DWORD dwLinkFlagsToSet = LINK_STATE_NO_ACTION;
  2318. DWORD dwLinkFlagsToUnset = LINK_STATE_NO_ACTION;
  2319. HRESULT hr = S_OK;
  2320. //Is action supported?
  2321. if (!fActionIsSupported(la))
  2322. goto Exit;
  2323. //figure out how we want to change the link state
  2324. if (LA_KICK == la)
  2325. {
  2326. //kick the link
  2327. dwLinkFlagsToSet = LINK_STATE_RETRY_ENABLED |
  2328. LINK_STATE_ADMIN_FORCE_CONN |
  2329. LINK_STATE_CONNECT_IF_NO_MSGS;
  2330. dwLinkFlagsToUnset = LINK_STATE_ADMIN_HALT;
  2331. }
  2332. else if (LA_FREEZE == la)
  2333. {
  2334. //Admin wants this link to stop sending mail outbound
  2335. dwLinkFlagsToSet = LINK_STATE_ADMIN_HALT;
  2336. dwLinkFlagsToUnset = LINK_STATE_ADMIN_FORCE_CONN;
  2337. }
  2338. else if (LA_THAW == la)
  2339. {
  2340. //Unset frozen flags
  2341. dwLinkFlagsToUnset = LINK_STATE_ADMIN_HALT;
  2342. }
  2343. else
  2344. {
  2345. //invalid arg
  2346. hr = E_INVALIDARG;
  2347. goto Exit;
  2348. }
  2349. dwModifyLinkState(dwLinkFlagsToSet, dwLinkFlagsToUnset);
  2350. Exit:
  2351. return hr;
  2352. }
  2353. //---[ CLinkMsgQueue::QueryInterface ]-----------------------------------------
  2354. //
  2355. //
  2356. // Description:
  2357. // QueryInterface for CDestMsgQueue that supports:
  2358. // - IQueueAdminAction
  2359. // - IUnknown
  2360. // - IQueueAdminLink
  2361. // Parameters:
  2362. //
  2363. // Returns:
  2364. //
  2365. // History:
  2366. // 2/21/99 - MikeSwa Created
  2367. //
  2368. //-----------------------------------------------------------------------------
  2369. STDMETHODIMP CLinkMsgQueue::QueryInterface(REFIID riid, LPVOID *ppvObj)
  2370. {
  2371. HRESULT hr = S_OK;
  2372. if (!ppvObj)
  2373. {
  2374. hr = E_POINTER;
  2375. goto Exit;
  2376. }
  2377. if (IID_IUnknown == riid)
  2378. {
  2379. *ppvObj = static_cast<IQueueAdminAction *>(this);
  2380. }
  2381. else if (IID_IQueueAdminAction == riid)
  2382. {
  2383. *ppvObj = static_cast<IQueueAdminAction *>(this);
  2384. }
  2385. else if (IID_IQueueAdminLink == riid)
  2386. {
  2387. *ppvObj = static_cast<IQueueAdminLink *>(this);
  2388. }
  2389. else
  2390. {
  2391. *ppvObj = NULL;
  2392. hr = E_NOINTERFACE;
  2393. goto Exit;
  2394. }
  2395. static_cast<IUnknown *>(*ppvObj)->AddRef();
  2396. Exit:
  2397. return hr;
  2398. }
  2399. //---[ CLinkMsgQueue::HrGetNumQueues ]-----------------------------------------
  2400. //
  2401. //
  2402. // Description:
  2403. // Returns the number of queues on this link
  2404. // Parameters:
  2405. // OUT pcQueues # numbr of queues
  2406. // Returns:
  2407. // S_OK on success
  2408. // E_POINTER if pcQueues is not valid
  2409. // History:
  2410. // 2/22/99 - MikeSwa Created
  2411. //
  2412. //-----------------------------------------------------------------------------
  2413. STDMETHODIMP CLinkMsgQueue::HrGetNumQueues(DWORD *pcQueues)
  2414. {
  2415. HRESULT hr = S_OK;
  2416. _ASSERT(pcQueues);
  2417. if (!pcQueues)
  2418. {
  2419. hr = E_POINTER;
  2420. goto Exit;
  2421. }
  2422. *pcQueues = cGetNumQueues();
  2423. Exit:
  2424. return hr;
  2425. }
  2426. //---[ CLinkMsgQueue::fMatchesID ]---------------------------------------------
  2427. //
  2428. //
  2429. // Description:
  2430. // Used to determine if this link matches a given scheduleID/link pair
  2431. // Parameters:
  2432. // IN QueueLinkID ID to match against
  2433. // Returns:
  2434. // TRUE if it matches
  2435. // FALSE if it does not
  2436. // History:
  2437. // 2/23/99 - MikeSwa Created
  2438. //
  2439. //-----------------------------------------------------------------------------
  2440. BOOL STDMETHODCALLTYPE CLinkMsgQueue::fMatchesID(QUEUELINK_ID *pQueueLinkID)
  2441. {
  2442. _ASSERT(pQueueLinkID);
  2443. _ASSERT(pQueueLinkID->szName);
  2444. CAQScheduleID aqsched(pQueueLinkID->uuid, pQueueLinkID->dwId);
  2445. if (!fIsSameScheduleID(&aqsched))
  2446. return FALSE;
  2447. if (!fBiStrcmpi(m_szSMTPDomain, pQueueLinkID->szName))
  2448. return FALSE;
  2449. //Everything matched!
  2450. return TRUE;
  2451. }