//----------------------------------------------------------------------------- // // // File: domain.h // // Description: // Contains descriptions of the Domain table management structure // // Author: mikeswa // // Copyright (C) 1997 Microsoft Corporation // //----------------------------------------------------------------------------- #ifndef _DOMAIN_H_ #define _DOMAIN_H_ #include "cmt.h" #include #include #include #include class CInternalDomainInfo; class CDestMsgQueue; class CDomainEntry; class CDomainMappingTable; class CAQSvrInst; class CAQMessageType; class CLinkMsgQueue; class CLocalLinkMsgQueue; class CAQScheduleID; class CMsgRef; class CDeliveryContext; class CAsyncAdminMsgRefQueue; class CAsyncAdminMailMsgQueue; class CMailMsgAdminLink; class CDomainEntryLinkIterator; class CDomainEntryQueueIterator; class CDomainEntryIterator; #include "asyncq.h" #define DOMAIN_ENTRY_SIG 'tnED' #define DOMAIN_ENTRY_ITERATOR_SIG 'ItnD' #define DOMAIN_MAPPING_TABLE_SIG ' TMD' //Name used for global 'local' link #define LOCAL_LINK_NAME "LocalLink" #define UNREACHABLE_LINK_NAME "UnreachableLink" #define CURRENTLY_UNREACHABLE_LINK_NAME "CurrentlyUnreachableLink" #define PRECAT_QUEUE_NAME "PreCatQueue" #define PREROUTING_QUEUE_NAME "PreRoutingQueue" #define PRESUBMISSION_QUEUE_NAME "PreSubmissionQueue" #define POSTDSN_QUEUE_NAME "PostDSNGenerationQueue" // {34E2DCCA-C91A-11d2-A6B1-00C04FA3490A} static const GUID g_sGuidLocalLink = { 0x34e2dcca, 0xc91a, 0x11d2, { 0xa6, 0xb1, 0x0, 0xc0, 0x4f, 0xa3, 0x49, 0xa } }; // {CD08CEE0-2A95-11d3-B38E-00C04F6B6167} static const GUID g_sGuidPrecatLink = { 0xcd08cee0, 0x2a95, 0x11d3, { 0xb3, 0x8e, 0x0, 0xc0, 0x4f, 0x6b, 0x61, 0x67 } }; // {98C90E90-2BB5-11d3-B390-00C04F6B6167} static const GUID g_sGuidPreRoutingLink = { 0x98c90e90, 0x2bb5, 0x11d3, { 0xb3, 0x90, 0x0, 0xc0, 0x4f, 0x6b, 0x61, 0x67 } }; // {0F5C33F2-B83A-41fa-9BE3-69C6D1314E13} static const GUID g_sGuidPreSubmissionLink = { 0xf5c33f2, 0xb83a, 0x41fa, { 0x9b, 0xe3, 0x69, 0xc6, 0xd1, 0x31, 0x4e, 0x13 } }; #define DMT_FLAGS_SPECIAL_DELIVERY_SPINLOCK 0x80000000 #define DMT_FLAGS_SPECIAL_DELIVERY_CALLBACK 0x40000000 //Bits used to retry GetNextHop after it has failed. If //DMT_FLAGS_RESET_ROUTES_IN_PROGRESS is set, then a reset routes attempt is //in progress, and we should not have more than one attempt pending. If //DMT_FLAGS_GET_NEXT_HOP_FAILED is set, then a failure has been encountered //since the last reset routes. #define DMT_FLAGS_RESET_ROUTES_IN_PROGRESS 0x20000000 #define DMT_FLAGS_GET_NEXT_HOP_FAILED 0x10000000 //---[ DomainMapping ]--------------------------------------------------------- // // // Hungarian: dmap, pdmap // // unquely identifies a queue / domain name pair. // Each Domain mapping that contains the same QueueID, belongs to // same queue. // // This entire ID should be treated as an opaque to the outside world. // // This class is essentially an abstraction that can allow us to add another // layer of indirection. Configuration based grouping of queues.... static // routing instead of dynamic. //----------------------------------------------------------------------------- class CDomainMapping { public: //removed constructor so we could have this in a union... works fine, but //you have to set mapping with manually or via clone void Clone(IN CDomainMapping *pdmap); //Returns a ptr to the DestMsgQueue associated with this object HRESULT HrGetDestMsgQueue(IN CAQMessageType *paqmt, OUT CDestMsgQueue **ppdmq); friend class CDomainEntry; //provide sorting operators friend bool operator <(CDomainMapping &pdmap1, CDomainMapping &pdmap2) {return (pdmap1.m_pdentryDomainID < pdmap2.m_pdentryDomainID);}; friend bool operator >(CDomainMapping &pdmap1, CDomainMapping &pdmap2) {return (pdmap1.m_pdentryDomainID > pdmap2.m_pdentryDomainID);}; friend bool operator <=(CDomainMapping &pdmap1, CDomainMapping &pdmap2) {return (pdmap1.m_pdentryDomainID <= pdmap2.m_pdentryDomainID);}; friend bool operator >=(CDomainMapping &pdmap1, CDomainMapping &pdmap2) {return (pdmap1.m_pdentryDomainID >= pdmap2.m_pdentryDomainID);}; //Compressed Queues will not be supported... this will be a sufficient test friend bool operator ==(CDomainMapping &pdmap1, CDomainMapping &pdmap2) {return (pdmap1.m_pdentryDomainID == pdmap2.m_pdentryDomainID);}; CDomainEntry *pdentryGetQueueEntry() {return m_pdentryQueueID;}; protected: CDomainEntry *m_pdentryDomainID; CDomainEntry *m_pdentryQueueID; }; //---[ CDomainEntry ]---------------------------------------------------------- // // // Hungarian: dentry pdentry // // Represents a entry in the Domain Name Mapping Table //----------------------------------------------------------------------------- class CDomainEntry : public CBaseObject { protected: DWORD m_dwSignature; CShareLockInst m_slPrivateData; //Share lock used to maintain lists CDomainMapping m_dmap; //Domain mapping for this domain DWORD m_cbDomainName; LPSTR m_szDomainName; //Domain name for this entry DWORD m_cQueues; DWORD m_cLinks; CAQSvrInst *m_paqinst; LIST_ENTRY m_liDestQueues; //linked list of dest queues for this domain name LIST_ENTRY m_liLinks; //linked list of links for this domain name friend class CDomainEntryIterator; friend class CDomainEntryLinkIterator; friend class CDomainEntryQueueIterator; public: CDomainEntry(CAQSvrInst *paqinst); ~CDomainEntry(); HRESULT HrInitialize( DWORD cbDomainName, //string length of domain name LPSTR szDomainName, //domain name for entry CDomainEntry *pdentryQueueID, //primary entry for this domain CDestMsgQueue *pdmq, //queue prt for this entry //NULL if not primary CLinkMsgQueue *plmq); HRESULT HrDeinitialize(); //Returns the Domain Mapping associated with this object HRESULT HrGetDomainMapping(OUT CDomainMapping *pdmap); //Returns the Domain Name associated with this object //Caller is responsible for freeing string HRESULT HrGetDomainName(OUT LPSTR *pszDomainName); //Returns a ptr to the DestMsgQueue associated with this object HRESULT HrGetDestMsgQueue(IN CAQMessageType *paqmt, OUT CDestMsgQueue **ppdmq); //Add a queue to this domain entry if one does not already exist for that message type HRESULT HrAddUniqueDestMsgQueue(IN CDestMsgQueue *pdmqNew, OUT CDestMsgQueue **ppdmqCurrent); //Returns a ptr to the DestMsgQueue associated with this object HRESULT HrGetLinkMsgQueue(IN CAQScheduleID *paqsched, OUT CLinkMsgQueue **pplmq); //Add a queue to this domain entry if one does not already exist for that message type HRESULT HrAddUniqueLinkMsgQueue(IN CLinkMsgQueue *plmqNew, OUT CLinkMsgQueue **pplmqCurrent); void RemoveDestMsgQueue(IN CDestMsgQueue *pdmq); void RemoveLinkMsgQueue(IN CLinkMsgQueue *plmq); //returns internal ptr to domain name... use HrGetDomainName if you //are not *directly* tied to the life span of a domain entry inline LPSTR szGetDomainName() {return m_szDomainName;}; inline DWORD cbGetDomainNameLength() {return m_cbDomainName;}; inline void InitDomainString(PDOMAIN_STRING pDomain); //Is it safe to get rid of this domain entry? inline BOOL fSafeToRemove() { return (BOOL) ((m_lReferences == 1) && (m_cQueues == 0) && (m_cLinks == 0));} }; //---[ CDomainEntryIterator ]-------------------------------------------------- // // // Description: // Base iterator class for domain entry. Provides a consistent snapshot // of the elements of a domain entry // Hungarian: // deit, pdeit // //----------------------------------------------------------------------------- class CDomainEntryIterator { protected: DWORD m_dwSignature; DWORD m_cItems; DWORD m_iCurrentItem; PVOID *m_rgpvItems; protected: CDomainEntryIterator(); PVOID pvGetNext(); VOID Recycle(); virtual VOID ReleaseItem(PVOID pvItem) {_ASSERT(FALSE && "Base virtual function");}; virtual PVOID pvItemFromListEntry(PLIST_ENTRY pli) {_ASSERT(FALSE && "Base virtual function");return NULL;}; virtual PLIST_ENTRY pliHeadFromDomainEntry(CDomainEntry *pdentry) {_ASSERT(FALSE && "Base virtual function");return NULL;}; virtual DWORD cItemsFromDomainEntry(CDomainEntry *pdentry) {_ASSERT(FALSE && "Base virtual function");return 0;}; public: HRESULT HrInitialize(CDomainEntry *pdentry); VOID Reset() {m_iCurrentItem = 0;}; }; //---[ CDomainEntryLinkIterator ]---------------------------------------------- // // // Description: // Implementation of CDomainEntryIterator for CLinkMsgQueues // Hungarian: // delit, pdelit // //----------------------------------------------------------------------------- class CDomainEntryLinkIterator : public CDomainEntryIterator { protected: virtual VOID ReleaseItem(PVOID pvItem); virtual PVOID pvItemFromListEntry(PLIST_ENTRY pli); virtual PLIST_ENTRY pliHeadFromDomainEntry(CDomainEntry *pdentry) {return &(pdentry->m_liLinks);}; virtual DWORD cItemsFromDomainEntry(CDomainEntry *pdentry) {return pdentry->m_cLinks;}; public: ~CDomainEntryLinkIterator() {Recycle();}; CLinkMsgQueue *plmqGetNextLinkMsgQueue(CLinkMsgQueue *plmq); }; //---[ CDomainEntryQueueIterator ]--------------------------------------------- // // // Description: // Implementation of CDomainEntryIterator for CDestMsgQueues // Hungarian: // deqit, pdeqit // //----------------------------------------------------------------------------- class CDomainEntryQueueIterator : public CDomainEntryIterator { protected: virtual VOID ReleaseItem(PVOID pvItem); virtual PVOID pvItemFromListEntry(PLIST_ENTRY pli); virtual PLIST_ENTRY pliHeadFromDomainEntry(CDomainEntry *pdentry) {return &(pdentry->m_liDestQueues);}; virtual DWORD cItemsFromDomainEntry(CDomainEntry *pdentry) {return pdentry->m_cQueues;}; public: ~CDomainEntryQueueIterator() {Recycle();}; CDestMsgQueue *pdmqGetNextDestMsgQueue(CDestMsgQueue *pdmq); }; class CDomainMappingTable { private: DWORD m_dwSignature; DWORD m_dwInternalVersion; //version # used to keep track of queue deletions DWORD m_dwFlags; CAQSvrInst *m_paqinst; DWORD m_cOutstandingExternalShareLocks; //number of outstanding external sharelocks LIST_ENTRY m_liEmptyDMQHead; //head of list for empty DMQ's DWORD m_cThreadsForEmptyDMQList; DOMAIN_NAME_TABLE m_dnt; //where domain names are actually stored CShareLockInst m_slPrivateData; //Sharelock for accessing Domain Name Table CLocalLinkMsgQueue *m_plmqLocal; //Local link Queue CLinkMsgQueue *m_plmqCurrentlyUnreachable; //link for currently unreachable CLinkMsgQueue *m_plmqUnreachable; //link unreachable destinations CMailMsgAdminLink *m_pmmaqPreCategorized; //link for precat queue CMailMsgAdminLink *m_pmmaqPreRouting; //link for prerouting queue CMailMsgAdminLink *m_pmmaqPreSubmission; //link for prerouting queue DWORD m_cSpecialRetryMinutes; DWORD m_cResetRoutesRetriesPending; HRESULT HrInitLocalDomain( IN CDomainEntry *pdentry, //entry to init IN DOMAIN_STRING *pStrDomain, //Domain name IN CAQMessageType *paqmtMessageType, //Message type as returned by routing OUT CDomainMapping *pdmap); //domain mapping for domain HRESULT HrInitRemoteDomain( IN CDomainEntry *pdentry, //entry to init IN DOMAIN_STRING *pStrDomain, //Domain Name IN CInternalDomainInfo *pIntDomainInfo, //domain config IN CAQMessageType *paqmtMessageType, //Message type as returned by routing IN IMessageRouter *pIMessageRouter, //router for this message OUT CDomainMapping *pdmap, //domain mapping for domain OUT CDestMsgQueue **ppdmq, //destmsgqueue for domain OUT CLinkMsgQueue **pplmq); HRESULT HrCreateQueueForEntry( IN CDomainEntry *pdentry, IN DOMAIN_STRING *pStrDomain, IN CInternalDomainInfo *pIntDomainInfo, IN CAQMessageType *paqmtMessageType, IN IMessageRouter *pIMessageRouter, IN CDomainMapping *pdmap, OUT CDestMsgQueue **ppdmq); HRESULT HrGetNextHopLink( IN CDomainEntry *pdentry, IN LPSTR szDomain, IN DWORD cbDomain, IN CInternalDomainInfo *pIntDomainInfo, IN CAQMessageType *paqmtMessageType, IN IMessageRouter *pIMessageRouter, IN BOOL fDMTLocked, OUT CLinkMsgQueue **pplmq, OUT HRESULT *phrRoutingDiag); void LogDomainUnreachableEvent(BOOL fCurrentlyUnreachable, LPCSTR szDomain); //Checks head of EMPTY_LIST to see if there are any expired queues //or an excesive number of non-empty queues in the list BOOL fNeedToWalkEmptyQueueList(); //Used to delete expired queues BOOL fDeleteExpiredQueues(); //Domain Name Table iterator function to move a domain to the currently //unreachable link - used for re-routing static VOID MakeSingleDomainCurrentlyUnreachable(PVOID pvContext, PVOID pvData, BOOL fWildcard, BOOL *pfContinue, BOOL *pfDelete); HRESULT HrPrvGetDomainEntry(IN DWORD cbDomainNameLength, IN LPSTR szDomainName, IN BOOL fDMTLocked, OUT CDomainEntry **ppdentry); HRESULT HrInializeGlobalLink(IN LPCSTR szLinkName, IN DWORD cbLinkName, OUT CLinkMsgQueue **pplmq, DWORD dwSupportedActions = 0, DWORD dwLinkType = 0); HRESULT HrDeinitializeGlobalLink(IN OUT CLinkMsgQueue **pplmq); HRESULT HrPrvInsertDomainEntry( IN PDOMAIN_STRING pstrDomainName, IN CDomainEntry *pdentryNew, IN BOOL fTreatAsWildcard, OUT CDomainEntry **ppdentryOld); static void RetryResetRoutes(PVOID pvThis); void RequestResetRoutesRetryIfNecessary(); public: CDomainMappingTable(); ~CDomainMappingTable(); HRESULT HrInitialize( CAQSvrInst *paqinst, CAsyncAdminMsgRefQueue *paradmq, CAsyncAdminMailMsgQueue *pmmaqPrecatQ, CAsyncAdminMailMsgQueue *pmmaqPreRoutingQ, CAsyncAdminMailMsgQueue *pmmaqPreSubmission); HRESULT HrDeinitialize(); //Lookup Domain name; This will create a new entry if neccessary. HRESULT HrMapDomainName( IN LPSTR szDomainName, //Domain Name to map IN CAQMessageType *paqmtMessageType, //Message type as returned by routing IN IMessageRouter *pIMessageRouter, //router for this message OUT CDomainMapping *pdmap, //Mapping returned caller allocated OUT CDestMsgQueue **ppdmq);//ptr to Queue HRESULT HrGetDomainEntry(IN DWORD cbDomainNameLength, IN LPSTR szDomainName, OUT CDomainEntry **ppdentry) { return HrPrvGetDomainEntry(cbDomainNameLength, szDomainName, FALSE, ppdentry); } HRESULT HrIterateOverSubDomains(DOMAIN_STRING * pstrDomain, IN DOMAIN_ITR_FN pfn, IN PVOID pvContext) { HRESULT hr = S_OK; m_slPrivateData.ShareLock(); hr = m_dnt.HrIterateOverSubDomains(pstrDomain, pfn, pvContext); m_slPrivateData.ShareUnlock(); return hr; }; // Functions for rerouting domains HRESULT HrBeginRerouteDomains(); HRESULT HrCompleteRerouteDomains(); // Reroute the queues on a given link HRESULT HrRerouteLink(CLinkMsgQueue *plmqReroute); HRESULT HrGetOrCreateLink( IN LPSTR szRouteAddress, IN DWORD cbRouteAddress, IN DWORD dwScheduleID, IN LPSTR szConnectorName, IN IMessageRouter *pIMessageRouter, IN BOOL fCreateIfNotExist, IN DWORD linkInfoType, OUT CLinkMsgQueue **pplmq, OUT BOOL *pfRemoveOwnedSchedule); //The following functions are used to check the version number of the DMT. //The version number is guananteed remain constant while the DMT Sharelock //is held. While it is not *required* to get lock before getting the //version number for the first time, it is required to get the lock (and //keep it) while verify that the version number has not changed. The //expected usage of these functions is: //DWORD dwDMTVersion = pdmt->dwGetDMTVersion(); //... //pdmt->AquireDMTShareLock(); //if (pdmt->dwGetDMTVersion() == dwDMTVersion) // ... do stuff that requires consitant DMT version //pdmt->ReleaseDMTShareLock(); inline void AquireDMTShareLock(); inline void ReleaseDMTShareLock(); inline DWORD dwGetDMTVersion(); //Used by DestMsgQueue to Add themselves from the empty queue list void AddDMQToEmptyList(CDestMsgQueue *pdmq); void ProcessSpecialLinks(DWORD cSpecialRetryMinutes, BOOL fRoutingLockHeld); static void SpecialRetryCallback(PVOID pvContext); CLinkMsgQueue *plmqGetLocalLink(); CLinkMsgQueue *plmqGetCurrentlyUnreachable(); CMailMsgAdminLink *pmmaqGetPreCategorized(); CMailMsgAdminLink *pmmaqGetPreRouting(); CMailMsgAdminLink *pmmaqGetPreSubmission(); HRESULT HrPrepareForLocalDelivery( IN CMsgRef *pmsgref, IN BOOL fDelayDSN, IN OUT CDeliveryContext *pdcntxt, OUT DWORD *pcRecips, OUT DWORD **prgdwRecips); DWORD GetCurrentlyUnreachableTotalMsgCount(); }; //---[ CDomainEntry::InitDomainString ]---------------------------------------- // // // Description: // Initialized a domain string based on this domain's info // Parameters: // pDomain - ptr to DOMAIN_STRING to initialize // Returns: // - // History: // 5/26/98 - MikeSwa Created // //----------------------------------------------------------------------------- void CDomainEntry::InitDomainString(PDOMAIN_STRING pDomain) { pDomain->Buffer = m_szDomainName; pDomain->Length = (USHORT) m_cbDomainName; pDomain->MaximumLength = pDomain->Length; } //---[ CDomainMappingTable::AquireDMTShareLock ]------------------------------- // // // Description: // Aquires a share lcok on the DMT's internal lock... must be released // with a call to ReleaseDMTShareLock. // Parameters: // // Returns: // // History: // 9/8/98 - MikeSwa Created // //----------------------------------------------------------------------------- void CDomainMappingTable::AquireDMTShareLock() { m_slPrivateData.ShareLock(); DEBUG_DO_IT(InterlockedIncrement((PLONG) &m_cOutstandingExternalShareLocks)); } //---[ CDomainMappingTable::ReleaseDMTShareLock ]------------------------------- // // // Description: // Releases a DMT sharelock aquired by AquireDMTShareLock // Parameters: // - // Returns: // - // History: // 9/8/98 - MikeSwa Created // //----------------------------------------------------------------------------- void CDomainMappingTable::ReleaseDMTShareLock() { _ASSERT(m_cOutstandingExternalShareLocks); //Count should not go below 0 DEBUG_DO_IT(InterlockedDecrement((PLONG) &m_cOutstandingExternalShareLocks)); m_slPrivateData.ShareUnlock(); } //---[ CDomainMappingTable::dwGetDMTVersion ]---------------------------------- // // // Description: // Returns the internal DMT version number // Parameters: // - // Returns: // DMT Version number // History: // 9/8/98 - MikeSwa Created // //----------------------------------------------------------------------------- DWORD CDomainMappingTable::dwGetDMTVersion() { return m_dwInternalVersion; } void ReUnreachableErrorToAqueueError(HRESULT reErr, HRESULT *aqErr); #endif //_DOMAIN_H_