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.

3815 lines
124 KiB

  1. //-----------------------------------------------------------------------------
  2. //
  3. //
  4. // File: domain.cpp
  5. //
  6. // Description: Implementation of CDomainMapping, CDomainEntry, and
  7. // CDomainMappingTable.
  8. //
  9. // The DomainMappingTable is a domain name hash table that contains the
  10. // mappings from final destination to queues.
  11. //
  12. // Author: mikeswa
  13. //
  14. // Copyright (C) 1997 Microsoft Corporation
  15. //
  16. //-----------------------------------------------------------------------------
  17. #include "aqprecmp.h"
  18. #include "aqroute.h"
  19. #include "localq.h"
  20. #include "asyncq.h"
  21. #include "mailadmq.h"
  22. #include "tran_evntlog.h"
  23. const DWORD LOCAL_DOMAIN_NAME_SIZE = 512;
  24. //Max mislabled queues in empty list, before we will clean the list
  25. const DWORD MAX_MISPLACED_QUEUES_IN_EMPTY_LIST = 100;
  26. //Callback for retry
  27. void CDomainMappingTable::SpecialRetryCallback(PVOID pvContext)
  28. {
  29. CDomainMappingTable *pdnt = (CDomainMappingTable *) pvContext;
  30. _ASSERT(pdnt);
  31. _ASSERT(DOMAIN_MAPPING_TABLE_SIG == pdnt->m_dwSignature);
  32. dwInterlockedUnsetBits(&(pdnt->m_dwFlags), DMT_FLAGS_SPECIAL_DELIVERY_CALLBACK);
  33. pdnt->ProcessSpecialLinks(0, FALSE);
  34. }
  35. //---[ ReUnreachableErrorToAqueueError ]---------------------------------------
  36. //
  37. //
  38. // Description:
  39. // Translates a HRESULT returned from GetNextHop to one that is meaningful
  40. // to aqueue DSN generation.
  41. // Parameters:
  42. // IN HRESULT reErr -- Error from routing.
  43. // IN OUT HRESULT aqErr -- Corresponding aqueue error code.
  44. // Returns:
  45. // Nothing.
  46. // History:
  47. // GPulla created.
  48. //
  49. //-----------------------------------------------------------------------------
  50. void ReUnreachableErrorToAqueueError(HRESULT reErr, HRESULT *aqErr)
  51. {
  52. if(E_ACCESSDENIED == reErr)
  53. *aqErr = AQUEUE_E_ACCESS_DENIED;
  54. else if(HRESULT_FROM_WIN32(ERROR_MESSAGE_EXCEEDS_MAX_SIZE) == reErr)
  55. *aqErr = AQUEUE_E_MESSAGE_TOO_LARGE;
  56. else
  57. *aqErr = AQUEUE_E_NO_ROUTE;
  58. }
  59. //---[ DeinitDomainEntryIteratorFn ]--------------------------------------------
  60. //
  61. //
  62. // Description:
  63. // Deletes and releases all internal domain info objects in table
  64. // Parameters:
  65. // IN pvContext - pointer to context (ignored)
  66. // IN pvData - data entry to look at
  67. // IN fWildcardData - TRUE if data is a wildcard entry (ignored)
  68. // OUT pfContinue - TRUE if iterator should continue to the next entry
  69. // OUT pfRemoveEntry - TRUE if entry should be deleted
  70. // Returns:
  71. // -
  72. // History:
  73. // 6/17/98 - MikeSwa Created
  74. //
  75. //-----------------------------------------------------------------------------
  76. VOID DeinitDomainEntryIteratorFn(PVOID pvContext, PVOID pvData, BOOL fWildcard,
  77. BOOL *pfContinue, BOOL *pfDelete)
  78. {
  79. CDomainEntry *pdentry = (CDomainEntry *) pvData;
  80. *pfDelete = FALSE;
  81. *pfContinue = TRUE;
  82. pdentry->HrDeinitialize();
  83. }
  84. //---[ ReleaseDomainEntryIteratorFn ]------------------------------------------
  85. //
  86. //
  87. // Description:
  88. // Deletes and releases all internal domain info objects in table
  89. // Parameters:
  90. // IN pvContext - pointer to context (ignored)
  91. // IN pvData - data entry to look at
  92. // IN fWildcardData - TRUE if data is a wildcard entry (ignored)
  93. // OUT pfContinue - TRUE if iterator should continue to the next entry
  94. // OUT pfRemoveEntry - TRUE if entry should be deleted
  95. // Returns:
  96. // -
  97. // History:
  98. // 6/17/98 - MikeSwa Created
  99. //
  100. //-----------------------------------------------------------------------------
  101. VOID ReleaseDomainEntryIteratorFn(PVOID pvContext, PVOID pvData, BOOL fWildcard,
  102. BOOL *pfContinue, BOOL *pfDelete)
  103. {
  104. ULONG cRefs;
  105. CDomainEntry *pdentry = (CDomainEntry *) pvData;
  106. *pfDelete = TRUE;
  107. *pfContinue = TRUE;
  108. cRefs = pdentry->Release();
  109. _ASSERT(!cRefs && "leaking domain entries");
  110. }
  111. //***[ CDomainMapping Methods ]************************************************
  112. //---[ CDomainMapping::Clone ]-------------------------------------------------
  113. //
  114. //
  115. // Description: Fills the current mapping with data from another DomainMapping
  116. //
  117. // Parameters:
  118. // IN pdmap CDomainMapping to clone
  119. //
  120. // Returns:
  121. // -
  122. //
  123. //-----------------------------------------------------------------------------
  124. void CDomainMapping::Clone(IN CDomainMapping *pdmap)
  125. {
  126. TraceFunctEnterEx((LPARAM) this, "CDomainMapping::Clone");
  127. Assert(pdmap);
  128. m_pdentryDomainID = pdmap->m_pdentryDomainID;
  129. m_pdentryQueueID = pdmap->m_pdentryQueueID;
  130. TraceFunctLeave();
  131. }
  132. //---[ CDomainMapping::HrGetDestMsgQueue ]-------------------------------------
  133. //
  134. //
  135. // Description: Returns a pointer to the queue that this mapping points to
  136. //
  137. // Parameters:
  138. // IN paqmt Message Type to get queue for
  139. // OUT ppdmq pointer returned
  140. //
  141. // Returns:
  142. // S_OK on success
  143. // AQUEUE_E_INVALID_DOMAIN
  144. //
  145. //-----------------------------------------------------------------------------
  146. HRESULT CDomainMapping::HrGetDestMsgQueue(IN CAQMessageType *paqmt,
  147. OUT CDestMsgQueue **ppdmq)
  148. {
  149. TraceFunctEnterEx((LPARAM) this, "CDomainMapping::HrGetDestMsgQueue");
  150. HRESULT hr = S_OK;
  151. Assert(ppdmq);
  152. if (m_pdentryQueueID == NULL)
  153. {
  154. hr = AQUEUE_E_INVALID_DOMAIN;
  155. goto Exit;
  156. }
  157. hr = m_pdentryQueueID->HrGetDestMsgQueue(paqmt, ppdmq);
  158. Exit:
  159. TraceFunctLeave();
  160. return hr;
  161. }
  162. //***[ CDomainEntry Methods ]**************************************************
  163. //---[ CDomainEntry::CDomainEntry() ]------------------------------------------
  164. //
  165. //
  166. // Description: CDomainEntry constructor
  167. //
  168. // Parameters:
  169. // paqinst - ptr to the virtual server object
  170. //
  171. // Returns:
  172. // -
  173. //
  174. //-----------------------------------------------------------------------------
  175. CDomainEntry::CDomainEntry(CAQSvrInst *paqinst) :
  176. m_slPrivateData("CDomainEntry")
  177. {
  178. _ASSERT(paqinst);
  179. TraceFunctEnterEx((LPARAM) this, "CDomainEntry::CDomainEntry");
  180. //Create a mapping that is not compressed
  181. m_dmap.m_pdentryDomainID = this;
  182. m_dmap.m_pdentryQueueID = this;
  183. m_dwSignature = DOMAIN_ENTRY_SIG;
  184. //init pointers
  185. m_szDomainName = NULL;
  186. m_cbDomainName = 0;
  187. InitializeListHead(&m_liDestQueues);
  188. InitializeListHead(&m_liLinks);
  189. m_cLinks = 0;
  190. m_cQueues = 0;
  191. m_paqinst = paqinst;
  192. m_paqinst->AddRef();
  193. TraceFunctLeave();
  194. }
  195. //---[ CDomainEntry::~CDomainEntry() ]-----------------------------------------
  196. //
  197. //
  198. // Description: CDomainEntry destructor
  199. //
  200. // Parameters:
  201. // -
  202. //
  203. // Returns:
  204. // -
  205. //
  206. //-----------------------------------------------------------------------------
  207. CDomainEntry::~CDomainEntry()
  208. {
  209. TraceFunctEnterEx((LPARAM) this, "CDomainEntry::~CDomainEntry");
  210. PLIST_ENTRY pli = NULL; //used to iterate over lists
  211. CDestMsgQueue *pdmq = NULL;
  212. CLinkMsgQueue *plmq = NULL;
  213. //Remove all DestMsgQueues from list
  214. while (!IsListEmpty(&m_liDestQueues))
  215. {
  216. pli = m_liDestQueues.Flink;
  217. _ASSERT((pli != &m_liDestQueues) && "List Macros are broken");
  218. pdmq = CDestMsgQueue::pdmqGetDMQFromDomainListEntry(pli);
  219. pdmq->RemoveQueueFromDomainList();
  220. pdmq->Release();
  221. m_cQueues--;
  222. }
  223. //Remove all links from list
  224. while (!IsListEmpty(&m_liLinks))
  225. {
  226. pli = m_liLinks.Flink;
  227. plmq = CLinkMsgQueue::plmqGetLinkMsgQueue(pli);
  228. plmq->fRemoveLinkFromList();
  229. plmq->Release();
  230. m_cLinks--;
  231. _ASSERT((pli != &m_liLinks) && "List Macros are broken");
  232. }
  233. FreePv(m_szDomainName);
  234. if (m_paqinst)
  235. m_paqinst->Release();
  236. TraceFunctLeave();
  237. }
  238. //---[ CDomainEntry::HrInitialize ]--------------------------------------------
  239. //
  240. //
  241. // Description: Initilizer for CDomainEntry. This should be called BEFORE the
  242. // entry is inserted into the DMT where other threads can access it.
  243. //
  244. // Parameters:
  245. // szDomainName string of domain name for entry, will *NOT* be copied, this
  246. // object will take control of this. This will save a unneeded
  247. // buffer copy and allocation per domain entry
  248. // pdentryQueueID ptr to the primary entry for this domain (usually this)
  249. // pdmq ptr to DestMsgQueue
  250. // plmq ptr to LinkMsgQueue to allocate
  251. // Returns:
  252. // S_OK on success
  253. // E_OUTOFMEMORY if any allocation fails
  254. //
  255. // It is expected that this is only called by the DMT while creating an entry
  256. //-----------------------------------------------------------------------------
  257. HRESULT CDomainEntry::HrInitialize(DWORD cbDomainName, LPSTR szDomainName,
  258. CDomainEntry *pdentryQueueID, CDestMsgQueue *pdmq,
  259. CLinkMsgQueue *plmq)
  260. {
  261. TraceFunctEnterEx((LPARAM) this, "CDomainEntry::HrInitialize");
  262. Assert(szDomainName);
  263. Assert((pdentryQueueID == this) || (pdmq == NULL));
  264. HRESULT hr = S_OK;
  265. m_cbDomainName = cbDomainName;
  266. m_szDomainName = szDomainName;
  267. //write domain mapping
  268. m_dmap.m_pdentryDomainID = this;
  269. m_dmap.m_pdentryQueueID = pdentryQueueID;
  270. //add the queue to our list of queues
  271. if (pdmq)
  272. {
  273. m_slPrivateData.ExclusiveLock();
  274. m_cQueues++;
  275. pdmq->AddRef();
  276. pdmq->InsertQueueInDomainList(&m_liDestQueues);
  277. m_slPrivateData.ExclusiveUnlock();
  278. }
  279. if (plmq)
  280. {
  281. m_slPrivateData.ExclusiveLock();
  282. m_cLinks++;
  283. plmq->AddRef();
  284. plmq->InsertLinkInList(&m_liLinks);
  285. m_slPrivateData.ExclusiveUnlock();
  286. }
  287. TraceFunctLeave();
  288. return hr;
  289. }
  290. //---[ CDomainEntry::HrDeinitialize ]------------------------------------------
  291. //
  292. //
  293. // Description: Deinitializer for CDomainEntry
  294. //
  295. // Parameters:
  296. // -
  297. // Returns:
  298. // S_OK on success
  299. //
  300. //-----------------------------------------------------------------------------
  301. HRESULT CDomainEntry::HrDeinitialize()
  302. {
  303. TraceFunctEnterEx((LPARAM) this, "CDomainEntry::HrDeinitialize");
  304. HRESULT hr = S_OK;
  305. PLIST_ENTRY pli = NULL; //used to iterate over lists
  306. CDestMsgQueue *pdmq = NULL;
  307. CLinkMsgQueue *plmq = NULL;
  308. m_slPrivateData.ExclusiveLock();
  309. while (!IsListEmpty(&m_liDestQueues))
  310. {
  311. pli = m_liDestQueues.Flink;
  312. _ASSERT((pli != &m_liDestQueues) && "List Macros are broken");
  313. pdmq = CDestMsgQueue::pdmqGetDMQFromDomainListEntry(pli);
  314. pdmq->HrDeinitialize();
  315. pdmq->RemoveQueueFromDomainList();
  316. pdmq->Release();
  317. m_cQueues--;
  318. pdmq = NULL;
  319. }
  320. //Remove all links from list
  321. while (!IsListEmpty(&m_liLinks))
  322. {
  323. pli = m_liLinks.Flink;
  324. plmq = CLinkMsgQueue::plmqGetLinkMsgQueue(pli);
  325. plmq->HrDeinitialize();
  326. plmq->fRemoveLinkFromList();
  327. plmq->Release();
  328. m_cLinks--;
  329. _ASSERT((pli != &m_liLinks) && "List Macros are broken");
  330. }
  331. if (m_paqinst)
  332. {
  333. m_paqinst->Release();
  334. m_paqinst = NULL;
  335. }
  336. m_slPrivateData.ExclusiveUnlock();
  337. TraceFunctLeave();
  338. return hr;
  339. }
  340. //---[ CDomainEntry::HrGetDomainMapping ]--------------------------------------
  341. //
  342. //
  343. // Description: Returns Domain Mapping for this object
  344. //
  345. // Parameters:
  346. // OUT pdmap CDomainMapping for return information
  347. //
  348. // Returns:
  349. // S_OK on success
  350. //-----------------------------------------------------------------------------
  351. HRESULT CDomainEntry::HrGetDomainMapping(OUT CDomainMapping *pdmap)
  352. {
  353. TraceFunctEnterEx((LPARAM) this, "CDomainEntry::HrGetDomainMapping");
  354. HRESULT hr = S_OK;
  355. _ASSERT(pdmap);
  356. pdmap->Clone(&m_dmap);
  357. TraceFunctLeave();
  358. return S_OK;
  359. }
  360. //---[ CDomainEntry::HrGetDomainName ]----------------------------------------------
  361. //
  362. //
  363. // Description: Copies Domain Name. Caller is responsible for freeing string
  364. //
  365. // Parameters:
  366. // OUT pszDomainName string of domain name for entry, will be copied
  367. //
  368. // Returns:
  369. // S_OK on success
  370. // E_OUTOFMEMORY if any allocation fails
  371. //
  372. //
  373. //-----------------------------------------------------------------------------
  374. HRESULT CDomainEntry::HrGetDomainName(OUT LPSTR *pszDomainName)
  375. {
  376. TraceFunctEnterEx((LPARAM) this, "CDomainEntry::HrGetDomainName");
  377. HRESULT hr = S_OK;
  378. Assert(pszDomainName);
  379. if (m_szDomainName == NULL)
  380. {
  381. *pszDomainName = NULL;
  382. goto Exit;
  383. }
  384. //Copy domain name
  385. *pszDomainName = (LPSTR) pvMalloc(m_cbDomainName + sizeof(CHAR));
  386. if (*pszDomainName == NULL)
  387. {
  388. hr = E_OUTOFMEMORY;
  389. goto Exit;
  390. }
  391. strcpy(*pszDomainName, m_szDomainName);
  392. Exit:
  393. TraceFunctLeave();
  394. return hr;
  395. }
  396. //---[ CDomainEntry::HrGetDestMsgQueue ]---------------------------------------
  397. //
  398. //
  399. // Description: Returns a pointer to the queue that this entry points to
  400. //
  401. // Parameters:
  402. // IN paqmt Message Type to get domain for
  403. // OUT ppdmq pointer returned
  404. //
  405. // Returns:
  406. // S_OK on success
  407. // E_FAIL no queue matching message type is found
  408. //
  409. //-----------------------------------------------------------------------------
  410. HRESULT CDomainEntry::HrGetDestMsgQueue(IN CAQMessageType *paqmt,
  411. OUT CDestMsgQueue **ppdmq)
  412. {
  413. TraceFunctEnterEx((LPARAM) this, "CDomainEntry::HrGetDestMsgQueue");
  414. HRESULT hr = S_OK;
  415. PLIST_ENTRY pli = NULL;
  416. CDestMsgQueue *pdmq = NULL;
  417. _ASSERT(ppdmq);
  418. _ASSERT(m_dmap.m_pdentryDomainID == this);
  419. DEBUG_DO_IT(DWORD cQueues = 0);
  420. if (m_dmap.m_pdentryQueueID == m_dmap.m_pdentryDomainID)
  421. {
  422. //this must be the primary entry... scan our own list of queues
  423. m_slPrivateData.ShareLock();
  424. pli = m_liDestQueues.Flink;
  425. while (pli != &m_liDestQueues)
  426. {
  427. _ASSERT(m_cQueues >= cQueues);
  428. DEBUG_DO_IT(cQueues++);
  429. pdmq = CDestMsgQueue::pdmqIsSameMessageType(paqmt, pli);
  430. if (pdmq)
  431. break;
  432. pli = pli->Flink;
  433. }
  434. m_slPrivateData.ShareUnlock();
  435. if (!pdmq)
  436. hr = E_FAIL; //no such queue
  437. else
  438. {
  439. pdmq->AddRef();
  440. *ppdmq = pdmq;
  441. }
  442. }
  443. else
  444. {
  445. //we are not primary
  446. _ASSERT(0 && "Non-primary domain entry... currently only primary entries are supported");
  447. _ASSERT(IsListEmpty(&m_liDestQueues)); //make sure it matches the profile
  448. hr = m_dmap.m_pdentryQueueID->HrGetDestMsgQueue(paqmt, ppdmq);
  449. }
  450. TraceFunctLeave();
  451. return hr;
  452. }
  453. //---[ CDomainEntry::HrAddUniqueDestMsgQueue ]---------------------------------
  454. //
  455. //
  456. // Description:
  457. // Adds a queue to this entry's list of queues if a queue with the same
  458. // message type does not already exist.
  459. //
  460. // Will appropriately AddRef domain.
  461. // Parameters:
  462. // IN pdmqNew - CDestMsgQueue to add
  463. // OUT ppdmqCurrent - Set to curent CDestMsgQueue on failure
  464. // Returns:
  465. // S_OK on success
  466. // E_FAIL if a CDestMsgQueue with same Message type alread exists.
  467. // History:
  468. // 5/28/98 - MikeSwa Created
  469. // 9/8/98 - MikeSwa Modified to use AddRef/Relase for queues
  470. //
  471. //-----------------------------------------------------------------------------
  472. HRESULT CDomainEntry::HrAddUniqueDestMsgQueue(IN CDestMsgQueue *pdmqNew,
  473. OUT CDestMsgQueue **ppdmqCurrent)
  474. {
  475. _ASSERT(pdmqNew);
  476. _ASSERT(ppdmqCurrent);
  477. HRESULT hr = S_OK;
  478. PLIST_ENTRY pli = NULL;
  479. CDestMsgQueue *pdmq = NULL;
  480. CAQMessageType *paqmt = pdmqNew->paqmtGetMessageType();
  481. DEBUG_DO_IT(DWORD cQueues = 0);
  482. *ppdmqCurrent = NULL;
  483. m_slPrivateData.ExclusiveLock();
  484. pli = m_liDestQueues.Flink;
  485. //First look through list and make sure that there isn't already a
  486. //queue with this message type
  487. while (pli != &m_liDestQueues)
  488. {
  489. _ASSERT(m_cQueues >= cQueues);
  490. pdmq = CDestMsgQueue::pdmqIsSameMessageType(paqmt, pli);
  491. if (pdmq)
  492. {
  493. hr = E_FAIL;
  494. pdmq->AddRef();
  495. *ppdmqCurrent = pdmq;
  496. goto Exit;
  497. }
  498. DEBUG_DO_IT(cQueues++);
  499. pli = pli->Flink;
  500. }
  501. pdmqNew->AddRef();
  502. pdmqNew->InsertQueueInDomainList(&m_liDestQueues);
  503. m_cQueues++;
  504. Exit:
  505. m_slPrivateData.ExclusiveUnlock();
  506. return hr;
  507. }
  508. //---[ CDomainEntry::HrGetLinkMsgQueue ]---------------------------------------
  509. //
  510. //
  511. // Description:
  512. // Gets a link for the given schedule id
  513. // Parameters:
  514. // IN paqsched - ScheduleID to search for
  515. // OUT pplmq - returned queue
  516. // Returns:
  517. // S_OK on success
  518. // E_FAIL if no link matching the schudule ID can be found
  519. // History:
  520. // 6/11/98 - MikeSwa Created
  521. //
  522. //-----------------------------------------------------------------------------
  523. HRESULT CDomainEntry::HrGetLinkMsgQueue(IN CAQScheduleID *paqsched,
  524. OUT CLinkMsgQueue **pplmq)
  525. {
  526. TraceFunctEnterEx((LPARAM) this, "CDomainEntry::HrGetLinkMsgQueue");
  527. HRESULT hr = S_OK;
  528. PLIST_ENTRY pli = NULL;
  529. CLinkMsgQueue *plmq = NULL;
  530. _ASSERT(pplmq);
  531. _ASSERT(m_dmap.m_pdentryDomainID == this);
  532. DEBUG_DO_IT(DWORD cLinks = 0);
  533. m_slPrivateData.ShareLock();
  534. pli = m_liLinks.Flink;
  535. while (pli != &m_liLinks)
  536. {
  537. _ASSERT(m_cLinks >= cLinks);
  538. DEBUG_DO_IT(cLinks++);
  539. plmq = CLinkMsgQueue::plmqIsSameScheduleID(paqsched, pli);
  540. if (plmq)
  541. {
  542. plmq->AddRef();
  543. break;
  544. }
  545. pli = pli->Flink;
  546. }
  547. m_slPrivateData.ShareUnlock();
  548. if (!plmq)
  549. hr = E_FAIL; //no such queue
  550. else
  551. *pplmq = plmq;
  552. TraceFunctLeave();
  553. return hr;
  554. }
  555. //---[ CDomainEntry::HrAddUniqueLinkMsgQueue ]---------------------------------
  556. //
  557. //
  558. // Description:
  559. // Inserts a link with a unique schedule ID
  560. // Parameters:
  561. // IN plmqNew New link to insert
  562. // OUT plmqCurrent Current link with schedule ID on insert failure
  563. // Returns:
  564. // S_OK if insert succeeds
  565. // E_FAIL if insert fails
  566. // History:
  567. // 6/11/98 - MikeSwa Created
  568. //
  569. //-----------------------------------------------------------------------------
  570. HRESULT CDomainEntry::HrAddUniqueLinkMsgQueue(IN CLinkMsgQueue *plmqNew,
  571. OUT CLinkMsgQueue **pplmqCurrent)
  572. {
  573. _ASSERT(plmqNew);
  574. _ASSERT(pplmqCurrent);
  575. HRESULT hr = S_OK;
  576. PLIST_ENTRY pli = NULL;
  577. CLinkMsgQueue *plmq = NULL;
  578. CAQScheduleID *paqsched = plmqNew->paqschedGetScheduleID();
  579. DEBUG_DO_IT(DWORD cLinks = 0);
  580. *pplmqCurrent = NULL;
  581. m_slPrivateData.ExclusiveLock();
  582. pli = m_liLinks.Flink;
  583. //First look through list and make sure that there isn't already a
  584. //queue with this schedule ID
  585. while (pli != &m_liLinks)
  586. {
  587. _ASSERT(m_cLinks >= cLinks);
  588. plmq = CLinkMsgQueue::plmqIsSameScheduleID(paqsched, pli);
  589. if (plmq)
  590. {
  591. hr = E_FAIL;
  592. *pplmqCurrent = plmq;
  593. plmq->AddRef();
  594. goto Exit;
  595. }
  596. DEBUG_DO_IT(cLinks++);
  597. pli = pli->Flink;
  598. }
  599. plmqNew->InsertLinkInList(&m_liLinks);
  600. plmqNew->AddRef();
  601. m_cLinks++;
  602. Exit:
  603. m_slPrivateData.ExclusiveUnlock();
  604. return hr;
  605. }
  606. //---[ CDomainEntry::RemoveDestMsgQueue ]--------------------------------------
  607. //
  608. //
  609. // Description:
  610. // Removes empty DMQ from entry.
  611. // Parameters:
  612. // IN pdmq DMQ to remove from domain entry
  613. // Returns:
  614. // -
  615. // History:
  616. // 9/14/98 - MikeSwa Created
  617. //
  618. //-----------------------------------------------------------------------------
  619. void CDomainEntry::RemoveDestMsgQueue(IN CDestMsgQueue *pdmq)
  620. {
  621. _ASSERT(pdmq && "INVALID Param for internal function");
  622. m_slPrivateData.ExclusiveLock();
  623. pdmq->RemoveQueueFromDomainList();
  624. pdmq->HrDeinitialize();
  625. pdmq->Release();
  626. m_cQueues--;
  627. m_slPrivateData.ExclusiveUnlock();
  628. }
  629. //---[ CDomainEntry::RemoveLinkMsgQueue ]--------------------------------------
  630. //
  631. //
  632. // Description:
  633. // Removes an empty LinkMsgQueue from the domain entry
  634. // Parameters:
  635. // IN plmq Link to remove
  636. // Returns:
  637. // -
  638. // History:
  639. // 9/14/98 - MikeSwa Created
  640. //
  641. //-----------------------------------------------------------------------------
  642. void CDomainEntry::RemoveLinkMsgQueue(IN CLinkMsgQueue *plmq)
  643. {
  644. _ASSERT(plmq && "INVALID Param for internal function");
  645. m_slPrivateData.ExclusiveLock();
  646. BOOL fRemove = plmq->fRemoveLinkFromList();
  647. if (fRemove)
  648. m_cLinks--;
  649. m_slPrivateData.ExclusiveUnlock();
  650. if (fRemove) {
  651. //do *NOT* call HrDeinitialize here since it will deadlock
  652. plmq->RemovedFromDMT();
  653. plmq->Release();
  654. }
  655. }
  656. //***[ CDomainMappingTable Methods ]*******************************************
  657. //---[ CDomainMappingTable::CDomainMappingTable ]------------------------------
  658. //
  659. //
  660. // Description: CDomainMappingTable constructor
  661. //
  662. // Parameters: -
  663. //
  664. // Returns: -
  665. //
  666. //
  667. //-----------------------------------------------------------------------------
  668. CDomainMappingTable::CDomainMappingTable() :
  669. m_slPrivateData("CDomainMappingTable")
  670. {
  671. TraceFunctEnterEx((LPARAM) this, "CDomainMappingTable::CDomainMappingTable");
  672. m_paqinst = NULL;
  673. m_dwSignature = DOMAIN_MAPPING_TABLE_SIG;
  674. m_dwInternalVersion = 0;
  675. m_cOutstandingExternalShareLocks = 0;
  676. m_cThreadsForEmptyDMQList = 0;
  677. m_plmqLocal = NULL;
  678. m_plmqCurrentlyUnreachable = NULL;
  679. m_plmqUnreachable = NULL;
  680. m_pmmaqPreCategorized = NULL;
  681. m_pmmaqPreRouting = NULL;
  682. m_cSpecialRetryMinutes = 0;
  683. m_cResetRoutesRetriesPending = 0;
  684. m_dwFlags = 0;
  685. InitializeListHead(&m_liEmptyDMQHead);
  686. TraceFunctLeave();
  687. }
  688. //---[ CDomainMappingTable::~CDomainMappingTable ]------------------------------------------------------------
  689. //
  690. //
  691. // Description: CDomainMappingTable destructor
  692. //
  693. // Parameters: -
  694. //
  695. // Returns: -
  696. //
  697. //
  698. //-----------------------------------------------------------------------------
  699. CDomainMappingTable::~CDomainMappingTable()
  700. {
  701. TraceFunctEnterEx((LPARAM) this, "CDomainMappingTable::~CDomainMappingTable");
  702. //Remove everything from the table
  703. m_dnt.HrIterateOverSubDomains(NULL, ReleaseDomainEntryIteratorFn, NULL);
  704. if (m_paqinst)
  705. {
  706. m_paqinst->Release();
  707. m_paqinst = NULL;
  708. }
  709. if (m_plmqLocal)
  710. m_plmqLocal->Release();
  711. if (m_plmqCurrentlyUnreachable)
  712. m_plmqCurrentlyUnreachable->Release();
  713. if (m_plmqUnreachable)
  714. m_plmqUnreachable->Release();
  715. if (m_pmmaqPreCategorized)
  716. m_pmmaqPreCategorized->Release();
  717. if (m_pmmaqPreRouting)
  718. m_pmmaqPreRouting->Release();
  719. if (m_pmmaqPreSubmission)
  720. m_pmmaqPreSubmission->Release();
  721. _ASSERT(!m_cOutstandingExternalShareLocks); //there should be no outstanding sharelocks
  722. TraceFunctLeave();
  723. }
  724. //---[ CDomainMappingTable::HrInitialize ]-------------------------------------
  725. //
  726. //
  727. // Description: Performs initialization that may return an error code
  728. //
  729. // Parameters:
  730. // IN paqinst AQ Svr Inst
  731. // IN paradmq Local Async Queue (passed to local link as part
  732. // Returns: S_OK on success
  733. //
  734. //
  735. //-----------------------------------------------------------------------------
  736. HRESULT CDomainMappingTable::HrInitialize(CAQSvrInst *paqinst,
  737. CAsyncAdminMsgRefQueue *paradmq,
  738. CAsyncAdminMailMsgQueue *pammqPreCatQ,
  739. CAsyncAdminMailMsgQueue *pammqPreRoutingQ,
  740. CAsyncAdminMailMsgQueue *pammqPreSubmissionQ)
  741. {
  742. TraceFunctEnterEx((LPARAM) this, "CDomainMappingTable::HrInitialize");
  743. HRESULT hr = S_OK;
  744. HRESULT hrCurrent = S_OK;
  745. _ASSERT(paqinst);
  746. m_paqinst = paqinst;
  747. m_paqinst->AddRef();
  748. //------Link for local Queue-----------------------------------------------
  749. m_plmqLocal = new CLocalLinkMsgQueue(paradmq, g_sGuidLocalLink, m_paqinst);
  750. if (!m_plmqLocal)
  751. hr = E_OUTOFMEMORY;
  752. hrCurrent = HrInializeGlobalLink(LOCAL_LINK_NAME,
  753. sizeof(LOCAL_LINK_NAME) - sizeof(CHAR),
  754. (CLinkMsgQueue **) &m_plmqLocal,
  755. LA_KICK | LA_FREEZE | LA_THAW,
  756. LI_TYPE_LOCAL_DELIVERY);
  757. if (FAILED(hrCurrent) && SUCCEEDED(hr))
  758. hr = hrCurrent;
  759. //------Link for currently unreachable Queue-------------------------------
  760. hrCurrent = HrInializeGlobalLink(CURRENTLY_UNREACHABLE_LINK_NAME,
  761. sizeof(CURRENTLY_UNREACHABLE_LINK_NAME) - sizeof(CHAR),
  762. &m_plmqCurrentlyUnreachable,
  763. 0,
  764. LI_TYPE_CURRENTLY_UNREACHABLE);
  765. if (FAILED(hrCurrent) && SUCCEEDED(hr))
  766. hr = hrCurrent;
  767. hrCurrent = HrInializeGlobalLink(UNREACHABLE_LINK_NAME,
  768. sizeof(UNREACHABLE_LINK_NAME) - sizeof(CHAR),
  769. &m_plmqUnreachable,
  770. 0,
  771. LI_TYPE_INTERNAL);
  772. if (FAILED(hrCurrent) && SUCCEEDED(hr))
  773. hr = hrCurrent;
  774. //-------Link for precat Queue---------------------------------------------
  775. m_pmmaqPreCategorized = new CMailMsgAdminLink(g_sGuidPrecatLink, PRECAT_QUEUE_NAME,
  776. pammqPreCatQ, LI_TYPE_PENDING_CAT,
  777. m_paqinst);
  778. if (!m_pmmaqPreCategorized)
  779. hr = E_OUTOFMEMORY;
  780. if (FAILED(hrCurrent) && SUCCEEDED(hr))
  781. hr = hrCurrent;
  782. //-------Link for prerouting Queue-----------------------------------------
  783. m_pmmaqPreRouting = new CMailMsgAdminLink(g_sGuidPreRoutingLink, PREROUTING_QUEUE_NAME,
  784. pammqPreRoutingQ, LI_TYPE_PENDING_ROUTING,
  785. m_paqinst);
  786. if (!m_pmmaqPreRouting)
  787. hr = E_OUTOFMEMORY;
  788. if (FAILED(hrCurrent) && SUCCEEDED(hr))
  789. hr = hrCurrent;
  790. //-------Link for presubmissionQueue--------------------------------------
  791. m_pmmaqPreSubmission = new CMailMsgAdminLink(g_sGuidPreSubmissionLink,
  792. PRESUBMISSION_QUEUE_NAME,
  793. pammqPreSubmissionQ, LI_TYPE_PENDING_SUBMIT,
  794. m_paqinst);
  795. if (!m_pmmaqPreSubmission)
  796. hr = E_OUTOFMEMORY;
  797. if (FAILED(hrCurrent) && SUCCEEDED(hr))
  798. hr = hrCurrent;
  799. //-------------------------------------------------------------------------
  800. hrCurrent = m_dnt.HrInit();
  801. if (FAILED(hrCurrent) && SUCCEEDED(hr))
  802. hr = hrCurrent;
  803. TraceFunctLeave();
  804. return hr;
  805. }
  806. //---[ CDomainMappingTable::HrDeinitialize ]-----------------------------------
  807. //
  808. //
  809. // Description: Performs cleanup that may return an error code
  810. //
  811. // Parameters: -
  812. //
  813. // Returns: S_OK on success
  814. //
  815. //
  816. //-----------------------------------------------------------------------------
  817. HRESULT CDomainMappingTable::HrDeinitialize()
  818. {
  819. TraceFunctEnterEx((LPARAM) this, "CDomainMappingTable::HrDeinitialize");
  820. HRESULT hr = S_OK;
  821. HRESULT hrCurrent = S_OK;
  822. hr = m_dnt.HrIterateOverSubDomains(NULL, DeinitDomainEntryIteratorFn, NULL);
  823. //Deinitialize global special links
  824. hrCurrent = HrDeinitializeGlobalLink((CLinkMsgQueue **) &m_plmqLocal);
  825. if (FAILED(hrCurrent) && SUCCEEDED(hr))
  826. hr = hrCurrent;
  827. hrCurrent = HrDeinitializeGlobalLink(&m_plmqCurrentlyUnreachable);
  828. if (FAILED(hrCurrent) && SUCCEEDED(hr))
  829. hr = hrCurrent;
  830. hrCurrent = HrDeinitializeGlobalLink(&m_plmqUnreachable);
  831. if (FAILED(hrCurrent) && SUCCEEDED(hr))
  832. hr = hrCurrent;
  833. //NOTE: This *must* come after Deinitialize of entries
  834. if (m_paqinst)
  835. {
  836. m_paqinst->Release();
  837. m_paqinst = NULL;
  838. }
  839. TraceFunctLeave();
  840. return hr;
  841. }
  842. //---[ <CDomainMappingTable::HrInializeGlobalLink ]-----------------------------
  843. //
  844. //
  845. // Description:
  846. // Initializes a single global link for the DMT. Configures link to not
  847. // send notifications to the connection manager, and to
  848. // Parameters:
  849. // IN szLinkName The link name to use for the link
  850. // IN cbLinkName The string length of the link name
  851. // OUT pplmq Link to allocate/initialize
  852. // IN dwSupportedActions Bitmask specifying what actions are supported on this link
  853. // IN dwLinkType Link type to be returned to admin (LI_TYPE)
  854. // Returns:
  855. //
  856. // History:
  857. // 1/27/99 - MikeSwa Created
  858. //
  859. //-----------------------------------------------------------------------------
  860. HRESULT CDomainMappingTable::HrInializeGlobalLink(IN LPCSTR szLinkName,
  861. IN DWORD cbLinkName,
  862. OUT CLinkMsgQueue **pplmq,
  863. DWORD dwSupportedActions,
  864. DWORD dwLinkType)
  865. {
  866. HRESULT hr = S_OK;
  867. _ASSERT(pplmq);
  868. if (!*pplmq)
  869. *pplmq = new CLinkMsgQueue();
  870. if (*pplmq)
  871. {
  872. //Initialize local queue
  873. hr = (*pplmq)->HrInitialize(m_paqinst, NULL, cbLinkName,
  874. (LPSTR) szLinkName,
  875. eLinkFlagsAQSpecialLinkInfo, NULL);
  876. //Set flags so no connections will be made for this link
  877. (*pplmq)->dwModifyLinkState( LINK_STATE_PRIV_NO_NOTIFY |
  878. LINK_STATE_PRIV_NO_CONNECTION,
  879. LINK_STATE_NO_ACTION );
  880. (*pplmq)->SetSupportedActions(dwSupportedActions);
  881. (*pplmq)->SetLinkType(dwLinkType);
  882. }
  883. else
  884. hr = E_OUTOFMEMORY;
  885. return hr;
  886. }
  887. //---[ CDomainMappingTable::HrDeinitializeGlobalLink ]-------------------------
  888. //
  889. //
  890. // Description:
  891. // Deinitializes a single global link for the DMT
  892. // Parameters:
  893. // IN OUT pplmq Link to deinitialze / set to NULL
  894. // Returns:
  895. // S_OK on success
  896. // ERROR code from CLinkMsgQueue::HrDeinitialize();
  897. // History:
  898. // 1/27/99 - MikeSwa Created
  899. // 7/21/99 - MikeSwa Modified - removed free of link domain
  900. //
  901. //-----------------------------------------------------------------------------
  902. HRESULT CDomainMappingTable::HrDeinitializeGlobalLink(IN OUT CLinkMsgQueue **pplmq)
  903. {
  904. HRESULT hr = S_OK;
  905. _ASSERT(pplmq);
  906. if (pplmq && *pplmq)
  907. {
  908. hr = (*pplmq)->HrDeinitialize();
  909. (*pplmq)->Release();
  910. *pplmq = NULL;
  911. }
  912. return hr;
  913. }
  914. //---[ CDomainMappingTable::HrMapDomainName ]----------------------------------
  915. //
  916. //
  917. // Description:
  918. // Looks up a DomainName in the DMT. Will create a new entry if necessary
  919. //
  920. // Parameters:
  921. // IN szDomainName Domain Name to map
  922. // IN paqmtMessageType Message type as returned by routing
  923. // IN pIMessageRouter IMessageRouter for this message
  924. // OUT pdmap Mapping returned - allocated by caller
  925. // OUT ppdmq ptr to Queue
  926. // Returns:
  927. // S_OK on success
  928. // E_OUTOFMEMORY if an allocation fails
  929. // HRESULT_FROM_WIN32(ERROR_RETRY) if mapping data changes and entire
  930. // message should be re-mapped
  931. //
  932. //-----------------------------------------------------------------------------
  933. HRESULT CDomainMappingTable::HrMapDomainName(
  934. IN LPSTR szDomainName,
  935. IN CAQMessageType *paqmtMessageType,
  936. IN IMessageRouter *pIMessageRouter,
  937. OUT CDomainMapping *pdmap,
  938. OUT CDestMsgQueue **ppdmq)
  939. {
  940. TraceFunctEnterEx((LPARAM) this, "CDomainMappingTable::HrMapDomainName");
  941. _ASSERT(pdmap);
  942. _ASSERT(ppdmq);
  943. _ASSERT(szDomainName);
  944. _ASSERT(szDomainName[0] && "unsupported config - RAID #68208");
  945. _ASSERT(pIMessageRouter);
  946. HRESULT hr = S_OK;
  947. CDomainEntry *pdentryResult = NULL;
  948. CDomainEntry *pdentryExisting = NULL;
  949. DWORD cbDomainName = 0;
  950. CInternalDomainInfo *pIntDomainInfo= NULL;
  951. BOOL fLocal = FALSE; //Is delivery local?
  952. BOOL fWalkEmptyList= FALSE;
  953. DOMAIN_STRING strDomain; //allows quicker lookups/inserts
  954. CLinkMsgQueue *plmq = NULL;
  955. *ppdmq = NULL;
  956. cbDomainName = strlen(szDomainName)*sizeof(CHAR);
  957. INIT_DOMAIN_STRING(strDomain, cbDomainName, szDomainName);
  958. m_slPrivateData.ShareLock();
  959. hr = m_dnt.HrFindDomainName(&strDomain, (PVOID *) &pdentryResult);
  960. //
  961. // If succeeded aquire usage lock before we give up DMT lock.
  962. // Handle failure cases after releasing lock.
  963. //
  964. if (SUCCEEDED(hr))
  965. {
  966. pdentryResult->AddRef();
  967. }
  968. fWalkEmptyList = fNeedToWalkEmptyQueueList();
  969. m_slPrivateData.ShareUnlock();
  970. //
  971. // Check and see if we need to delete empty queues.
  972. //
  973. if (fWalkEmptyList)
  974. {
  975. if (fDeleteExpiredQueues())
  976. {
  977. //something has changes
  978. hr = HRESULT_FROM_WIN32(ERROR_RETRY);
  979. goto Exit;
  980. }
  981. }
  982. if (hr == DOMHASH_E_NO_SUCH_DOMAIN) //gotta create a new entry
  983. {
  984. DebugTrace((LPARAM) this, "Creating new DMT entry");
  985. pdentryResult = new CDomainEntry(m_paqinst);
  986. if (NULL == pdentryResult)
  987. {
  988. hr = E_OUTOFMEMORY;
  989. goto Exit;
  990. }
  991. _ASSERT(m_paqinst);
  992. hr = m_paqinst->HrGetInternalDomainInfo(cbDomainName, szDomainName, &pIntDomainInfo);
  993. if (FAILED(hr))
  994. {
  995. //It must match the "*" domain at least
  996. _ASSERT(AQUEUE_E_INVALID_DOMAIN != hr);
  997. goto Exit;
  998. }
  999. else
  1000. {
  1001. _ASSERT(pIntDomainInfo);
  1002. if (pIntDomainInfo->m_DomainInfo.dwDomainInfoFlags &
  1003. DOMAIN_INFO_LOCAL_MAILBOX)
  1004. {
  1005. DebugTrace((LPARAM) NULL, "INFO: Local delivery queued.");
  1006. fLocal = TRUE;
  1007. }
  1008. }
  1009. //perform initialization of domain entry... create queues if needed
  1010. if (fLocal)
  1011. {
  1012. hr = HrInitLocalDomain(pdentryResult, &strDomain,
  1013. paqmtMessageType, pdmap);
  1014. }
  1015. else
  1016. {
  1017. hr = HrInitRemoteDomain(pdentryResult, &strDomain, pIntDomainInfo,
  1018. paqmtMessageType, pIMessageRouter, pdmap, ppdmq, &plmq);
  1019. }
  1020. if (FAILED(hr))
  1021. {
  1022. ErrorTrace((LPARAM) this, "ERROR: Initializing %s domain %s - hr 0x%08X",
  1023. (fLocal ? "local" : "remote"), szDomainName, hr);
  1024. goto Exit;
  1025. }
  1026. m_slPrivateData.ExclusiveLock();
  1027. hr = HrPrvInsertDomainEntry(&strDomain, pdentryResult, FALSE, &pdentryExisting);
  1028. //Need to release exclusive lock in if/else clause
  1029. if (SUCCEEDED(hr)) //the insertion succeeded
  1030. {
  1031. pdentryResult->AddRef();
  1032. m_slPrivateData.ExclusiveUnlock();
  1033. DebugTrace((LPARAM) szDomainName, "INFO: Creating new entry in DMT for domain %s", szDomainName);
  1034. _ASSERT((fLocal || *ppdmq) && "Out param should be set here!"); //skip past getting value from table
  1035. if (!fLocal)
  1036. {
  1037. hr = plmq->HrAddQueue(*ppdmq);
  1038. if (FAILED(hr))
  1039. {
  1040. // Remove DMQ from link w/o notify since we never added it
  1041. (*ppdmq)->RemoveDMQFromLink(FALSE);
  1042. // Release the link we couldn't use and get the
  1043. // currently unreachable link
  1044. plmq->Release();
  1045. plmq = plmqGetCurrentlyUnreachable();
  1046. if (plmq)
  1047. {
  1048. (*ppdmq)->SetRouteInfo(plmq);
  1049. hr = plmq->HrAddQueue(*ppdmq);
  1050. if (FAILED(hr))
  1051. (*ppdmq)->RemoveDMQFromLink(FALSE);
  1052. }
  1053. // Schedule a reset routes to clean up the currently
  1054. // unreachable queue
  1055. m_paqinst->ResetRoutes(RESET_NEXT_HOPS);
  1056. }
  1057. }
  1058. goto Exit;
  1059. }
  1060. else if (DOMHASH_E_DOMAIN_EXISTS == hr) //another inserted first
  1061. {
  1062. hr = S_OK; //not really a failure
  1063. DebugTrace((LPARAM) this, "Another thread inserted in the the DMT before us");
  1064. pdentryExisting->AddRef();
  1065. m_slPrivateData.ExclusiveUnlock();
  1066. _ASSERT(pdentryExisting != pdentryResult);
  1067. //release entry that we could not insert and replace with entry currently
  1068. //in the table
  1069. pdentryResult->HrDeinitialize();
  1070. pdentryResult->Release();
  1071. pdentryResult = NULL;
  1072. pdentryResult = pdentryExisting;
  1073. //Release queue if we have one
  1074. if (*ppdmq)
  1075. {
  1076. (*ppdmq)->Release();
  1077. *ppdmq = NULL;
  1078. }
  1079. }
  1080. else
  1081. {
  1082. m_slPrivateData.ExclusiveUnlock();
  1083. //general failure to insert
  1084. //We must deinitialize the entry to force it to release any
  1085. //queues and links associated with it.
  1086. pdentryResult->HrDeinitialize();
  1087. goto Exit;
  1088. }
  1089. }
  1090. if (!*ppdmq & !fLocal) //we did not create entry in the table
  1091. {
  1092. _ASSERT(pdentryResult);
  1093. //
  1094. // Prefix wants us to to more than assert.
  1095. // If HrFindDomainName() fails silently or
  1096. // fails with an error other than AQUEUE_E_INVALID_DOMAIN,
  1097. // will will hit this code path
  1098. //
  1099. if (!pdentryResult)
  1100. {
  1101. //
  1102. // Make sure HR is set.
  1103. //
  1104. if (SUCCEEDED(hr))
  1105. hr = E_FAIL;
  1106. goto Exit;
  1107. }
  1108. //Domain Name already exists in table
  1109. //At this point, we need to pull an existing entry from the mapping
  1110. //get domain mapping
  1111. hr = pdentryResult->HrGetDomainMapping(pdmap);
  1112. if (FAILED(hr))
  1113. goto Exit;
  1114. //get queue
  1115. hr = pdentryResult->HrGetDestMsgQueue(paqmtMessageType, ppdmq);
  1116. if (FAILED(hr))
  1117. {
  1118. //entry exists, but no queue for our message type
  1119. _ASSERT(NULL == *ppdmq); //cannot fail if we create queue
  1120. //$$TODO cache domain config on entry
  1121. if (!pIntDomainInfo)
  1122. {
  1123. hr = m_paqinst->HrGetInternalDomainInfo(cbDomainName, szDomainName,
  1124. &pIntDomainInfo);
  1125. if (FAILED(hr))
  1126. {
  1127. //It must match the "*" domain at least
  1128. _ASSERT(AQUEUE_E_INVALID_DOMAIN != hr);
  1129. goto Exit;
  1130. }
  1131. }
  1132. _ASSERT(pIntDomainInfo);
  1133. if (!(pIntDomainInfo->m_DomainInfo.dwDomainInfoFlags &
  1134. DOMAIN_INFO_LOCAL_MAILBOX))
  1135. {
  1136. //this is a not a local domain entry
  1137. hr = HrCreateQueueForEntry(pdentryResult, &strDomain, pIntDomainInfo,
  1138. paqmtMessageType, pIMessageRouter, pdmap, ppdmq);
  1139. if (FAILED(hr))
  1140. goto Exit;
  1141. }
  1142. else
  1143. {
  1144. fLocal = TRUE;
  1145. }
  1146. }
  1147. }
  1148. _ASSERT((*ppdmq || fLocal) && "Non-local domains must have queue ptrs!");
  1149. Exit:
  1150. if (FAILED(hr)) //cleanup
  1151. {
  1152. if (pdentryResult)
  1153. {
  1154. pdentryResult->Release();
  1155. }
  1156. if (*ppdmq)
  1157. {
  1158. (*ppdmq)->Release();
  1159. *ppdmq = NULL;
  1160. }
  1161. }
  1162. else
  1163. {
  1164. if (*ppdmq) {
  1165. // send link state notification saying that the link has
  1166. // been created
  1167. (*ppdmq)->SendLinkStateNotification();
  1168. }
  1169. if (pdentryResult) pdentryResult->Release();
  1170. }
  1171. if (plmq)
  1172. plmq->Release();
  1173. if (pIntDomainInfo)
  1174. pIntDomainInfo->Release();
  1175. TraceFunctLeave();
  1176. return hr;
  1177. }
  1178. //---[ CDomainMappingTable::HrPrvGetDomainEntry ]------------------------------
  1179. //
  1180. //
  1181. // Description:
  1182. // Internal private function to lookup Domain entry for given domain
  1183. // Parameters:
  1184. // IN cbDomainnameLength Length of string to search for
  1185. // IN szDomainName Domain Name to search for
  1186. // IN fDMTLocked TRUE if locks are already
  1187. // OUT ppdentry Domain Entry for domain (from DMT)
  1188. // Returns:
  1189. // S_OK on success
  1190. // AQUEUE_E_INVALID_DOMAIN if domain is not found
  1191. //
  1192. //-----------------------------------------------------------------------------
  1193. HRESULT CDomainMappingTable::HrPrvGetDomainEntry(IN DWORD cbDomainNameLength,
  1194. IN LPSTR szDomainName, BOOL fDMTLocked,
  1195. OUT CDomainEntry **ppdentry)
  1196. {
  1197. HRESULT hr = S_OK;
  1198. BOOL fLocked = FALSE;
  1199. DOMAIN_STRING strDomain;
  1200. _ASSERT(cbDomainNameLength);
  1201. _ASSERT(szDomainName);
  1202. _ASSERT(ppdentry);
  1203. INIT_DOMAIN_STRING(strDomain, cbDomainNameLength, szDomainName);
  1204. if (!fDMTLocked)
  1205. {
  1206. m_slPrivateData.ShareLock();
  1207. fLocked = TRUE;
  1208. }
  1209. hr = m_dnt.HrFindDomainName(&strDomain, (PVOID *) ppdentry);
  1210. if (FAILED(hr))
  1211. {
  1212. if (DOMHASH_E_NO_SUCH_DOMAIN == hr)
  1213. hr = AQUEUE_E_INVALID_DOMAIN;
  1214. _ASSERT(NULL == *ppdentry);
  1215. goto Exit;
  1216. }
  1217. (*ppdentry)->AddRef();
  1218. Exit:
  1219. if (fLocked)
  1220. m_slPrivateData.ShareUnlock();
  1221. return hr;
  1222. }
  1223. //---[ CDomainMappingTable::HrPrvInsertDomainEntry ]---------------------------
  1224. //
  1225. //
  1226. // Description:
  1227. // Private wrapper function for HrInsertDomainName
  1228. // Parameters:
  1229. // IN pstrDomainName Domain Name to insert in DNT
  1230. // IN pdnetryNew DomainEntry to insert
  1231. // IN fTreadAsWildcard TRUE if DNT to be told to treat as wildcard
  1232. // OUT pdentryOld Existing DomainEntry if there is one
  1233. // Returns:
  1234. //
  1235. // History:
  1236. // 10/5/1999 - MikeSwa Created
  1237. //
  1238. //-----------------------------------------------------------------------------
  1239. HRESULT CDomainMappingTable::HrPrvInsertDomainEntry(
  1240. IN PDOMAIN_STRING pstrDomainName,
  1241. IN CDomainEntry *pdentryNew,
  1242. IN BOOL fTreatAsWildcard,
  1243. OUT CDomainEntry **ppdentryOld)
  1244. {
  1245. HRESULT hr = S_OK;
  1246. hr = m_dnt.HrInsertDomainName(pstrDomainName, pdentryNew, fTreatAsWildcard,
  1247. (PVOID *) ppdentryOld);
  1248. if (E_INVALIDARG == hr)
  1249. hr = PHATQ_BAD_DOMAIN_SYNTAX;
  1250. return hr;
  1251. }
  1252. //---[ CDomainMappingTable::HrInitLocalDomain ]--------------------------------
  1253. //
  1254. //
  1255. // Description:
  1256. // Performs initialization needed for a local domain when an entry is
  1257. // created in the DMT
  1258. // Parameters:
  1259. // IN OUT pdentry - entry to init
  1260. // IN pStrDomain - domain name of entry
  1261. // IN paqmtMessageType Message Type of message
  1262. // OUT pdmap - Domain Mapping for domain
  1263. // Returns:
  1264. // S_OK - when all succeeds
  1265. //
  1266. //-----------------------------------------------------------------------------
  1267. HRESULT CDomainMappingTable::HrInitLocalDomain(
  1268. IN CDomainEntry *pdentry,
  1269. IN DOMAIN_STRING *pStrDomain,
  1270. IN CAQMessageType *paqmtMessageType,
  1271. OUT CDomainMapping *pdmap)
  1272. {
  1273. TraceFunctEnterEx((LPARAM) this, "CDomainMappingTable::HrInitLocalDomain");
  1274. HRESULT hr = S_OK;
  1275. LPSTR szKey = NULL;
  1276. _ASSERT(pdentry);
  1277. _ASSERT(pStrDomain);
  1278. _ASSERT(pdmap);
  1279. _ASSERT('\0' == pStrDomain->Buffer[pStrDomain->Length]);
  1280. //make copy of string to store in domain entry
  1281. szKey = (LPSTR) pvMalloc(pStrDomain->Length + sizeof(CHAR));
  1282. if (szKey == NULL)
  1283. {
  1284. hr = E_OUTOFMEMORY;
  1285. goto Exit;
  1286. }
  1287. strcpy(szKey, pStrDomain->Buffer);
  1288. //passes ownership of szKey
  1289. hr = pdentry->HrInitialize(pStrDomain->Length, szKey, pdentry, NULL, NULL);
  1290. if (FAILED(hr))
  1291. goto Exit;
  1292. hr = pdentry->HrGetDomainMapping(pdmap);
  1293. if (FAILED(hr))
  1294. goto Exit;
  1295. Exit:
  1296. if (FAILED(hr) && szKey)
  1297. FreePv(szKey);
  1298. TraceFunctLeave();
  1299. return hr;
  1300. }
  1301. //---[ CDomainMappingTable::HrInitRemoteDomain ]--------------------------------
  1302. //
  1303. //
  1304. // Description:
  1305. // Performs initialization needed for a remote domain when an entry is
  1306. // created in the DMT.
  1307. // Parameters:
  1308. // IN pdentry - entry to init
  1309. // IN pStrDomain - domain name of entry
  1310. // IN pIntDomainInfo - Internal config info for domain
  1311. // IN paqmtMessageType - Message type returned by routing
  1312. // IN pIMessageRouter - Message Router interface for this message
  1313. // OUT pdmap - Domain Mapping for domain
  1314. // OUT ppdmq - destmsgqueue for domain
  1315. // OUT pplmq - LinkMsgQueue that this queue should be associated with
  1316. // caller should call HrAddQueue once entry is in DMT
  1317. // Returns:
  1318. // S_OK on success.
  1319. // E_OUTOFMEMORY when allocations fail
  1320. // History:
  1321. // 6/24/98 - Mikeswa Modified... added pplmq param and removed call to
  1322. // HrAddQueue
  1323. //-----------------------------------------------------------------------------
  1324. HRESULT CDomainMappingTable::HrInitRemoteDomain(
  1325. IN CDomainEntry *pdentry,
  1326. IN DOMAIN_STRING *pStrDomain,
  1327. IN CInternalDomainInfo *pIntDomainInfo,
  1328. IN CAQMessageType *paqmtMessageType,
  1329. IN IMessageRouter *pIMessageRouter,
  1330. OUT CDomainMapping *pdmap,
  1331. OUT CDestMsgQueue **ppdmq,
  1332. OUT CLinkMsgQueue **pplmq)
  1333. {
  1334. TraceFunctEnterEx((LPARAM) this, "CDomainMappingTable::HrInitRemoteDomain");
  1335. HRESULT hr = S_OK;
  1336. HRESULT hrRoutingDiag = S_OK;
  1337. LPSTR szKey = NULL;
  1338. CDestMsgQueue *pdmq = NULL;
  1339. CLinkMsgQueue *plmq = NULL;
  1340. BOOL fEntryInit = FALSE;
  1341. _ASSERT(ppdmq);
  1342. _ASSERT(pplmq);
  1343. _ASSERT(pdentry);
  1344. _ASSERT(pStrDomain);
  1345. _ASSERT(pdmap);
  1346. _ASSERT(pIMessageRouter);
  1347. _ASSERT('\0' == pStrDomain->Buffer[pStrDomain->Length]);
  1348. //Initialze out params
  1349. *ppdmq = NULL;
  1350. *pplmq = NULL;
  1351. //make copy of string to store in domain entry
  1352. szKey = (LPSTR) pvMalloc(pStrDomain->Length + sizeof(CHAR));
  1353. if (szKey == NULL)
  1354. {
  1355. hr = E_OUTOFMEMORY;
  1356. goto Exit;
  1357. }
  1358. strcpy(szKey, pStrDomain->Buffer);
  1359. hr = HrGetNextHopLink(pdentry, szKey, pStrDomain->Length, pIntDomainInfo,
  1360. paqmtMessageType, pIMessageRouter, FALSE, &plmq, &hrRoutingDiag);
  1361. if (FAILED(hr))
  1362. goto Exit;
  1363. pdmq = new CDestMsgQueue(m_paqinst, paqmtMessageType, pIMessageRouter);
  1364. if (!pdmq)
  1365. {
  1366. hr = E_OUTOFMEMORY;
  1367. goto Exit;
  1368. }
  1369. //passes ownership of szKey & pdmq
  1370. hr = pdentry->HrInitialize(pStrDomain->Length, szKey, pdentry, pdmq, NULL);
  1371. if (FAILED(hr))
  1372. goto Exit;
  1373. fEntryInit = TRUE; //we cannot delete pdmq or szKey now
  1374. //get the newly created domain mapping so we can initialize the queue
  1375. hr = pdentry->HrGetDomainMapping(pdmap);
  1376. if (FAILED(hr))
  1377. goto Exit;
  1378. //Initialize queue to use this domain mapping using the DomainMapping we just got
  1379. hr = pdmq->HrInitialize(pdmap);
  1380. if (FAILED(hr))
  1381. goto Exit;
  1382. //Associate link with DMQ
  1383. pdmq->SetRouteInfo(plmq);
  1384. //Set routing error if there was one.
  1385. pdmq->SetRoutingDiagnostic(hrRoutingDiag);
  1386. *ppdmq = pdmq;
  1387. *pplmq = plmq;
  1388. Exit:
  1389. //Cleanup failure cases
  1390. if (FAILED(hr) && !fEntryInit)
  1391. {
  1392. if (szKey)
  1393. FreePv(szKey);
  1394. if (NULL != pdmq)
  1395. {
  1396. //once domain entry has been initialized, it owns pdmq
  1397. pdmq->HrDeinitialize();
  1398. pdmq->Release();
  1399. _ASSERT(NULL == *ppdmq);
  1400. }
  1401. if (NULL != plmq)
  1402. {
  1403. plmq->HrDeinitialize();
  1404. }
  1405. }
  1406. if (plmq && !*pplmq) //we haven't passed refernce to OUT param
  1407. plmq->Release();
  1408. TraceFunctLeave();
  1409. return hr;
  1410. }
  1411. //---[ CDomainMappingTable::HrCreateQueueForEntry ]----------------------------
  1412. //
  1413. //
  1414. // Description:
  1415. // Create a new queue for an already existing domain entry.
  1416. //
  1417. // Currently, this is done by creating a new queue and link, and
  1418. // attempting to associate the queue with the domain entry.
  1419. // Parameters:
  1420. // IN pdentry - entry to add queue to
  1421. // IN pStrDomain - domain name of entry
  1422. // IN pIntDomainInfo - Internal config info for domain
  1423. // IN paqmtMessageType - Message type returned by routing
  1424. // IN pIMessageRouter - Message Router interface for this message
  1425. // IN pdmap - Domain Mapping for domain
  1426. // OUT ppdmq - destmsgqueue for domain
  1427. // Returns:
  1428. // S_OK on succcess
  1429. // History:
  1430. // 6/2/98 - MikeSwa Created
  1431. //
  1432. //-----------------------------------------------------------------------------
  1433. HRESULT CDomainMappingTable::HrCreateQueueForEntry(
  1434. IN CDomainEntry *pdentry,
  1435. IN DOMAIN_STRING *pStrDomain,
  1436. IN CInternalDomainInfo *pIntDomainInfo,
  1437. IN CAQMessageType *paqmtMessageType,
  1438. IN IMessageRouter *pIMessageRouter,
  1439. IN CDomainMapping *pdmap,
  1440. OUT CDestMsgQueue **ppdmq)
  1441. {
  1442. TraceFunctEnterEx((LPARAM) this, "CDomainMappingTable::HrCreateQueueForEntry");
  1443. _ASSERT(pdentry);
  1444. HRESULT hr = S_OK;
  1445. HRESULT hrRoutingDiag = S_OK;
  1446. CDestMsgQueue *pdmq = NULL;
  1447. CLinkMsgQueue *plmq = NULL;
  1448. LPSTR szKey = pdentry->szGetDomainName();
  1449. *ppdmq = NULL;
  1450. _ASSERT(pStrDomain);
  1451. _ASSERT(pdmap);
  1452. _ASSERT(pIMessageRouter);
  1453. _ASSERT('\0' == pStrDomain->Buffer[pStrDomain->Length]);
  1454. hr = HrGetNextHopLink(pdentry, szKey, pStrDomain->Length, pIntDomainInfo,
  1455. paqmtMessageType, pIMessageRouter, FALSE, &plmq, &hrRoutingDiag);
  1456. if (FAILED(hr))
  1457. goto Exit;
  1458. pdmq = new CDestMsgQueue(m_paqinst, paqmtMessageType, pIMessageRouter);
  1459. if (NULL == pdmq)
  1460. {
  1461. hr = E_OUTOFMEMORY;
  1462. goto Exit;
  1463. }
  1464. DebugTrace((LPARAM) szKey, "INFO: Creating new Destination Message Queue for domain %s", szKey);
  1465. hr = pdmq->HrInitialize(pdmap);
  1466. if (FAILED(hr))
  1467. goto Exit;
  1468. //Associate link with DMQ
  1469. pdmq->SetRouteInfo(plmq);
  1470. //Set routing error if there was one.
  1471. pdmq->SetRoutingDiagnostic(hrRoutingDiag);
  1472. //Now attempt to associate newly created queue/link pair with domain entry
  1473. hr = pdentry->HrAddUniqueDestMsgQueue(pdmq, ppdmq);
  1474. if (SUCCEEDED(hr))
  1475. {
  1476. *ppdmq = pdmq;
  1477. //Only add the queue in this case... if a queue is already in the entry, then
  1478. //the other thread must have already (or soon will) call HrAddQueue... we
  1479. //should not call it twice.
  1480. hr = plmq->HrAddQueue(*ppdmq);
  1481. if (FAILED(hr))
  1482. {
  1483. // Remove DMQ from link w/o notify since we never added it
  1484. (*ppdmq)->RemoveDMQFromLink(FALSE);
  1485. // Release the link we couldn't use and get the
  1486. // currently unreachable link
  1487. plmq->Release();
  1488. plmq = plmqGetCurrentlyUnreachable();
  1489. if (plmq)
  1490. {
  1491. pdmq->SetRouteInfo(plmq);
  1492. hr = plmq->HrAddQueue(*ppdmq);
  1493. if (FAILED(hr))
  1494. {
  1495. (*ppdmq)->RemoveDMQFromLink(FALSE);
  1496. }
  1497. }
  1498. // Schedule a reset routes to clean up the currently
  1499. // unreachable queue
  1500. m_paqinst->ResetRoutes(RESET_NEXT_HOPS);
  1501. // If we still have a failed hr, goto exit
  1502. if (FAILED(hr))
  1503. goto Exit;
  1504. }
  1505. }
  1506. else
  1507. {
  1508. DebugTrace((LPARAM) this, "INFO: Thread swap while trying to add queue for domain %s", szKey);
  1509. _ASSERT(*ppdmq != pdmq);
  1510. _ASSERT(*ppdmq && "HrAddUniqueDestMsgQueue failed without returning an error code");
  1511. hr = S_OK; //return new value
  1512. //Remove link from DMQ... since we will never call HrAddQueue
  1513. //don't notify link since it was never added
  1514. pdmq->RemoveDMQFromLink(FALSE);
  1515. }
  1516. Exit:
  1517. //Cleanup failure cases (including if queue created is not used)
  1518. if (FAILED(hr) || (*ppdmq != pdmq))
  1519. {
  1520. if (NULL != pdmq)
  1521. {
  1522. //once domain entry has been initialized, it owns pdmq
  1523. pdmq->Release();
  1524. }
  1525. }
  1526. if (NULL != plmq)
  1527. plmq->Release();
  1528. TraceFunctLeave();
  1529. return hr;
  1530. }
  1531. //---[ CDomainMappingTable::LogDomainUnreachableEvent] ------------------------
  1532. //
  1533. //
  1534. // Description:
  1535. // Logs an event for an unreachable domain
  1536. // Parameters:
  1537. // IN fCurrentlyUnreachable Is the domain currently unreachable or
  1538. // completely unreachable?
  1539. // IN szDomain Final destination domain
  1540. // History:
  1541. // 3/8/99 - AWetmore Created
  1542. //
  1543. //-----------------------------------------------------------------------------
  1544. void CDomainMappingTable::LogDomainUnreachableEvent(BOOL fCurrentlyUnreachable,
  1545. LPCSTR szDomain)
  1546. {
  1547. DWORD dwMessageId =
  1548. (fCurrentlyUnreachable) ? AQUEUE_DOMAIN_CURRENTLY_UNREACHABLE
  1549. : AQUEUE_DOMAIN_UNREACHABLE;
  1550. LPSTR rgszSubStrings[1];
  1551. rgszSubStrings[0] = (char*)szDomain;
  1552. if (m_paqinst)
  1553. {
  1554. m_paqinst->HrTriggerLogEvent(
  1555. dwMessageId, // Message ID
  1556. TRAN_CAT_QUEUE_ENGINE, // Category
  1557. 1, // Word count of substring
  1558. (const char**)&rgszSubStrings[0], // Substring
  1559. EVENTLOG_WARNING_TYPE, // Type of the message
  1560. 0, // No error code
  1561. LOGEVENT_LEVEL_MINIMUM, // Logging level
  1562. "", // Key to identify this event
  1563. LOGEVENT_FLAG_PERIODIC // Event logging option
  1564. );
  1565. }
  1566. }
  1567. //---[ CDomainMappingTable::HrGetNextHopLink ]------------------------------
  1568. //
  1569. //
  1570. // Description:
  1571. // Creates and initializes the CLinkMsgQueue object for this message
  1572. // (if neccessary). Calls router to get next hop info
  1573. // Parameters:
  1574. // IN pdentry Entry that is being initialized for destination
  1575. // IN szDomain Final destination domain
  1576. // IN cbDomain string length in bytes of domain (without \0)
  1577. // IN pIntDomainInfo Domain info for final destination domain
  1578. // IN paqmtMessageType Message type of this message
  1579. // IN pIMessageRouter Routing interface for this message
  1580. // IN fDMTLocked TRUE if DMT is already locked
  1581. // OUT pplmq Resulting link msg queue
  1582. // OUT phrRoutingDiag If next hop is unreachable, this tells us why
  1583. // Returns:
  1584. // S_OK on success
  1585. // History:
  1586. // 6/19/98 - MikeSwa Created
  1587. //
  1588. //-----------------------------------------------------------------------------
  1589. HRESULT CDomainMappingTable::HrGetNextHopLink(
  1590. IN CDomainEntry *pdentry,
  1591. IN LPSTR szDomain,
  1592. IN DWORD cbDomain,
  1593. IN CInternalDomainInfo *pIntDomainInfo,
  1594. IN CAQMessageType *paqmtMessageType,
  1595. IN IMessageRouter *pIMessageRouter,
  1596. IN BOOL fDMTLocked,
  1597. OUT CLinkMsgQueue **pplmq,
  1598. OUT HRESULT *phrRoutingDiag)
  1599. {
  1600. TraceFunctEnterEx((LPARAM) this, "CDomainMappingTable::HrGetNextHopLink");
  1601. HRESULT hr = S_OK;
  1602. BOOL fCalledGetNextHop = FALSE;
  1603. BOOL fValidSMTP = FALSE;
  1604. BOOL fOwnsScheduleId = FALSE;
  1605. LPSTR szRouteAddressType = NULL;
  1606. LPSTR szRouteAddress = NULL;
  1607. LPSTR szRouteAddressClass = NULL;
  1608. LPSTR szConnectorName = NULL;
  1609. DWORD dwScheduleID = 0;
  1610. DWORD dwNextHopType = 0;
  1611. CLinkMsgQueue *plmq = NULL;
  1612. CLinkMsgQueue *plmqTmp = NULL;
  1613. LPSTR szOwnedDomain = NULL; // string buffer that is "owned" by an entry
  1614. CDomainEntry *pdentryLink = NULL; //entry for link
  1615. CDomainEntry *pdentryTmp = NULL;
  1616. DOMAIN_STRING strNextHop;
  1617. DWORD cbRouteAddress = 0;
  1618. CAQScheduleID aqsched;
  1619. IMessageRouterLinkStateNotification *pILinkStateNotify = NULL;
  1620. LinkFlags lf = eLinkFlagsExternalSMTPLinkInfo;
  1621. *phrRoutingDiag = S_OK;
  1622. _ASSERT(pdentry);
  1623. _ASSERT(szDomain);
  1624. _ASSERT(pIntDomainInfo);
  1625. _ASSERT(paqmtMessageType);
  1626. _ASSERT(pIMessageRouter);
  1627. _ASSERT(pplmq);
  1628. hr = pIMessageRouter->QueryInterface(IID_IMessageRouterLinkStateNotification,
  1629. (VOID **) &pILinkStateNotify);
  1630. if (FAILED(hr))
  1631. {
  1632. pILinkStateNotify = NULL;
  1633. hr = S_OK;
  1634. }
  1635. //If we can route this domain.... call router to get next hop
  1636. //We do not route TURN/ETRN domains... or local drop domains
  1637. //Also check to see if the domain is configured as a local domain...
  1638. //if it is, return the local link
  1639. if (DOMAIN_INFO_LOCAL_MAILBOX & pIntDomainInfo->m_DomainInfo.dwDomainInfoFlags)
  1640. {
  1641. //The likely scenario of this happening is if a domain was previously
  1642. //configured as remote and then reconfigured as local.
  1643. m_plmqLocal->AddRef();
  1644. *pplmq = m_plmqLocal;
  1645. goto Exit;
  1646. }
  1647. else if (!(pIntDomainInfo->m_DomainInfo.dwDomainInfoFlags &
  1648. (DOMAIN_INFO_TURN_ONLY | DOMAIN_INFO_ETRN_ONLY | DOMAIN_INFO_LOCAL_DROP)))
  1649. {
  1650. hr = pIMessageRouter->GetNextHop(MTI_ROUTING_ADDRESS_TYPE_SMTP, szDomain,
  1651. paqmtMessageType->dwGetMessageType(), &szRouteAddressType,
  1652. &szRouteAddress, &dwScheduleID, &szRouteAddressClass,
  1653. &szConnectorName, &dwNextHopType);
  1654. fCalledGetNextHop = TRUE;
  1655. *pplmq = NULL;
  1656. if(MTI_NEXT_HOP_TYPE_UNREACHABLE == dwNextHopType)
  1657. {
  1658. //If the next hop is unreachable, store the reason for the unreachable
  1659. //error into *phrRoutingDiag (which is used by aqueue DSN generation).
  1660. const char *rgszStrings[2] = { szDomain, NULL };
  1661. if (m_paqinst)
  1662. {
  1663. m_paqinst->HrTriggerLogEvent(
  1664. PHATQ_UNREACHABLE_DOMAIN, // Message ID
  1665. TRAN_CAT_QUEUE_ENGINE, // Category
  1666. 2, // Word count of substring
  1667. rgszStrings, // Substring
  1668. EVENTLOG_WARNING_TYPE, // Type of the message
  1669. hr, // error code
  1670. LOGEVENT_LEVEL_FIELD_ENGINEERING, // Logging level
  1671. "phatq", // key to this event
  1672. LOGEVENT_FLAG_PERIODIC, // Logging option
  1673. 1, // index of format message string in rgszStrings
  1674. GetModuleHandle(AQ_MODULE_NAME) // module handle to format a message
  1675. );
  1676. }
  1677. ReUnreachableErrorToAqueueError(hr, phrRoutingDiag);
  1678. hr = S_OK;
  1679. }
  1680. if (FAILED(hr))
  1681. {
  1682. RequestResetRoutesRetryIfNecessary();
  1683. ErrorTrace((LPARAM) this,
  1684. "GetNextHop failed with hr - 0x%08X", hr);
  1685. //treat all failures as a routing currently unreachable
  1686. hr = S_OK;
  1687. dwNextHopType = MTI_NEXT_HOP_TYPE_CURRENTLY_UNREACHABLE;
  1688. }
  1689. if (MTI_NEXT_HOP_TYPE_CURRENTLY_UNREACHABLE == dwNextHopType)
  1690. {
  1691. LogDomainUnreachableEvent(TRUE, szDomain);
  1692. *pplmq = m_plmqCurrentlyUnreachable;
  1693. }
  1694. else if (MTI_NEXT_HOP_TYPE_UNREACHABLE == dwNextHopType)
  1695. {
  1696. LogDomainUnreachableEvent(FALSE, szDomain);
  1697. *pplmq = m_plmqUnreachable;
  1698. }
  1699. else if ((MTI_NEXT_HOP_TYPE_SAME_VIRTUAL_SERVER == dwNextHopType) ||
  1700. (szRouteAddressType &&
  1701. lstrcmpi(MTI_ROUTING_ADDRESS_TYPE_SMTP, szRouteAddressType)))
  1702. {
  1703. //Handle any cases that might be considered local delivery
  1704. *pplmq = m_plmqLocal;
  1705. }
  1706. else if (!szRouteAddressType || ('\0' == *szRouteAddressType) ||
  1707. !szRouteAddress || ('\0' == *szRouteAddress))
  1708. {
  1709. //This is a bogus combination of values... try try again
  1710. hr = E_FAIL;
  1711. goto Exit;
  1712. }
  1713. else
  1714. {
  1715. fValidSMTP = TRUE;
  1716. fOwnsScheduleId = TRUE;
  1717. //At this point we should have valid SMTP values for the address
  1718. _ASSERT(szRouteAddressType);
  1719. _ASSERT(szRouteAddress);
  1720. _ASSERT(!lstrcmpi(MTI_ROUTING_ADDRESS_TYPE_SMTP, szRouteAddressType));
  1721. if (MTI_NEXT_HOP_TYPE_PEER_SMTP1_BYPASS_CONFIG_LOOKUP == dwNextHopType ||
  1722. MTI_NEXT_HOP_TYPE_PEER_SMTP2_BYPASS_CONFIG_LOOKUP == dwNextHopType) {
  1723. lf = eLinkFlagsInternalSMTPLinkInfo;
  1724. }
  1725. }
  1726. if (!fValidSMTP)
  1727. {
  1728. //Must be going to one of them-there global queues.
  1729. hr = S_OK;
  1730. if (*pplmq)
  1731. (*pplmq)->AddRef();
  1732. else
  1733. hr = E_FAIL;
  1734. //Our work here is done
  1735. goto Exit;
  1736. }
  1737. }
  1738. if ((!fCalledGetNextHop) || (!lstrcmpi(szDomain, szRouteAddress)))
  1739. {
  1740. //final destination and next hop are the same
  1741. DebugTrace((LPARAM) this, "DEBUG: Routing case 1 - same next hop and final dest");
  1742. // See if there is already a link for this schedule ID
  1743. aqsched.Init(pIMessageRouter, dwScheduleID);
  1744. hr = pdentry->HrGetLinkMsgQueue(&aqsched, &plmq);
  1745. if (FAILED(hr))
  1746. {
  1747. hr = S_OK;
  1748. //link does not exist for this schedule id yet
  1749. plmq = new CLinkMsgQueue(dwScheduleID, pIMessageRouter,
  1750. pILinkStateNotify);
  1751. if (!plmq)
  1752. {
  1753. hr = E_OUTOFMEMORY;
  1754. goto Exit;
  1755. }
  1756. fOwnsScheduleId = FALSE; //link now owns it
  1757. DebugTrace((LPARAM) szDomain, "INFO: Creating new Link for domain %s", szDomain);
  1758. hr = plmq->HrInitialize(m_paqinst, pdentry, cbDomain, szDomain,
  1759. lf, szConnectorName);
  1760. if (FAILED(hr))
  1761. goto Exit;
  1762. hr = pdentry->HrAddUniqueLinkMsgQueue(plmq, &plmqTmp);
  1763. if (FAILED(hr))
  1764. {
  1765. //Another link was inserted since we called get link msg queue
  1766. DebugTrace((LPARAM) this, "DEBUG: Routing case 2(a) - next hop link created by other thread");
  1767. _ASSERT(plmqTmp);
  1768. plmq->HrDeinitialize();
  1769. plmq->Release();
  1770. plmq = plmqTmp;
  1771. hr = S_OK;
  1772. }
  1773. }
  1774. else
  1775. {
  1776. DebugTrace((LPARAM) this, "DEBUG: Routing case 2(b) - next hop link created by other thread");
  1777. }
  1778. }
  1779. else
  1780. {
  1781. //next hop is different from final destination
  1782. cbRouteAddress = strlen(szRouteAddress);
  1783. //First see if there is an entry for this link
  1784. hr = HrPrvGetDomainEntry(cbRouteAddress, szRouteAddress, fDMTLocked, &pdentryLink);
  1785. if (AQUEUE_E_INVALID_DOMAIN == hr)
  1786. {
  1787. //an entry for this link does not exist... add one for this link
  1788. hr = S_OK;
  1789. DebugTrace((LPARAM) this, "DEBUG: Routing case 3 - next hop entry does not exist");
  1790. szOwnedDomain = (LPSTR) pvMalloc(sizeof(CHAR)*(cbRouteAddress+1));
  1791. if (!szOwnedDomain)
  1792. {
  1793. hr = E_OUTOFMEMORY;
  1794. goto Exit;
  1795. }
  1796. lstrcpy(szOwnedDomain, szRouteAddress);
  1797. pdentryLink = new CDomainEntry(m_paqinst);
  1798. if (!pdentryLink)
  1799. {
  1800. hr = E_OUTOFMEMORY;
  1801. goto Exit;
  1802. }
  1803. plmq = new CLinkMsgQueue(dwScheduleID, pIMessageRouter,
  1804. pILinkStateNotify);
  1805. if (!plmq)
  1806. {
  1807. hr = E_OUTOFMEMORY;
  1808. goto Exit;
  1809. }
  1810. fOwnsScheduleId = FALSE; //link now owns it
  1811. //passes ownership of szOwnedDomain
  1812. hr = pdentryLink->HrInitialize(cbRouteAddress, szOwnedDomain,
  1813. pdentryLink, NULL, plmq);
  1814. if (FAILED(hr))
  1815. goto Exit;
  1816. hr = plmq->HrInitialize(m_paqinst, pdentryLink, cbRouteAddress,
  1817. szOwnedDomain, lf, szConnectorName);
  1818. if (FAILED(hr))
  1819. goto Exit;
  1820. //insert entry in DMT
  1821. strNextHop.Length = (USHORT) cbRouteAddress;
  1822. strNextHop.Buffer = szOwnedDomain;
  1823. strNextHop.MaximumLength = (USHORT) cbRouteAddress;
  1824. if (!fDMTLocked)
  1825. m_slPrivateData.ExclusiveLock();
  1826. hr = HrPrvInsertDomainEntry(&strNextHop, pdentryLink, FALSE, &pdentryTmp);
  1827. if (hr == DOMHASH_E_DOMAIN_EXISTS)
  1828. {
  1829. DebugTrace((LPARAM) this, "DEBUG: Routing case 4 - next hop entry did not exist... inserted by other thread");
  1830. plmq->Release();
  1831. plmq = NULL;
  1832. pdentryTmp->AddRef();
  1833. pdentryLink->HrDeinitialize();
  1834. pdentryLink->Release();
  1835. pdentryLink = pdentryTmp;
  1836. hr = S_OK;
  1837. //Will fall through to case as if an entry was found by HrGetDomainEntry
  1838. }
  1839. else if (SUCCEEDED(hr))
  1840. {
  1841. pdentryLink->AddRef();
  1842. }
  1843. if (!fDMTLocked)
  1844. m_slPrivateData.ExclusiveUnlock();
  1845. if (FAILED(hr))
  1846. goto Exit;
  1847. }
  1848. else if (FAILED(hr))
  1849. {
  1850. ErrorTrace((LPARAM) this, "ERROR: General DMT failure - hr 0x%08X", hr);
  1851. //general failure... bail
  1852. goto Exit;
  1853. }
  1854. if (!plmq)
  1855. {
  1856. DebugTrace((LPARAM) this, "DEBUG: Routing case 5 - next hop entry exists");
  1857. //An entry exists for this next hop... use link if possible
  1858. // 1 - Get link for this schedule ID.. if it exists use it
  1859. // 2 - Create another link and attempt to insert it
  1860. _ASSERT(pdentryLink);
  1861. aqsched.Init(pIMessageRouter, dwScheduleID);
  1862. hr = pdentryLink->HrGetLinkMsgQueue(&aqsched, &plmq);
  1863. if (FAILED(hr))
  1864. {
  1865. hr = S_OK;
  1866. //link does not exist for this schedule id yet
  1867. DebugTrace((LPARAM) this, "DEBUG: Routing case 6 - next hop link does not exist");
  1868. szOwnedDomain = pdentryLink->szGetDomainName();
  1869. plmq = new CLinkMsgQueue(dwScheduleID, pIMessageRouter,
  1870. pILinkStateNotify);
  1871. if (!plmq)
  1872. {
  1873. hr = E_OUTOFMEMORY;
  1874. goto Exit;
  1875. }
  1876. fOwnsScheduleId = FALSE; //link now owns it
  1877. hr = plmq->HrInitialize(m_paqinst, pdentryLink, cbRouteAddress,
  1878. szOwnedDomain, lf, szConnectorName);
  1879. if (FAILED(hr))
  1880. goto Exit;
  1881. hr = pdentryLink->HrAddUniqueLinkMsgQueue(plmq, &plmqTmp);
  1882. if (FAILED(hr))
  1883. {
  1884. //Another link was inserted since we called get link msg queue
  1885. DebugTrace((LPARAM) this, "DEBUG: Routing case 7 - next hop link created by other thread");
  1886. _ASSERT(plmqTmp);
  1887. plmq->Release();
  1888. plmq = plmqTmp;
  1889. hr = S_OK;
  1890. }
  1891. }
  1892. else
  1893. {
  1894. DebugTrace((LPARAM) this, "DEBUG: Routing case 8 - next hop link exists");
  1895. }
  1896. }
  1897. }
  1898. _ASSERT(plmq && "We should have allocated a link by this point");
  1899. *pplmq = plmq;
  1900. Exit:
  1901. if (pdentryLink)
  1902. pdentryLink->Release();
  1903. if (fCalledGetNextHop)
  1904. {
  1905. //
  1906. // If we have not passed the schedule ID on to a link, we
  1907. // must notify routing that we are not using it (to avoid a leak).
  1908. // This needs to be be done *before* we release the strings
  1909. // and routing interfaces. If we hit this case, we have
  1910. // either failed to create a link, or another link has
  1911. // been created by another thread.
  1912. //
  1913. if (fOwnsScheduleId && pILinkStateNotify && pIMessageRouter)
  1914. {
  1915. FILETIME ftNotUsed = {0,0};
  1916. DWORD dwSetNotUsed = LINK_STATE_NO_ACTION;
  1917. DWORD dwUnsetNotUsed = LINK_STATE_NO_ACTION;
  1918. pILinkStateNotify->LinkStateNotify(
  1919. szDomain,
  1920. pIMessageRouter->GetTransportSinkID(),
  1921. dwScheduleID,
  1922. szConnectorName,
  1923. LINK_STATE_LINK_NO_LONGER_USED,
  1924. 0, //consecutive failures
  1925. &ftNotUsed,
  1926. &dwSetNotUsed,
  1927. &dwUnsetNotUsed);
  1928. }
  1929. //
  1930. // Free Strings returned by GetNextHop
  1931. //
  1932. _VERIFY(SUCCEEDED(pIMessageRouter->GetNextHopFree(
  1933. MTI_ROUTING_ADDRESS_TYPE_SMTP,
  1934. szDomain,
  1935. szConnectorName,
  1936. szRouteAddressType,
  1937. szRouteAddress,
  1938. szRouteAddressClass)));
  1939. }
  1940. if (pILinkStateNotify)
  1941. pILinkStateNotify->Release();
  1942. if (FAILED(hr) && plmq)
  1943. plmq->Release();
  1944. TraceFunctLeave();
  1945. return hr;
  1946. }
  1947. //---[ CDomainMappingTable::HrGetOrCreateLink ]--------------------------------
  1948. //
  1949. //
  1950. // Description:
  1951. // Gets or creates a link object for a domain name
  1952. // Parameters:
  1953. // IN szRouteAddress Final destination domain
  1954. // IN cbRouteAddress string length in bytes of domain (without \0)
  1955. // IN dwScheduleID Schedule ID for link (used in create)
  1956. // IN szConnectorName Name (stringized GUID) of connector in DS
  1957. // IN pIMessageRouter Routing interface for this message (used in create)
  1958. // IN fCreateIfNotExist Create the link if it doesn't exist?
  1959. // OUT pplmq Resulting link msg queue
  1960. // OUT pfRemoveOwnedSchedule FALSE if a new link was created, TRUE on errors
  1961. // and in case the link was addref'ed.
  1962. // Returns:
  1963. // S_OK on success
  1964. // History:
  1965. // 7/6/1999 - AWetmore Created
  1966. // 12/30/1999 - MikeSwa Modified to not notify until connection attempt
  1967. //
  1968. //-----------------------------------------------------------------------------
  1969. HRESULT CDomainMappingTable::HrGetOrCreateLink(
  1970. IN LPSTR szRouteAddress,
  1971. IN DWORD cbRouteAddress,
  1972. IN DWORD dwScheduleID,
  1973. IN LPSTR szConnectorName,
  1974. IN IMessageRouter *pIMessageRouter,
  1975. IN BOOL fCreateIfNotExist,
  1976. IN DWORD linkFlags,
  1977. OUT CLinkMsgQueue **pplmq,
  1978. OUT BOOL *pfRemoveOwnedSchedule)
  1979. {
  1980. TraceFunctEnter("CDomainMappingTable::HrGetOrCreateLink");
  1981. HRESULT hr = S_OK;
  1982. BOOL fValidSMTP = FALSE;
  1983. LPSTR szRouteAddressType = NULL;
  1984. LPSTR szRouteAddressClass = NULL;
  1985. DWORD dwNextHopType = 0;
  1986. CLinkMsgQueue *plmq = NULL;
  1987. CLinkMsgQueue *plmqTmp = NULL;
  1988. LPSTR szOwnedDomain = NULL; // string buffer that is "owned" by an entry
  1989. CDomainEntry *pdentryLink = NULL; //entry for link
  1990. CDomainEntry *pdentryTmp = NULL;
  1991. DOMAIN_STRING strNextHop;
  1992. CAQScheduleID aqsched;
  1993. IMessageRouterLinkStateNotification *pILinkStateNotify = NULL;
  1994. CAQStats aqstats;
  1995. *pfRemoveOwnedSchedule = TRUE;
  1996. _ASSERT(szRouteAddress);
  1997. _ASSERT(pplmq);
  1998. //First see if there is an entry for this link
  1999. hr = HrPrvGetDomainEntry(cbRouteAddress, szRouteAddress, FALSE, &pdentryLink);
  2000. if (AQUEUE_E_INVALID_DOMAIN == hr && fCreateIfNotExist)
  2001. {
  2002. _ASSERT(pIMessageRouter);
  2003. hr = pIMessageRouter->QueryInterface(IID_IMessageRouterLinkStateNotification,
  2004. (VOID **) &pILinkStateNotify);
  2005. if (FAILED(hr)) {
  2006. pILinkStateNotify = NULL;
  2007. goto Exit;
  2008. }
  2009. //an entry for this link does not exist... add one for this link
  2010. hr = S_OK;
  2011. DebugTrace((LPARAM) this, "DEBUG: Routing case 3 - next hop entry does not exist");
  2012. szOwnedDomain = (LPSTR) pvMalloc(sizeof(CHAR)*(cbRouteAddress+1));
  2013. if (!szOwnedDomain)
  2014. {
  2015. hr = E_OUTOFMEMORY;
  2016. goto Exit;
  2017. }
  2018. lstrcpy(szOwnedDomain, szRouteAddress);
  2019. pdentryLink = new CDomainEntry(m_paqinst);
  2020. if (!pdentryLink)
  2021. {
  2022. hr = E_OUTOFMEMORY;
  2023. goto Exit;
  2024. }
  2025. plmq = new CLinkMsgQueue(dwScheduleID, pIMessageRouter,
  2026. pILinkStateNotify);
  2027. if (!plmq)
  2028. {
  2029. hr = E_OUTOFMEMORY;
  2030. goto Exit;
  2031. }
  2032. *pfRemoveOwnedSchedule = FALSE;
  2033. //passes ownership of szOwnedDomain
  2034. hr = pdentryLink->HrInitialize(cbRouteAddress, szOwnedDomain,
  2035. pdentryLink, NULL, plmq);
  2036. if (FAILED(hr))
  2037. goto Exit;
  2038. hr = plmq->HrInitialize(m_paqinst, pdentryLink, cbRouteAddress,
  2039. szOwnedDomain, (LinkFlags) linkFlags, szConnectorName);
  2040. if (FAILED(hr))
  2041. goto Exit;
  2042. //insert entry in DMT
  2043. strNextHop.Length = (USHORT) cbRouteAddress;
  2044. strNextHop.Buffer = szOwnedDomain;
  2045. strNextHop.MaximumLength = (USHORT) cbRouteAddress;
  2046. m_slPrivateData.ExclusiveLock();
  2047. hr = HrPrvInsertDomainEntry(&strNextHop, pdentryLink, FALSE, &pdentryTmp);
  2048. if (hr == DOMHASH_E_DOMAIN_EXISTS)
  2049. {
  2050. DebugTrace((LPARAM) this, "DEBUG: Routing case 4 - next hop entry did not exist... inserted by other thread");
  2051. plmq->Release();
  2052. plmq = NULL;
  2053. pdentryTmp->AddRef();
  2054. pdentryLink->HrDeinitialize();
  2055. pdentryLink->Release();
  2056. pdentryLink = pdentryTmp;
  2057. hr = S_OK;
  2058. //Will fall through to case as if an entry was found by HrGetDomainEntry
  2059. }
  2060. else if (SUCCEEDED(hr))
  2061. {
  2062. pdentryLink->AddRef();
  2063. }
  2064. m_slPrivateData.ExclusiveUnlock();
  2065. if (FAILED(hr))
  2066. goto Exit;
  2067. }
  2068. else if (FAILED(hr))
  2069. {
  2070. ErrorTrace((LPARAM) this, "ERROR: General DMT failure - hr 0x%08X", hr);
  2071. //general failure... bail
  2072. goto Exit;
  2073. }
  2074. if (!plmq) {
  2075. _ASSERT(pdentryLink);
  2076. DebugTrace((LPARAM) this, "DEBUG: Routing case 5 - next hop entry exists");
  2077. //An entry exists for this next hop... use link if possible
  2078. // 1 - Get link for this schedule ID.. if it exists use it
  2079. // 2 - Create another link and attempt to insert it
  2080. _ASSERT(pdentryLink);
  2081. aqsched.Init(pIMessageRouter, dwScheduleID);
  2082. hr = pdentryLink->HrGetLinkMsgQueue(&aqsched, &plmq);
  2083. if (FAILED(hr) && fCreateIfNotExist)
  2084. {
  2085. hr = S_OK;
  2086. //link does not exist for this schedule id yet
  2087. DebugTrace((LPARAM) this, "DEBUG: Routing case 6 - next hop link does not exist");
  2088. szOwnedDomain = pdentryLink->szGetDomainName();
  2089. if (!pILinkStateNotify) {
  2090. _ASSERT(pIMessageRouter);
  2091. hr = pIMessageRouter->QueryInterface(IID_IMessageRouterLinkStateNotification,
  2092. (VOID **) &pILinkStateNotify);
  2093. if (FAILED(hr)) {
  2094. pILinkStateNotify = NULL;
  2095. goto Exit;
  2096. }
  2097. }
  2098. plmq = new CLinkMsgQueue(dwScheduleID, pIMessageRouter,
  2099. pILinkStateNotify);
  2100. if (!plmq)
  2101. {
  2102. hr = E_OUTOFMEMORY;
  2103. goto Exit;
  2104. }
  2105. *pfRemoveOwnedSchedule = FALSE;
  2106. hr = plmq->HrInitialize(m_paqinst, pdentryLink, cbRouteAddress,
  2107. szOwnedDomain, (LinkFlags) linkFlags, szConnectorName);
  2108. if (FAILED(hr))
  2109. goto Exit;
  2110. hr = pdentryLink->HrAddUniqueLinkMsgQueue(plmq, &plmqTmp);
  2111. if (FAILED(hr))
  2112. {
  2113. //Another link was inserted since we called get link msg queue
  2114. DebugTrace((LPARAM) this, "DEBUG: Routing case 7 - next hop link created by other thread");
  2115. _ASSERT(plmqTmp);
  2116. plmq->Release();
  2117. plmq = plmqTmp;
  2118. hr = S_OK;
  2119. }
  2120. } else {
  2121. DebugTrace((LPARAM) this, "DEBUG: Routing case 8 - next hop link exists");
  2122. }
  2123. }
  2124. _ASSERT(plmq && "We should have allocated a link by this point");
  2125. if (plmq) {
  2126. //We count a SetLinkState as a link state notification.... do not
  2127. //notify again before attempting a connection or we will break link
  2128. //state notifications.
  2129. plmq->dwModifyLinkState(LINK_STATE_PRIV_HAVE_SENT_NOTIFICATION,
  2130. LINK_STATE_NO_ACTION);
  2131. aqstats.m_dwNotifyType = NotifyTypeNewLink | NotifyTypeLinkMsgQueue;
  2132. aqstats.m_plmq = plmq;
  2133. hr = m_paqinst->HrNotify(&aqstats, TRUE);
  2134. }
  2135. if (SUCCEEDED(hr)) *pplmq = plmq;
  2136. Exit:
  2137. if (pdentryLink)
  2138. pdentryLink->Release();
  2139. if (pILinkStateNotify)
  2140. pILinkStateNotify->Release();
  2141. if (FAILED(hr) && plmq)
  2142. plmq->Release();
  2143. TraceFunctLeave();
  2144. return hr;
  2145. }
  2146. //---[ CDomainMappingTable::AddDMQToEmptyList ]--------------------------------
  2147. //
  2148. //
  2149. // Description:
  2150. // Used by DMQ to add itself to the list of empty queues. This function
  2151. // will aquire (and release) the appropriate locks
  2152. // Parameters:
  2153. // IN pdmq DestMsgQueue to add to list
  2154. // Returns:
  2155. // -
  2156. // History:
  2157. // 9/12/98 - MikeSwa Created
  2158. // 5/5/99 - MikeSwa Changed to TryExclusiveLock to avoid potential
  2159. // deadlock.
  2160. //
  2161. //-----------------------------------------------------------------------------
  2162. void CDomainMappingTable::AddDMQToEmptyList(CDestMsgQueue *pdmq)
  2163. {
  2164. TraceFunctEnterEx((LPARAM) this, "CDomainMappingTable::AddDMQToEmptyList");
  2165. if (m_slPrivateData.TryExclusiveLock())
  2166. {
  2167. pdmq->InsertQueueInEmptyQueueList(&m_liEmptyDMQHead);
  2168. m_slPrivateData.ExclusiveUnlock();
  2169. }
  2170. else
  2171. {
  2172. DebugTrace((LPARAM) this,
  2173. "AddDMQToEmptyList could not get m_slPrivateData Lock");
  2174. }
  2175. TraceFunctLeave();
  2176. }
  2177. //---[ CDomainMappingTable::fNeedToWalkEmptyQueueList ]------------------------
  2178. //
  2179. //
  2180. // Description:
  2181. // Checks list of Empty queues to see if we need to call
  2182. // fDeleteExpiredQueues(). The caller of this function *must* have
  2183. // m_slPrivateData is shared mode, if it returns TRUE, the caller *must*
  2184. // release the lock and call fDeleteExpiredQueues() (which will aquire
  2185. // the lock Exclusively)
  2186. // Parameters:
  2187. // -
  2188. // Returns:
  2189. // TRUE if fDeleteExpiredQueues should be called.
  2190. // History:
  2191. // 9/12/98 - MikeSwa Created
  2192. // 6/27/2000 - MikeSwa fixed short-circuit logic
  2193. //
  2194. //-----------------------------------------------------------------------------
  2195. BOOL CDomainMappingTable::fNeedToWalkEmptyQueueList()
  2196. {
  2197. BOOL fRet = FALSE;
  2198. PLIST_ENTRY pli = m_liEmptyDMQHead.Flink;
  2199. CDestMsgQueue *pdmq = NULL;
  2200. DWORD dwDMQState = 0;
  2201. DWORD cMisplacedQueues = 0; //# of queues in list that should not be
  2202. _ASSERT(pli);
  2203. // Only one thread at a time should be searching.
  2204. if (1 != InterlockedIncrement((PLONG) &m_cThreadsForEmptyDMQList))
  2205. {
  2206. InterlockedDecrement((PLONG) &m_cThreadsForEmptyDMQList);
  2207. return FALSE;
  2208. }
  2209. if(m_dwFlags & DMT_FLAGS_RESET_ROUTES_IN_PROGRESS)
  2210. return fRet; // we shouldn't remove queues during reset routes
  2211. while (&m_liEmptyDMQHead != pli)
  2212. {
  2213. pdmq = CDestMsgQueue::pdmqGetDMQFromEmptyListEntry(pli);
  2214. _ASSERT(pdmq);
  2215. dwDMQState = pdmq->dwGetDMQState();
  2216. //See if it is empty and expired.. if so, we have a winner
  2217. if (dwDMQState & CDestMsgQueue::DMQ_EMPTY)
  2218. {
  2219. if (dwDMQState & CDestMsgQueue::DMQ_EXPIRED)
  2220. {
  2221. fRet = TRUE;
  2222. break;
  2223. }
  2224. }
  2225. else
  2226. {
  2227. //
  2228. // The queue is no longer empty... we will remove it
  2229. // from this list the next time we have the exclusive
  2230. // lock.
  2231. //
  2232. cMisplacedQueues++;
  2233. //
  2234. // If there are a large number of non-empty DMQs, we
  2235. // want wish to return TRUE even though there are no DMQs to
  2236. // delete... just so we can clean the list of non-empty DMQs.
  2237. //
  2238. if (MAX_MISPLACED_QUEUES_IN_EMPTY_LIST < cMisplacedQueues)
  2239. {
  2240. fRet = TRUE;
  2241. break;
  2242. }
  2243. }
  2244. pli = pli->Flink;
  2245. }
  2246. // If we decide to delete queues we keep this count until we have done
  2247. // the work, if we decide not to then we release the count so someone
  2248. // else will walk the list again
  2249. if(fRet == FALSE)
  2250. {
  2251. InterlockedDecrement((PLONG) &m_cThreadsForEmptyDMQList);
  2252. }
  2253. //NOTE: The reason there may be non-empty queues in this list, is that
  2254. //we cannot remove the queues from the list when we ENQUEUE a message
  2255. //because it would deadlock (we already have the m_slPrivateData lock
  2256. //in shared mode), and removing the queue requires this lock in exclusive
  2257. //mode.
  2258. return fRet;
  2259. }
  2260. //---[ CDomainMappingTable::fDeleteExpiredQueues ]-----------------------------
  2261. //
  2262. //
  2263. // Description:
  2264. // Removes DMQs from empty list. DMQs will be deleted if they have expired
  2265. // and do not have any messages on them. Non-empty DMQs will be removed
  2266. // from the list as well.
  2267. //
  2268. // The DMT m_slPrivateData lock should *not* be held when this is called.
  2269. // This function will aquire it exclusively.
  2270. //
  2271. // Parameters:
  2272. // -
  2273. // Returns:
  2274. // TRUE if any queues, links, or entries were deleted
  2275. // FALSE if no queues were deleted
  2276. // History:
  2277. // 9/12/98 - MikeSwa Created
  2278. //
  2279. //-----------------------------------------------------------------------------
  2280. BOOL CDomainMappingTable::fDeleteExpiredQueues()
  2281. {
  2282. PLIST_ENTRY pli = NULL;
  2283. CDestMsgQueue *pdmq = NULL;
  2284. CLinkMsgQueue *plmq = NULL;
  2285. DWORD dwDMQState = 0;
  2286. CDomainMapping *pdmap = NULL;
  2287. CDomainEntry *pdentry = NULL;
  2288. CDomainEntry *pdentryOld = NULL;
  2289. BOOL fRemovedQueues = FALSE;
  2290. HRESULT hr = S_OK;
  2291. DOMAIN_STRING strDomain;
  2292. // Don't attempt this if it would require waiting for a lock. This
  2293. // decreases contention and also prevents multiple threads from trying
  2294. // to do this work
  2295. if (!m_slPrivateData.TryExclusiveLock())
  2296. goto Exit;
  2297. pli = m_liEmptyDMQHead.Flink;
  2298. while (&m_liEmptyDMQHead != pli)
  2299. {
  2300. _ASSERT(pli);
  2301. pdmq = CDestMsgQueue::pdmqGetDMQFromEmptyListEntry(pli);
  2302. _ASSERT(pdmq);
  2303. dwDMQState = pdmq->dwGetDMQState();
  2304. if (!(dwDMQState & CDestMsgQueue::DMQ_EMPTY))
  2305. {
  2306. //If it is not empty - remove it from the list
  2307. pli = pli->Flink;
  2308. pdmq->RemoveQueueFromEmptyQueueList();
  2309. continue;
  2310. }
  2311. else if (!(dwDMQState & CDestMsgQueue::DMQ_EXPIRED))
  2312. {
  2313. //If this queue hasn't expired... check the next to see if it is empty
  2314. pli = pli->Flink;
  2315. continue;
  2316. }
  2317. else
  2318. {
  2319. //We need to delete this DMQ
  2320. pli = pli->Flink; //get next LIST_ENTRY before we delete the queue
  2321. //Add a reference to the DMQ, so we can guarantee its lifespan
  2322. pdmq->AddRef();
  2323. //Remove the queue from the list of empty queues
  2324. pdmq->RemoveQueueFromEmptyQueueList();
  2325. //Get the domain mapping (and domain entry) for this DMQ
  2326. pdmq->GetDomainMapping(&pdmap);
  2327. _ASSERT(pdmap);
  2328. pdentry = pdmap->pdentryGetQueueEntry();
  2329. //Remove the DMQ from its associated link
  2330. plmq = pdmq->plmqGetLink();
  2331. pdmq->RemoveDMQFromLink(TRUE);
  2332. if (plmq)
  2333. {
  2334. //Remove the link from the DMT if it is empty
  2335. plmq->RemoveLinkIfEmpty();
  2336. plmq->Release();
  2337. plmq = NULL;
  2338. }
  2339. //Now that we have the domain entry, we can remove it the DMQ
  2340. //from it.
  2341. _ASSERT(pdentry);
  2342. pdentry->RemoveDestMsgQueue(pdmq);
  2343. //Remove Entry if needed
  2344. if (pdentry->fSafeToRemove())
  2345. {
  2346. //There are no links or queues left on this entry... we
  2347. //can remove it from the hash table and delete it
  2348. pdentry->InitDomainString(&strDomain);
  2349. hr = m_dnt.HrRemoveDomainName(&strDomain, (void **) &pdentryOld);
  2350. _ASSERT(DOMHASH_E_NO_SUCH_DOMAIN != hr);
  2351. if (SUCCEEDED(hr))
  2352. {
  2353. _ASSERT(pdentryOld == pdentry);
  2354. pdentryOld = NULL;
  2355. pdentry->Release();
  2356. pdentry = NULL;
  2357. }
  2358. }
  2359. //If there are no enqueues pending, this will be the last reference
  2360. //for the DMQ. If there is a enqueue pending, then there may be
  2361. //references outstanding that will be released when they see the
  2362. //updated DMT version number.
  2363. pdmq->Release();
  2364. fRemovedQueues = TRUE;
  2365. }
  2366. }
  2367. //Update version number, so other threads will know that queues
  2368. //have been removed from the DMT.
  2369. if (fRemovedQueues)
  2370. m_dwInternalVersion++;
  2371. m_slPrivateData.ExclusiveUnlock();
  2372. Exit:
  2373. // Release our count so someone else can search the list and possibly
  2374. // remove queues ...
  2375. InterlockedDecrement((PLONG) &m_cThreadsForEmptyDMQList);
  2376. return fRemovedQueues;
  2377. }
  2378. //---[ CDomainMappingTable::RequestResetRoutesRetryIfNecessary ]---------------
  2379. //
  2380. //
  2381. // Description:
  2382. // This is called when routing fails and we need to call reset routes
  2383. // at a later time to try it.
  2384. // Parameters:
  2385. // -
  2386. // Returns:
  2387. // -
  2388. // History:
  2389. // 11/15/1999 - MikeSwa Created
  2390. //
  2391. //-----------------------------------------------------------------------------
  2392. void CDomainMappingTable::RequestResetRoutesRetryIfNecessary()
  2393. {
  2394. HRESULT hr = S_OK;
  2395. dwInterlockedSetBits(&m_dwFlags, DMT_FLAGS_GET_NEXT_HOP_FAILED);
  2396. if (DMT_FLAGS_RESET_ROUTES_IN_PROGRESS & m_dwFlags)
  2397. return;
  2398. //We need to reqest a callback for a later reset routes. We should
  2399. //only allow one callback pending at a time. If this thread increments
  2400. //the count for a 0->1 transition, then we can request the callback
  2401. if (1 == InterlockedIncrement((PLONG) &m_cResetRoutesRetriesPending))
  2402. {
  2403. hr = m_paqinst->SetCallbackTime(
  2404. CDomainMappingTable::RetryResetRoutes,
  2405. this, g_cResetRoutesRetryMinutes);
  2406. if (FAILED(hr))
  2407. InterlockedDecrement((PLONG) &m_cResetRoutesRetriesPending);
  2408. }
  2409. }
  2410. //---[ CDomainMappingTable::RetryResetRoutes ]--------------------------------
  2411. //
  2412. //
  2413. // Description:
  2414. // Handles callback for reset routes. This codepath will be used if
  2415. // GetNextHop failes. Routing has no internal logic to remember if
  2416. // a failure has happened... and no method of sceduling a callback. By
  2417. // periodically call reset routes, we can centrally solve this problem
  2418. // for all routing sinks.
  2419. // Parameters:
  2420. // pvThis "this" pointer for CDomainMappingTable
  2421. // Returns:
  2422. // -
  2423. // History:
  2424. // 11/15/1999 - MikeSwa Created
  2425. //
  2426. //-----------------------------------------------------------------------------
  2427. void CDomainMappingTable::RetryResetRoutes(PVOID pvThis)
  2428. {
  2429. _ASSERT(pvThis);
  2430. CDomainMappingTable *pdmt = (CDomainMappingTable *)pvThis;
  2431. CAQSvrInst *paqinst = pdmt->m_paqinst;
  2432. //Make sure shutdown has not been started. This instance is waits for
  2433. //all threads before deleting itself, so it is safe to call in to the local
  2434. //variable
  2435. if (!paqinst)
  2436. return;
  2437. //Decrement the count, so another request can be queued up.
  2438. InterlockedDecrement((PLONG) &(pdmt->m_cResetRoutesRetriesPending));
  2439. //Kick off another reset routes
  2440. paqinst->ResetRoutes(RESET_NEXT_HOPS);
  2441. }
  2442. //---[ CDomainMappingTable::HrBeginRerouteDomains ]----------------------------
  2443. //
  2444. //
  2445. // Description:
  2446. // Begins the process of handing a ResetRoutes. This function sets
  2447. // DMT_FLAGS_RESET_ROUTES_IN_PROGRESS and then moves all queues into
  2448. // the CurrentlyUnreachable link so that they will not be processed
  2449. // until they have been re-routed.
  2450. // Parameters:
  2451. // NONE
  2452. // Returns:
  2453. // S_OK on success
  2454. // History:
  2455. // 11/5/1998 - MikeSwa Created
  2456. // 10/31/2000 - dbraun - renamed / was part of HrRerouteDomains
  2457. //
  2458. //-----------------------------------------------------------------------------
  2459. HRESULT CDomainMappingTable::HrBeginRerouteDomains()
  2460. {
  2461. HRESULT hr = S_OK;
  2462. DWORD dwFlags = 0;
  2463. m_slPrivateData.ExclusiveLock();
  2464. //Clear the failed bit before we make any calls into routing. This way
  2465. //we can detect a failure that has happened during this reset routes
  2466. dwInterlockedUnsetBits(&m_dwFlags, DMT_FLAGS_GET_NEXT_HOP_FAILED);
  2467. //Make sure this flag is set. This will prevent a reset routes request
  2468. //from being generated while this thread is resetting routes. If
  2469. //GetNextHop is still failing, we want to start the retry timer
  2470. //*after* we finish we reset routes, or we will get stuck in a loop
  2471. //of constant reset routes until GetNextHop succeeds.
  2472. dwInterlockedSetBits(&m_dwFlags, DMT_FLAGS_RESET_ROUTES_IN_PROGRESS);
  2473. //Move all of the domains to the currently unreachable link
  2474. hr = m_dnt.HrIterateOverSubDomains(NULL,
  2475. CDomainMappingTable::MakeSingleDomainCurrentlyUnreachable, this);
  2476. m_slPrivateData.ExclusiveUnlock();
  2477. //Handle a failure to move the domains - if this function fails then
  2478. //the caller won't call HrCompleteRerouteDomains so we need to clear
  2479. //the reset routes in progress bit and potentially request another
  2480. //ResetRoutes
  2481. if(FAILED(hr)) {
  2482. dwFlags = dwInterlockedUnsetBits(&m_dwFlags, DMT_FLAGS_RESET_ROUTES_IN_PROGRESS);
  2483. if (DMT_FLAGS_GET_NEXT_HOP_FAILED & dwFlags) {
  2484. //This reset routes failed... we must try again later.
  2485. RequestResetRoutesRetryIfNecessary();
  2486. }
  2487. }
  2488. return hr;
  2489. }
  2490. //---[ CDomainMappingTable::HrCompleteRerouteDomains ]-------------------------
  2491. //
  2492. //
  2493. // Description:
  2494. // This function completes the handling of a ResetRoutes. Mail flow has
  2495. // been restarted and this function will go through all the domains, now
  2496. // sitting in CurrentlyUnreachable, and will re-route them according to
  2497. // the new routing configuration.
  2498. // Parameters:
  2499. // NONE
  2500. // Returns:
  2501. // S_OK on success
  2502. // History:
  2503. // 11/5/1998 - MikeSwa Created
  2504. // 10/31/2000 - dbraun - renamed / was part of HrRerouteDomains
  2505. //
  2506. //-----------------------------------------------------------------------------
  2507. HRESULT CDomainMappingTable::HrCompleteRerouteDomains()
  2508. {
  2509. HRESULT hr = S_OK;
  2510. DWORD dwFlags = 0;
  2511. /* -- this would only be applicable if we supported a "partial retry
  2512. reset routes" that only called this function not the Begin above
  2513. // It is possible that we are being called WITHOUT HrBeginRerouteDomains
  2514. // being called first (if we failed here before the retry will only retry
  2515. // this part of rerouting) ... we need to make sure that
  2516. // DMT_FLAGS_RESET_ROUTES_IN_PROGRESS is set to ensure this proceeds safely
  2517. // and we need to make sure that DMT_FLAGS_GET_NEXT_HOP_FAILED is not set
  2518. // so we can pick up a failure during rerouting
  2519. dwInterlockedSetBits(&m_dwFlags, DMT_FLAGS_RESET_ROUTES_IN_PROGRESS);
  2520. dwInterlockedUnsetBits(&m_dwFlags, DMT_FLAGS_GET_NEXT_HOP_FAILED);
  2521. */
  2522. // Rebuild routing information queues in the CurrentlyUnreachable link.
  2523. hr = HrRerouteLink(m_plmqCurrentlyUnreachable);
  2524. dwFlags = dwInterlockedUnsetBits(&m_dwFlags, DMT_FLAGS_RESET_ROUTES_IN_PROGRESS);
  2525. if (DMT_FLAGS_GET_NEXT_HOP_FAILED & dwFlags)
  2526. {
  2527. // This reset routes failed... we must try again later.
  2528. RequestResetRoutesRetryIfNecessary();
  2529. }
  2530. return hr;
  2531. }
  2532. //---[ CDomainMappingTable::MakeSingleDomainCurrentlyUnreachable ]-------------
  2533. //
  2534. //
  2535. // Description:
  2536. // This iterator moves a given domain's queues to the CurrentlyUnreachable
  2537. // link to prevent SMTP from operating on them until they can be re-routed.
  2538. // Parameters:
  2539. // IN pvContext - pointer to context
  2540. // (pointer to BOOL that tells if we force a re-route)
  2541. // IN pvData - CDomainEntry for the given domain
  2542. // IN fWildcardData - TRUE if data is a wildcard entry
  2543. // OUT pfContinue - TRUE if iterator should continue to the next entry
  2544. // OUT pfDelete - TRUE if entry should be deleted
  2545. // Returns:
  2546. // -
  2547. // History:
  2548. // 10/31/2000 - dbraun - created
  2549. //
  2550. //-----------------------------------------------------------------------------
  2551. VOID CDomainMappingTable::MakeSingleDomainCurrentlyUnreachable(
  2552. PVOID pvContext, PVOID pvData,
  2553. BOOL fWildcard, BOOL *pfContinue,
  2554. BOOL *pfDelete)
  2555. {
  2556. TraceFunctEnterEx((LPARAM) pvContext, "CDomainMappingTable::MakeSingleDomainCurrentlyUnreachable");
  2557. CDomainEntry *pdentry = (CDomainEntry *) pvData;
  2558. HRESULT hr = S_OK;
  2559. CDestMsgQueue *pdmq = NULL;
  2560. CDomainMappingTable *pThis = (CDomainMappingTable *) pvContext;
  2561. CDomainEntryQueueIterator deqit;
  2562. CAQStats aqstat;
  2563. _ASSERT(pfContinue);
  2564. _ASSERT(pfDelete);
  2565. *pfDelete = FALSE;
  2566. *pfContinue = TRUE;
  2567. if (fWildcard)
  2568. return; //we shouldn't care about wildcard entries
  2569. _ASSERT(pdentry);
  2570. //
  2571. // Initialize the aqstat to type reroute
  2572. //
  2573. aqstat.m_dwNotifyType = NotifyTypeReroute;
  2574. //Loop through dmq's and blow away their routing information -
  2575. //then route them to the currently unreachable link
  2576. hr = deqit.HrInitialize(pdentry);
  2577. if (FAILED(hr))
  2578. pdmq = NULL;
  2579. else
  2580. pdmq = deqit.pdmqGetNextDestMsgQueue(pdmq);
  2581. while (pdmq)
  2582. {
  2583. //Remove this queue from it's link
  2584. pdmq->RemoveDMQFromLink(TRUE);
  2585. //
  2586. // Figure out if it is useful to route this queue
  2587. // before we call into routing
  2588. //
  2589. if (pdmq->fIsEmptyAndAbandoned())
  2590. pdentry->RemoveDestMsgQueue(pdmq);
  2591. else
  2592. {
  2593. //Add this queue to the currently unreachable link
  2594. hr = pThis->m_plmqCurrentlyUnreachable->HrAddQueue(pdmq);
  2595. if (FAILED(hr)) {
  2596. //We failed to add the queue to the currently unreachable link
  2597. ErrorTrace((LPARAM) pvContext,
  2598. "HrAddQueue failed on reroute hr - 0x%08X", hr);
  2599. hr = S_OK;
  2600. }
  2601. pdmq->SetRouteInfo(pThis->m_plmqCurrentlyUnreachable);
  2602. //
  2603. // Make sure the link is associated with the connection manager
  2604. //
  2605. hr = pThis->m_plmqCurrentlyUnreachable->HrNotify(&aqstat, TRUE);
  2606. if (FAILED(hr)) {
  2607. //If this fails... we *may* leak an unused link until the
  2608. //next reset routes (or shutdown).
  2609. ErrorTrace((LPARAM) pvContext,
  2610. "HrNotify failed on reroute hr - 0x%08X", hr);
  2611. hr = S_OK;
  2612. }
  2613. }
  2614. //On to the next queue for this entry
  2615. pdmq = deqit.pdmqGetNextDestMsgQueue(pdmq);
  2616. }
  2617. //
  2618. // If it is safe to clean this up... then do so
  2619. //
  2620. if (pdentry->fSafeToRemove())
  2621. {
  2622. *pfDelete = TRUE;
  2623. pdentry->Release();
  2624. pdentry = NULL;
  2625. }
  2626. TraceFunctLeave();
  2627. }
  2628. //---[ CDomainMappingTable::HrRerouteLink ]------------------------------------
  2629. //
  2630. //
  2631. // Description:
  2632. // Function that is used to re-route all queues on a given link.
  2633. // Parameters:
  2634. // IN plmq - pointer link to be rerouted
  2635. // Returns:
  2636. // -
  2637. // History:
  2638. // 11/8/2000 - dbraun created
  2639. //
  2640. //-----------------------------------------------------------------------------
  2641. HRESULT CDomainMappingTable::HrRerouteLink(CLinkMsgQueue *plmqReroute)
  2642. {
  2643. TraceFunctEnterEx((LPARAM) this, "CDomainMappingTable::HrRerouteLink");
  2644. CDomainEntry *pdentry = NULL;
  2645. CDestMsgQueue *pdmq = NULL;
  2646. CLinkMsgQueue *plmq = NULL;
  2647. CInternalDomainInfo *pIntDomainInfo = NULL;
  2648. HRESULT hr = S_OK;
  2649. HRESULT hrRoutingDiag = S_OK;
  2650. CQuickList *pqlQueueList = NULL;
  2651. DWORD cQueues = 0;
  2652. DWORD dwIndex = 0;
  2653. PVOID pvContext = NULL;
  2654. CDomainMapping *pdmap = NULL;
  2655. CAQStats aqstat;
  2656. _ASSERT(plmqReroute);
  2657. // Initialize the aqstat to type reroute
  2658. aqstat.m_dwNotifyType = NotifyTypeReroute;
  2659. // Loop through dmq's and re-route them
  2660. hr = plmqReroute->HrGetQueueListSnapshot(&pqlQueueList);
  2661. if (FAILED(hr))
  2662. {
  2663. cQueues = 0;
  2664. }
  2665. else
  2666. {
  2667. cQueues = pqlQueueList->dwGetCount();
  2668. }
  2669. for (dwIndex = 0; dwIndex < cQueues; dwIndex++)
  2670. {
  2671. // Get the dest msg queue
  2672. pdmq = (CDestMsgQueue *) pqlQueueList->pvGetItem(dwIndex, &pvContext);
  2673. // Get the domain for this queue
  2674. pdmq->GetDomainMapping(&pdmap);
  2675. _ASSERT(pdmap);
  2676. pdentry = pdmap->pdentryGetQueueEntry();
  2677. // Get the internal domain info for this domain
  2678. hr = m_paqinst->HrGetInternalDomainInfo(
  2679. pdentry->cbGetDomainNameLength(),
  2680. pdentry->szGetDomainName(),
  2681. &pIntDomainInfo);
  2682. if (FAILED(hr))
  2683. {
  2684. ErrorTrace((LPARAM) this, "ERROR: Unable to get config for domain - hr 0x%08X", hr);
  2685. pIntDomainInfo = NULL;
  2686. }
  2687. if (pIntDomainInfo)
  2688. {
  2689. hr = HrGetNextHopLink(
  2690. pdentry, pdentry->szGetDomainName(),
  2691. pdentry->cbGetDomainNameLength(), pIntDomainInfo,
  2692. pdmq->paqmtGetMessageType(), pdmq->pIMessageRouterGetRouter(),
  2693. FALSE, // DMT is not locked
  2694. &plmq, &hrRoutingDiag);
  2695. if (FAILED(hr))
  2696. {
  2697. //$$TODO - Deal with more exotic Get next hop errors
  2698. ErrorTrace((LPARAM) this,
  2699. "ERROR: Unable to get next hop for domain - hr 0x%08X", hr);
  2700. }
  2701. // Update with new routing info if we can
  2702. if (SUCCEEDED(hr))
  2703. {
  2704. // Remove from the old link
  2705. pdmq->RemoveDMQFromLink(TRUE);
  2706. // Add to the new one
  2707. hr = plmq->HrAddQueue(pdmq);
  2708. if (FAILED(hr))
  2709. {
  2710. // Release the link we couldn't use and get the
  2711. // currently unreachable link
  2712. plmq->Release();
  2713. plmq = plmqGetCurrentlyUnreachable();
  2714. if (plmq)
  2715. hr = plmq->HrAddQueue(pdmq);
  2716. // If we still have a failed hr, remove the DMQ from
  2717. // it's link, otherwise, make sure it is associated with
  2718. // the link we just added it to
  2719. if (FAILED(hr))
  2720. pdmq->RemoveDMQFromLink(TRUE);
  2721. else
  2722. pdmq->SetRouteInfo(plmq);
  2723. // Request reset routes retry
  2724. RequestResetRoutesRetryIfNecessary();
  2725. }
  2726. else
  2727. pdmq->SetRouteInfo(plmq);
  2728. // Set routing error if there was one.
  2729. pdmq->SetRoutingDiagnostic(hrRoutingDiag);
  2730. //
  2731. // Make sure the link is associated with the connection manager
  2732. //
  2733. hr = plmq->HrNotify(&aqstat, TRUE);
  2734. if (FAILED(hr))
  2735. {
  2736. // If this fails... we *may* leak an unused link until the
  2737. // next reset routes (or shutdown).
  2738. ErrorTrace((LPARAM) this,
  2739. "HrNotify failed on reroute hr - 0x%08X", hr);
  2740. hr = S_OK;
  2741. }
  2742. plmq->Release();
  2743. plmq = NULL;
  2744. }
  2745. // Release the domain info
  2746. pIntDomainInfo->Release();
  2747. pIntDomainInfo = NULL;
  2748. }
  2749. }
  2750. _ASSERT(!pIntDomainInfo);
  2751. if (pqlQueueList)
  2752. delete pqlQueueList;
  2753. TraceFunctLeave();
  2754. return hr;
  2755. }
  2756. //---[ CDomainMappingTable::ProcessSpecialLinks ]------------------------------
  2757. //
  2758. //
  2759. // Description:
  2760. // Processes all the special global links to handle things like local
  2761. // delivery.
  2762. // Parameters:
  2763. // IN cSpecialRetryMinutes minutes to retry currently unreachable
  2764. // if 0, will use previous value
  2765. // IN fRoutingLockHeld
  2766. // Returns:
  2767. // -
  2768. // History:
  2769. // 1/26/99 - MikeSwa Created
  2770. // 3/25/99 - MikeSwa Added fRoutingLockHeld to fix GetNextMsgRef deadlock
  2771. //
  2772. //-----------------------------------------------------------------------------
  2773. void CDomainMappingTable::ProcessSpecialLinks(DWORD cSpecialRetryMinutes,
  2774. BOOL fRoutingLockHeld)
  2775. {
  2776. HRESULT hr = S_OK;
  2777. CMsgRef *pmsgref = NULL;
  2778. CAQSvrInst *paqinst = m_paqinst;
  2779. BOOL fSchedRetry = FALSE;
  2780. BOOL fShutdownLock = FALSE;
  2781. //If this thread has the routing lock... we are safe from shutdown
  2782. _ASSERT(m_paqinst || !fRoutingLockHeld);
  2783. if (!paqinst)
  2784. return; //we must be shutting down
  2785. if (!fRoutingLockHeld)
  2786. {
  2787. //It is safe to access paqinst to acquire the shutdown lock because
  2788. //all threads that can call this function will be gone before the
  2789. //reference count on the server instance is 0 (however, this call
  2790. //could happen during deinialization which would cause m_aqinst
  2791. //to be NULL and the following call to return FALSE).
  2792. if (!paqinst->fTryShutdownLock())
  2793. return; //we are shutting down
  2794. //Now we have the shutdown lock... m_aqinst must be safe
  2795. _ASSERT(m_paqinst);
  2796. if (!m_paqinst)
  2797. {
  2798. //might as well be defensive in retail about this.
  2799. paqinst->ShutdownUnlock();
  2800. return;
  2801. }
  2802. fShutdownLock = TRUE;
  2803. }
  2804. if (!(DMT_FLAGS_SPECIAL_DELIVERY_SPINLOCK &
  2805. dwInterlockedSetBits(&m_dwFlags, DMT_FLAGS_SPECIAL_DELIVERY_SPINLOCK)))
  2806. {
  2807. //we have the lock... only 1 thread at a time should do this
  2808. //Loop over queue and enqueue for local delivery
  2809. while (SUCCEEDED(hr) && m_plmqLocal)
  2810. {
  2811. hr = m_plmqLocal->HrGetNextMsgRef(fRoutingLockHeld, &pmsgref);
  2812. if (FAILED(hr))
  2813. break;
  2814. _ASSERT(pmsgref);
  2815. m_paqinst->QueueMsgForLocalDelivery(pmsgref, TRUE);
  2816. pmsgref->Release();
  2817. pmsgref = NULL;
  2818. }
  2819. //Tell link to walk queues so they will be added to the empty list
  2820. //for deletion
  2821. if (m_plmqLocal)
  2822. {
  2823. m_plmqLocal->GenerateDSNsIfNecessary(TRUE /*check if empty */,
  2824. TRUE /*merge retry queue only*/);
  2825. }
  2826. //NDR all messages in the unreachable link
  2827. if (m_plmqUnreachable)
  2828. {
  2829. //We must hold the routing lock for DSN generation
  2830. if (!fRoutingLockHeld)
  2831. m_paqinst->RoutingShareLock();
  2832. m_plmqUnreachable->SetLastConnectionFailure(AQUEUE_E_NDR_ALL);
  2833. m_plmqUnreachable->GenerateDSNsIfNecessary(FALSE, FALSE);
  2834. if (!fRoutingLockHeld)
  2835. m_paqinst->RoutingShareUnlock();
  2836. }
  2837. //Process the currently unreachable link ... unless we are currently
  2838. //doing a ResetRoutes. If we are we must skip this because the
  2839. //currently unreachable link contains queues we should not process
  2840. //here - this function will be called again when ResetRoutes completes
  2841. if (m_plmqCurrentlyUnreachable &&
  2842. !(m_dwFlags & DMT_FLAGS_RESET_ROUTES_IN_PROGRESS))
  2843. {
  2844. if (cSpecialRetryMinutes)
  2845. {
  2846. //new time is sooner... we better ask for a retry
  2847. if (cSpecialRetryMinutes < m_cSpecialRetryMinutes)
  2848. fSchedRetry = TRUE;
  2849. InterlockedExchange((PLONG)&m_cSpecialRetryMinutes,
  2850. cSpecialRetryMinutes);
  2851. }
  2852. if (!(DMT_FLAGS_SPECIAL_DELIVERY_CALLBACK &
  2853. dwInterlockedSetBits(&m_dwFlags, DMT_FLAGS_SPECIAL_DELIVERY_CALLBACK)))
  2854. {
  2855. //We are the only thread doing this
  2856. //We must hold the routing lock for DSN generation
  2857. if (!fRoutingLockHeld)
  2858. m_paqinst->RoutingShareLock();
  2859. m_plmqCurrentlyUnreachable->GenerateDSNsIfNecessary(FALSE, FALSE);
  2860. dwInterlockedUnsetBits(&m_dwFlags, DMT_FLAGS_SPECIAL_DELIVERY_CALLBACK);
  2861. fSchedRetry = TRUE;
  2862. if (!fRoutingLockHeld)
  2863. m_paqinst->RoutingShareUnlock();
  2864. }
  2865. //periodically check the currently unreachable link... to genrate NDRs
  2866. if (fSchedRetry)
  2867. {
  2868. dwInterlockedSetBits(&m_dwFlags, DMT_FLAGS_SPECIAL_DELIVERY_CALLBACK);
  2869. if (m_paqinst)
  2870. {
  2871. hr = m_paqinst->SetCallbackTime(
  2872. CDomainMappingTable::SpecialRetryCallback, this,
  2873. m_cSpecialRetryMinutes);
  2874. if (FAILED(hr))
  2875. {
  2876. //Unmark bits to we try next time
  2877. dwInterlockedUnsetBits(&m_dwFlags,
  2878. DMT_FLAGS_SPECIAL_DELIVERY_CALLBACK);
  2879. hr = S_OK;
  2880. }
  2881. }
  2882. }
  2883. }
  2884. //Release the lock
  2885. dwInterlockedUnsetBits(&m_dwFlags, DMT_FLAGS_SPECIAL_DELIVERY_SPINLOCK);
  2886. }
  2887. if (fShutdownLock)
  2888. {
  2889. _ASSERT(m_paqinst);
  2890. m_paqinst->ShutdownUnlock();
  2891. }
  2892. }
  2893. //---[ CDomainMappingTable::HrPrepareForLocalDelivery ]------------------------
  2894. //
  2895. //
  2896. // Description:
  2897. // Prepares a message for local delivery using the m_plmqLocal Link.
  2898. // Parameters:
  2899. // IN pmsgref MsgRef to prepare for delivery
  2900. // IN fLocal Prepare delivery for all domains with NULL queues
  2901. // IN fDelayDSN Check/Set Delay bitmap (only send 1 Delay DSN).
  2902. // IN pqlstQueues QuickList of DMQ's
  2903. // IN OUT pdcntxt context that must be returned on Ack
  2904. // OUT pcRecips # of recips to deliver for
  2905. // OUT prgdwRecips Array of recipient indexes
  2906. // Returns:
  2907. // S_OK on success
  2908. // E_FAIL if m_plmqLocal is not initialized
  2909. // History:
  2910. // 1/26/99 - MikeSwa Created
  2911. //
  2912. //-----------------------------------------------------------------------------
  2913. HRESULT CDomainMappingTable::HrPrepareForLocalDelivery(
  2914. IN CMsgRef *pmsgref,
  2915. IN BOOL fDelayDSN,
  2916. IN OUT CDeliveryContext *pdcntxt,
  2917. OUT DWORD *pcRecips,
  2918. OUT DWORD **prgdwRecips)
  2919. {
  2920. if (m_plmqLocal)
  2921. {
  2922. return m_plmqLocal->HrPrepareDelivery(pmsgref, TRUE, fDelayDSN,
  2923. pdcntxt, pcRecips, prgdwRecips);
  2924. }
  2925. else
  2926. return E_FAIL;
  2927. }
  2928. DWORD CDomainMappingTable::GetCurrentlyUnreachableTotalMsgCount() {
  2929. return m_plmqCurrentlyUnreachable->cGetTotalMsgCount();
  2930. }
  2931. //---[ CDomainMappingTable::plmqGetLocalLink ]---------------------------------
  2932. //
  2933. //
  2934. // Description:
  2935. // Returns an addref'd pointer to the local link object
  2936. // Parameters:
  2937. // -
  2938. // Returns:
  2939. // AddRef'd pointer to the local link object
  2940. // History:
  2941. // 2/22/99 - MikeSwa Created
  2942. // 1/28/2000 - MikeSwa Modified to make shutdown safe
  2943. //
  2944. //-----------------------------------------------------------------------------
  2945. CLinkMsgQueue *CDomainMappingTable::plmqGetLocalLink()
  2946. {
  2947. CAQSvrInst *paqinst = m_paqinst;
  2948. CLinkMsgQueue *plmq = NULL;
  2949. //
  2950. // If paqinst is non-NULL, then we have the possibility of not being
  2951. // shutdown. If we are shutdown, it is possible that m_plmqLocal is
  2952. // in the process of being released (which would be a really bad
  2953. // time to addref/release it). We can safely access paqinst, since
  2954. // this class is a member of CAQSvrInst.
  2955. //
  2956. if (paqinst && paqinst->fTryShutdownLock())
  2957. {
  2958. plmq = m_plmqLocal;
  2959. if (plmq)
  2960. plmq->AddRef();
  2961. paqinst->ShutdownUnlock();
  2962. }
  2963. return plmq;
  2964. }
  2965. //---[ CDomainMappingTable::plmqGetCurrentlyUnreachable]---------------------------------
  2966. //
  2967. //
  2968. // Description:
  2969. // Returns an addref'd pointer to the currently unreachable link object
  2970. // Parameters:
  2971. // -
  2972. // Returns:
  2973. // AddRef'd pointer to the currently unreachable link object
  2974. // History:
  2975. // 6/21/99 - GPulla Created
  2976. //
  2977. //-----------------------------------------------------------------------------
  2978. CLinkMsgQueue *CDomainMappingTable::plmqGetCurrentlyUnreachable()
  2979. {
  2980. if(m_plmqCurrentlyUnreachable)
  2981. m_plmqCurrentlyUnreachable->AddRef();
  2982. return m_plmqCurrentlyUnreachable;
  2983. }
  2984. //---[ CDomainMappingTable::pmmaqGetPreCategorized]---------------------------------
  2985. //
  2986. //
  2987. // Description:
  2988. // Returns an addref'd pointer to the precategorized queue object
  2989. // Parameters:
  2990. // -
  2991. // AddRef'd pointer to the precategorized link object.
  2992. // History:
  2993. // 6/21/99 - GPulla Created
  2994. //
  2995. //-----------------------------------------------------------------------------
  2996. CMailMsgAdminLink *CDomainMappingTable::pmmaqGetPreCategorized()
  2997. {
  2998. if(m_pmmaqPreCategorized)
  2999. m_pmmaqPreCategorized->AddRef();
  3000. return m_pmmaqPreCategorized;
  3001. }
  3002. //---[ CDomainMappingTable::pmmaqGetPreRouting]---------------------------------
  3003. //
  3004. //
  3005. // Description:
  3006. // Returns an addref'd pointer to the pre routing queue object
  3007. // Parameters:
  3008. // -
  3009. // AddRef'd pointer to the precategorized link object.
  3010. // History:
  3011. // 6/21/99 - GPulla Created
  3012. //
  3013. //-----------------------------------------------------------------------------
  3014. CMailMsgAdminLink *CDomainMappingTable::pmmaqGetPreRouting()
  3015. {
  3016. if(m_pmmaqPreRouting)
  3017. m_pmmaqPreRouting->AddRef();
  3018. return m_pmmaqPreRouting;
  3019. }
  3020. //---[ CDomainMappingTable::pmmaqGetPreRouting]---------------------------------
  3021. //
  3022. //
  3023. // Description:
  3024. // Returns an addref'd pointer to the pre routing queue object
  3025. // Parameters:
  3026. // -
  3027. // AddRef'd pointer to the precategorized link object.
  3028. // History:
  3029. // 6/21/99 - GPulla Created
  3030. //
  3031. //-----------------------------------------------------------------------------
  3032. CMailMsgAdminLink *CDomainMappingTable::pmmaqGetPreSubmission()
  3033. {
  3034. if(m_pmmaqPreSubmission)
  3035. m_pmmaqPreSubmission->AddRef();
  3036. return m_pmmaqPreSubmission;
  3037. }
  3038. //---[ CDomainEntryIterator::CDomainEntryIterator ]----------------------------
  3039. //
  3040. //
  3041. // Description:
  3042. // Constructor for CDomainEntryIterator
  3043. // Parameters:
  3044. // -
  3045. // Returns:
  3046. // -
  3047. // History:
  3048. // 8/19/99 - MikeSwa Created
  3049. //
  3050. //-----------------------------------------------------------------------------
  3051. CDomainEntryIterator::CDomainEntryIterator()
  3052. {
  3053. m_dwSignature = DOMAIN_ENTRY_ITERATOR_SIG;
  3054. m_cItems = 0;
  3055. m_iCurrentItem = 0;
  3056. m_rgpvItems = NULL;
  3057. }
  3058. //---[ CDomainEntryIterator::Recycle ]-----------------------------------------
  3059. //
  3060. //
  3061. // Description:
  3062. // CDomainEntryIterator Recycle... destroys using virtual functions
  3063. // Parameters:
  3064. // -
  3065. // Returns:
  3066. // -
  3067. // History:
  3068. // 8/19/99 - MikeSwa Created
  3069. //
  3070. //-----------------------------------------------------------------------------
  3071. VOID CDomainEntryIterator::Recycle()
  3072. {
  3073. DWORD iCurrentItem = 0;
  3074. for (iCurrentItem = 0; iCurrentItem < m_cItems; iCurrentItem++)
  3075. {
  3076. _ASSERT(m_rgpvItems);
  3077. if (!m_rgpvItems)
  3078. break;
  3079. if (m_rgpvItems[iCurrentItem])
  3080. {
  3081. ReleaseItem(m_rgpvItems[iCurrentItem]);
  3082. m_rgpvItems[iCurrentItem] = NULL;
  3083. }
  3084. }
  3085. if (m_rgpvItems)
  3086. {
  3087. FreePv(m_rgpvItems);
  3088. m_rgpvItems = NULL;
  3089. }
  3090. m_cItems = 0;
  3091. m_iCurrentItem = 0;
  3092. }
  3093. //---[ CDomainEntryIterator::pvGetNext ]---------------------------------------
  3094. //
  3095. //
  3096. // Description:
  3097. // Gets the next Item
  3098. // Parameters:
  3099. // -
  3100. // Returns:
  3101. // The next item in the iterator
  3102. // NULL if at last item
  3103. // History:
  3104. // 8/19/99 - MikeSwa Created
  3105. //
  3106. //-----------------------------------------------------------------------------
  3107. PVOID CDomainEntryIterator::pvGetNext()
  3108. {
  3109. PVOID pvRet = NULL;
  3110. if (m_rgpvItems && (m_iCurrentItem < m_cItems))
  3111. {
  3112. pvRet = m_rgpvItems[m_iCurrentItem];
  3113. _ASSERT(pvRet);
  3114. m_iCurrentItem++;
  3115. }
  3116. return pvRet;
  3117. }
  3118. //---[ CDomainEntryIterator::HrInitialize ]------------------------------------
  3119. //
  3120. //
  3121. // Description:
  3122. // Initializes a CDomainEntryIterator from a given CDomainEntry
  3123. // Parameters:
  3124. // pdentry CDomainEntry to initialize from
  3125. // Returns:
  3126. // S_OK on success
  3127. // E_POINTER if pdentry is NULL
  3128. // E_INVALIDARG if HrInitialize has already be called for this iterator
  3129. // E_OUTOFMEMORY if we cannot allocate memory for iterator
  3130. // History:
  3131. // 8/20/99 - MikeSwa Created
  3132. //
  3133. //-----------------------------------------------------------------------------
  3134. HRESULT CDomainEntryIterator::HrInitialize(CDomainEntry *pdentry)
  3135. {
  3136. TraceFunctEnterEx((LPARAM) this, "CDomainEntryIterator::HrInitialize");
  3137. HRESULT hr = S_OK;
  3138. BOOL fEntryLocked = FALSE;
  3139. DWORD cAddedItems = 0;
  3140. DWORD cItems = 0;
  3141. PLIST_ENTRY pli = NULL;
  3142. PLIST_ENTRY pliHead = NULL;
  3143. CLinkMsgQueue *plmq = NULL;
  3144. _ASSERT(pdentry);
  3145. _ASSERT(!m_rgpvItems && "Iterator initialized twice");
  3146. if (!pdentry)
  3147. {
  3148. hr = E_POINTER;
  3149. ErrorTrace((LPARAM) this, "NULL pdentry used to initialized iterator");
  3150. goto Exit;
  3151. }
  3152. if (m_rgpvItems)
  3153. {
  3154. hr = E_INVALIDARG;
  3155. ErrorTrace((LPARAM) this, "Iterator initialized twice!");
  3156. goto Exit;
  3157. }
  3158. pdentry->m_slPrivateData.ShareLock();
  3159. fEntryLocked = TRUE;
  3160. cItems = cItemsFromDomainEntry(pdentry);
  3161. if (!cItems) //Empty entry
  3162. goto Exit;
  3163. m_rgpvItems = (PVOID *) pvMalloc(sizeof(PVOID) * cItems);
  3164. if (!m_rgpvItems)
  3165. {
  3166. hr = E_OUTOFMEMORY;
  3167. ErrorTrace((LPARAM) this,
  3168. "Unable to allocate memory for iterator of size %d", cItems);
  3169. goto Exit;
  3170. }
  3171. ZeroMemory(m_rgpvItems, sizeof(PVOID)*cItems);
  3172. pliHead = pliHeadFromDomainEntry(pdentry);
  3173. _ASSERT(pliHead);
  3174. pli = pliHead->Flink;
  3175. while(pliHead != pli)
  3176. {
  3177. _ASSERT(pli);
  3178. m_rgpvItems[cAddedItems] = pvItemFromListEntry(pli);
  3179. _ASSERT(m_rgpvItems[cAddedItems]);
  3180. if (m_rgpvItems[cAddedItems])
  3181. cAddedItems++;
  3182. pli = pli->Flink;
  3183. _ASSERT(cAddedItems <= cItems); //We've run out of room
  3184. if (cAddedItems > cItems)
  3185. break;
  3186. }
  3187. _ASSERT(cAddedItems == cItems);
  3188. m_cItems = cAddedItems;
  3189. Exit:
  3190. if (fEntryLocked)
  3191. pdentry->m_slPrivateData.ShareUnlock();
  3192. TraceFunctLeave();
  3193. return hr;
  3194. }
  3195. //---[ CDomainEntryLinkIterator ]---------------------------------------------
  3196. //
  3197. //
  3198. // Description:
  3199. // Releases a CLinkMsgQueue during the CDomainEntryQueueIterator
  3200. // destructor
  3201. // Parameters:
  3202. // pvItem LMQ to release
  3203. // Returns:
  3204. // -
  3205. // History:
  3206. // 8/19/99 - MikeSwa Created
  3207. //
  3208. //-----------------------------------------------------------------------------
  3209. VOID CDomainEntryLinkIterator::ReleaseItem(PVOID pvItem)
  3210. {
  3211. CLinkMsgQueue *plmq = (CLinkMsgQueue *) pvItem;
  3212. _ASSERT(plmq);
  3213. plmq->Release();
  3214. }
  3215. //---[ CDomainEntryLinkIterator::pvItemFromListEntry ]-------------------------
  3216. //
  3217. //
  3218. // Description:
  3219. // Returns a CLinkMsgQueue item from the given LIST_ENTRY
  3220. // Parameters:
  3221. // pli LIST_ENTRY to get LMQ from... *must* be non-NULL
  3222. // Returns:
  3223. // PVOID for Addref'd CLinkMsgQueue
  3224. // History:
  3225. // 8/20/99 - MikeSwa Created
  3226. //
  3227. //-----------------------------------------------------------------------------
  3228. PVOID CDomainEntryLinkIterator::pvItemFromListEntry(PLIST_ENTRY pli)
  3229. {
  3230. CLinkMsgQueue *plmq = CLinkMsgQueue::plmqGetLinkMsgQueue(pli);
  3231. _ASSERT(plmq);
  3232. _ASSERT(pli);
  3233. plmq->AddRef();
  3234. return plmq;
  3235. }
  3236. //---[ CDomainEntryLinkIterator::plmqGetNextLinkMsgQueue ]---------------------
  3237. //
  3238. //
  3239. // Description:
  3240. // Gets the next LMQ for this iterator
  3241. // Parameters:
  3242. // plmq to release... addref'd by previous call
  3243. // Returns:
  3244. // Addref'd Next LMQ.
  3245. // NULL if no more left
  3246. // History:
  3247. // 8/19/99 - MikeSwa Created
  3248. //
  3249. //-----------------------------------------------------------------------------
  3250. CLinkMsgQueue *CDomainEntryLinkIterator::plmqGetNextLinkMsgQueue(
  3251. CLinkMsgQueue *plmq)
  3252. {
  3253. CLinkMsgQueue *plmqNext = (CLinkMsgQueue *) pvGetNext();
  3254. if (plmqNext)
  3255. plmqNext->AddRef();
  3256. if (plmq)
  3257. plmq->Release();
  3258. return plmqNext;
  3259. }
  3260. //---[ CDomainEntryQueueIterator::pdmqGetNextDestMsgQueue ]--------------------
  3261. //
  3262. //
  3263. // Description:
  3264. // Get the next DMQ in the iterator
  3265. // Parameters:
  3266. // pdmq CDestMsgQueue to release... addref'd by previous call
  3267. // Returns:
  3268. // Addref'd next DMQ
  3269. // NULL if no more DMQ's for iterator
  3270. // History:
  3271. // 8/19/99 - MikeSwa Created
  3272. //
  3273. //-----------------------------------------------------------------------------
  3274. CDestMsgQueue *CDomainEntryQueueIterator::pdmqGetNextDestMsgQueue(
  3275. CDestMsgQueue *pdmq)
  3276. {
  3277. CDestMsgQueue *pdmqNext = (CDestMsgQueue *) pvGetNext();
  3278. if (pdmqNext)
  3279. {
  3280. pdmqNext->AssertSignature();
  3281. pdmqNext->AddRef();
  3282. }
  3283. if (pdmq)
  3284. {
  3285. pdmq->AssertSignature();
  3286. pdmq->Release();
  3287. }
  3288. return pdmqNext;
  3289. }
  3290. //---[ CDomainEntryDestIterator::pvItemFromListEntry ]-------------------------
  3291. //
  3292. //
  3293. // Description:
  3294. // Returns a CDestMsgQueue item from the given LIST_ENTRY
  3295. // Parameters:
  3296. // pli LIST_ENTRY to get DMQ from... *must* be non-NULL
  3297. // Returns:
  3298. // PVOID for Addref'd CDestMsgQueue
  3299. // History:
  3300. // 8/20/99 - MikeSwa Created
  3301. //
  3302. //-----------------------------------------------------------------------------
  3303. PVOID CDomainEntryQueueIterator::pvItemFromListEntry(PLIST_ENTRY pli)
  3304. {
  3305. CDestMsgQueue *pdmq = CDestMsgQueue::pdmqGetDMQFromDomainListEntry(pli);
  3306. _ASSERT(pdmq);
  3307. _ASSERT(pli);
  3308. pdmq->AssertSignature();
  3309. pdmq->AddRef();
  3310. return (PVOID) pdmq;
  3311. }
  3312. //---[ CDomainEntryQueueIterator ]---------------------------------------------
  3313. //
  3314. //
  3315. // Description:
  3316. // Releases a CDestMsgQueue during the CDomainEntryQueueIterator
  3317. // destructor
  3318. // Parameters:
  3319. // pvItem DMQ to release
  3320. // Returns:
  3321. // -
  3322. // History:
  3323. // 8/19/99 - MikeSwa Created
  3324. //
  3325. //-----------------------------------------------------------------------------
  3326. VOID CDomainEntryQueueIterator::ReleaseItem(PVOID pvItem)
  3327. {
  3328. CDestMsgQueue *pdmq = (CDestMsgQueue *) pvItem;
  3329. _ASSERT(pdmq);
  3330. pdmq->AssertSignature();
  3331. pdmq->Release();
  3332. }