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
3815 lines
124 KiB
//-----------------------------------------------------------------------------
|
|
//
|
|
//
|
|
// File: domain.cpp
|
|
//
|
|
// Description: Implementation of CDomainMapping, CDomainEntry, and
|
|
// CDomainMappingTable.
|
|
//
|
|
// The DomainMappingTable is a domain name hash table that contains the
|
|
// mappings from final destination to queues.
|
|
//
|
|
// Author: mikeswa
|
|
//
|
|
// Copyright (C) 1997 Microsoft Corporation
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "aqprecmp.h"
|
|
#include "aqroute.h"
|
|
#include "localq.h"
|
|
#include "asyncq.h"
|
|
#include "mailadmq.h"
|
|
#include "tran_evntlog.h"
|
|
|
|
const DWORD LOCAL_DOMAIN_NAME_SIZE = 512;
|
|
|
|
//Max mislabled queues in empty list, before we will clean the list
|
|
const DWORD MAX_MISPLACED_QUEUES_IN_EMPTY_LIST = 100;
|
|
|
|
|
|
//Callback for retry
|
|
void CDomainMappingTable::SpecialRetryCallback(PVOID pvContext)
|
|
{
|
|
CDomainMappingTable *pdnt = (CDomainMappingTable *) pvContext;
|
|
_ASSERT(pdnt);
|
|
_ASSERT(DOMAIN_MAPPING_TABLE_SIG == pdnt->m_dwSignature);
|
|
|
|
dwInterlockedUnsetBits(&(pdnt->m_dwFlags), DMT_FLAGS_SPECIAL_DELIVERY_CALLBACK);
|
|
pdnt->ProcessSpecialLinks(0, FALSE);
|
|
}
|
|
|
|
//---[ ReUnreachableErrorToAqueueError ]---------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Translates a HRESULT returned from GetNextHop to one that is meaningful
|
|
// to aqueue DSN generation.
|
|
// Parameters:
|
|
// IN HRESULT reErr -- Error from routing.
|
|
// IN OUT HRESULT aqErr -- Corresponding aqueue error code.
|
|
// Returns:
|
|
// Nothing.
|
|
// History:
|
|
// GPulla created.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void ReUnreachableErrorToAqueueError(HRESULT reErr, HRESULT *aqErr)
|
|
{
|
|
if(E_ACCESSDENIED == reErr)
|
|
*aqErr = AQUEUE_E_ACCESS_DENIED;
|
|
|
|
else if(HRESULT_FROM_WIN32(ERROR_MESSAGE_EXCEEDS_MAX_SIZE) == reErr)
|
|
*aqErr = AQUEUE_E_MESSAGE_TOO_LARGE;
|
|
|
|
else
|
|
*aqErr = AQUEUE_E_NO_ROUTE;
|
|
|
|
}
|
|
|
|
//---[ DeinitDomainEntryIteratorFn ]--------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Deletes and releases all internal domain info objects in table
|
|
// Parameters:
|
|
// IN pvContext - pointer to context (ignored)
|
|
// IN pvData - data entry to look at
|
|
// IN fWildcardData - TRUE if data is a wildcard entry (ignored)
|
|
// OUT pfContinue - TRUE if iterator should continue to the next entry
|
|
// OUT pfRemoveEntry - TRUE if entry should be deleted
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 6/17/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
VOID DeinitDomainEntryIteratorFn(PVOID pvContext, PVOID pvData, BOOL fWildcard,
|
|
BOOL *pfContinue, BOOL *pfDelete)
|
|
{
|
|
CDomainEntry *pdentry = (CDomainEntry *) pvData;
|
|
*pfDelete = FALSE;
|
|
*pfContinue = TRUE;
|
|
pdentry->HrDeinitialize();
|
|
}
|
|
|
|
//---[ ReleaseDomainEntryIteratorFn ]------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Deletes and releases all internal domain info objects in table
|
|
// Parameters:
|
|
// IN pvContext - pointer to context (ignored)
|
|
// IN pvData - data entry to look at
|
|
// IN fWildcardData - TRUE if data is a wildcard entry (ignored)
|
|
// OUT pfContinue - TRUE if iterator should continue to the next entry
|
|
// OUT pfRemoveEntry - TRUE if entry should be deleted
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 6/17/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
VOID ReleaseDomainEntryIteratorFn(PVOID pvContext, PVOID pvData, BOOL fWildcard,
|
|
BOOL *pfContinue, BOOL *pfDelete)
|
|
{
|
|
ULONG cRefs;
|
|
CDomainEntry *pdentry = (CDomainEntry *) pvData;
|
|
*pfDelete = TRUE;
|
|
*pfContinue = TRUE;
|
|
cRefs = pdentry->Release();
|
|
_ASSERT(!cRefs && "leaking domain entries");
|
|
}
|
|
|
|
//***[ CDomainMapping Methods ]************************************************
|
|
|
|
//---[ CDomainMapping::Clone ]-------------------------------------------------
|
|
//
|
|
//
|
|
// Description: Fills the current mapping with data from another DomainMapping
|
|
//
|
|
// Parameters:
|
|
// IN pdmap CDomainMapping to clone
|
|
//
|
|
// Returns:
|
|
// -
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void CDomainMapping::Clone(IN CDomainMapping *pdmap)
|
|
{
|
|
TraceFunctEnterEx((LPARAM) this, "CDomainMapping::Clone");
|
|
Assert(pdmap);
|
|
m_pdentryDomainID = pdmap->m_pdentryDomainID;
|
|
m_pdentryQueueID = pdmap->m_pdentryQueueID;
|
|
TraceFunctLeave();
|
|
}
|
|
|
|
//---[ CDomainMapping::HrGetDestMsgQueue ]-------------------------------------
|
|
//
|
|
//
|
|
// Description: Returns a pointer to the queue that this mapping points to
|
|
//
|
|
// Parameters:
|
|
// IN paqmt Message Type to get queue for
|
|
// OUT ppdmq pointer returned
|
|
//
|
|
// Returns:
|
|
// S_OK on success
|
|
// AQUEUE_E_INVALID_DOMAIN
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CDomainMapping::HrGetDestMsgQueue(IN CAQMessageType *paqmt,
|
|
OUT CDestMsgQueue **ppdmq)
|
|
{
|
|
TraceFunctEnterEx((LPARAM) this, "CDomainMapping::HrGetDestMsgQueue");
|
|
HRESULT hr = S_OK;
|
|
Assert(ppdmq);
|
|
|
|
if (m_pdentryQueueID == NULL)
|
|
{
|
|
hr = AQUEUE_E_INVALID_DOMAIN;
|
|
goto Exit;
|
|
}
|
|
|
|
hr = m_pdentryQueueID->HrGetDestMsgQueue(paqmt, ppdmq);
|
|
|
|
Exit:
|
|
TraceFunctLeave();
|
|
return hr;
|
|
}
|
|
|
|
//***[ CDomainEntry Methods ]**************************************************
|
|
|
|
//---[ CDomainEntry::CDomainEntry() ]------------------------------------------
|
|
//
|
|
//
|
|
// Description: CDomainEntry constructor
|
|
//
|
|
// Parameters:
|
|
// paqinst - ptr to the virtual server object
|
|
//
|
|
// Returns:
|
|
// -
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CDomainEntry::CDomainEntry(CAQSvrInst *paqinst) :
|
|
m_slPrivateData("CDomainEntry")
|
|
{
|
|
_ASSERT(paqinst);
|
|
TraceFunctEnterEx((LPARAM) this, "CDomainEntry::CDomainEntry");
|
|
//Create a mapping that is not compressed
|
|
m_dmap.m_pdentryDomainID = this;
|
|
m_dmap.m_pdentryQueueID = this;
|
|
m_dwSignature = DOMAIN_ENTRY_SIG;
|
|
|
|
//init pointers
|
|
m_szDomainName = NULL;
|
|
m_cbDomainName = 0;
|
|
InitializeListHead(&m_liDestQueues);
|
|
InitializeListHead(&m_liLinks);
|
|
|
|
m_cLinks = 0;
|
|
m_cQueues = 0;
|
|
|
|
m_paqinst = paqinst;
|
|
m_paqinst->AddRef();
|
|
|
|
TraceFunctLeave();
|
|
}
|
|
|
|
//---[ CDomainEntry::~CDomainEntry() ]-----------------------------------------
|
|
//
|
|
//
|
|
// Description: CDomainEntry destructor
|
|
//
|
|
// Parameters:
|
|
// -
|
|
//
|
|
// Returns:
|
|
// -
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CDomainEntry::~CDomainEntry()
|
|
{
|
|
TraceFunctEnterEx((LPARAM) this, "CDomainEntry::~CDomainEntry");
|
|
PLIST_ENTRY pli = NULL; //used to iterate over lists
|
|
CDestMsgQueue *pdmq = NULL;
|
|
CLinkMsgQueue *plmq = NULL;
|
|
|
|
//Remove all DestMsgQueues from list
|
|
while (!IsListEmpty(&m_liDestQueues))
|
|
{
|
|
pli = m_liDestQueues.Flink;
|
|
_ASSERT((pli != &m_liDestQueues) && "List Macros are broken");
|
|
pdmq = CDestMsgQueue::pdmqGetDMQFromDomainListEntry(pli);
|
|
pdmq->RemoveQueueFromDomainList();
|
|
pdmq->Release();
|
|
m_cQueues--;
|
|
}
|
|
|
|
//Remove all links from list
|
|
while (!IsListEmpty(&m_liLinks))
|
|
{
|
|
pli = m_liLinks.Flink;
|
|
plmq = CLinkMsgQueue::plmqGetLinkMsgQueue(pli);
|
|
plmq->fRemoveLinkFromList();
|
|
plmq->Release();
|
|
m_cLinks--;
|
|
_ASSERT((pli != &m_liLinks) && "List Macros are broken");
|
|
}
|
|
|
|
FreePv(m_szDomainName);
|
|
|
|
if (m_paqinst)
|
|
m_paqinst->Release();
|
|
|
|
TraceFunctLeave();
|
|
}
|
|
|
|
//---[ CDomainEntry::HrInitialize ]--------------------------------------------
|
|
//
|
|
//
|
|
// Description: Initilizer for CDomainEntry. This should be called BEFORE the
|
|
// entry is inserted into the DMT where other threads can access it.
|
|
//
|
|
// Parameters:
|
|
// szDomainName string of domain name for entry, will *NOT* be copied, this
|
|
// object will take control of this. This will save a unneeded
|
|
// buffer copy and allocation per domain entry
|
|
// pdentryQueueID ptr to the primary entry for this domain (usually this)
|
|
// pdmq ptr to DestMsgQueue
|
|
// plmq ptr to LinkMsgQueue to allocate
|
|
// Returns:
|
|
// S_OK on success
|
|
// E_OUTOFMEMORY if any allocation fails
|
|
//
|
|
// It is expected that this is only called by the DMT while creating an entry
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CDomainEntry::HrInitialize(DWORD cbDomainName, LPSTR szDomainName,
|
|
CDomainEntry *pdentryQueueID, CDestMsgQueue *pdmq,
|
|
CLinkMsgQueue *plmq)
|
|
{
|
|
TraceFunctEnterEx((LPARAM) this, "CDomainEntry::HrInitialize");
|
|
Assert(szDomainName);
|
|
Assert((pdentryQueueID == this) || (pdmq == NULL));
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
m_cbDomainName = cbDomainName;
|
|
m_szDomainName = szDomainName;
|
|
|
|
//write domain mapping
|
|
m_dmap.m_pdentryDomainID = this;
|
|
m_dmap.m_pdentryQueueID = pdentryQueueID;
|
|
|
|
//add the queue to our list of queues
|
|
if (pdmq)
|
|
{
|
|
m_slPrivateData.ExclusiveLock();
|
|
m_cQueues++;
|
|
pdmq->AddRef();
|
|
pdmq->InsertQueueInDomainList(&m_liDestQueues);
|
|
m_slPrivateData.ExclusiveUnlock();
|
|
}
|
|
|
|
if (plmq)
|
|
{
|
|
m_slPrivateData.ExclusiveLock();
|
|
m_cLinks++;
|
|
plmq->AddRef();
|
|
plmq->InsertLinkInList(&m_liLinks);
|
|
m_slPrivateData.ExclusiveUnlock();
|
|
}
|
|
|
|
TraceFunctLeave();
|
|
return hr;
|
|
}
|
|
|
|
//---[ CDomainEntry::HrDeinitialize ]------------------------------------------
|
|
//
|
|
//
|
|
// Description: Deinitializer for CDomainEntry
|
|
//
|
|
// Parameters:
|
|
// -
|
|
// Returns:
|
|
// S_OK on success
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CDomainEntry::HrDeinitialize()
|
|
{
|
|
TraceFunctEnterEx((LPARAM) this, "CDomainEntry::HrDeinitialize");
|
|
HRESULT hr = S_OK;
|
|
PLIST_ENTRY pli = NULL; //used to iterate over lists
|
|
CDestMsgQueue *pdmq = NULL;
|
|
CLinkMsgQueue *plmq = NULL;
|
|
|
|
m_slPrivateData.ExclusiveLock();
|
|
while (!IsListEmpty(&m_liDestQueues))
|
|
{
|
|
pli = m_liDestQueues.Flink;
|
|
_ASSERT((pli != &m_liDestQueues) && "List Macros are broken");
|
|
pdmq = CDestMsgQueue::pdmqGetDMQFromDomainListEntry(pli);
|
|
pdmq->HrDeinitialize();
|
|
pdmq->RemoveQueueFromDomainList();
|
|
pdmq->Release();
|
|
m_cQueues--;
|
|
pdmq = NULL;
|
|
}
|
|
|
|
//Remove all links from list
|
|
while (!IsListEmpty(&m_liLinks))
|
|
{
|
|
pli = m_liLinks.Flink;
|
|
plmq = CLinkMsgQueue::plmqGetLinkMsgQueue(pli);
|
|
plmq->HrDeinitialize();
|
|
plmq->fRemoveLinkFromList();
|
|
plmq->Release();
|
|
m_cLinks--;
|
|
_ASSERT((pli != &m_liLinks) && "List Macros are broken");
|
|
}
|
|
|
|
if (m_paqinst)
|
|
{
|
|
m_paqinst->Release();
|
|
m_paqinst = NULL;
|
|
}
|
|
|
|
m_slPrivateData.ExclusiveUnlock();
|
|
|
|
TraceFunctLeave();
|
|
return hr;
|
|
}
|
|
|
|
//---[ CDomainEntry::HrGetDomainMapping ]--------------------------------------
|
|
//
|
|
//
|
|
// Description: Returns Domain Mapping for this object
|
|
//
|
|
// Parameters:
|
|
// OUT pdmap CDomainMapping for return information
|
|
//
|
|
// Returns:
|
|
// S_OK on success
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CDomainEntry::HrGetDomainMapping(OUT CDomainMapping *pdmap)
|
|
{
|
|
TraceFunctEnterEx((LPARAM) this, "CDomainEntry::HrGetDomainMapping");
|
|
HRESULT hr = S_OK;
|
|
_ASSERT(pdmap);
|
|
pdmap->Clone(&m_dmap);
|
|
TraceFunctLeave();
|
|
return S_OK;
|
|
}
|
|
|
|
//---[ CDomainEntry::HrGetDomainName ]----------------------------------------------
|
|
//
|
|
//
|
|
// Description: Copies Domain Name. Caller is responsible for freeing string
|
|
//
|
|
// Parameters:
|
|
// OUT pszDomainName string of domain name for entry, will be copied
|
|
//
|
|
// Returns:
|
|
// S_OK on success
|
|
// E_OUTOFMEMORY if any allocation fails
|
|
//
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CDomainEntry::HrGetDomainName(OUT LPSTR *pszDomainName)
|
|
{
|
|
TraceFunctEnterEx((LPARAM) this, "CDomainEntry::HrGetDomainName");
|
|
HRESULT hr = S_OK;
|
|
Assert(pszDomainName);
|
|
|
|
if (m_szDomainName == NULL)
|
|
{
|
|
*pszDomainName = NULL;
|
|
goto Exit;
|
|
}
|
|
|
|
//Copy domain name
|
|
*pszDomainName = (LPSTR) pvMalloc(m_cbDomainName + sizeof(CHAR));
|
|
|
|
if (*pszDomainName == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
strcpy(*pszDomainName, m_szDomainName);
|
|
|
|
Exit:
|
|
TraceFunctLeave();
|
|
return hr;
|
|
}
|
|
|
|
//---[ CDomainEntry::HrGetDestMsgQueue ]---------------------------------------
|
|
//
|
|
//
|
|
// Description: Returns a pointer to the queue that this entry points to
|
|
//
|
|
// Parameters:
|
|
// IN paqmt Message Type to get domain for
|
|
// OUT ppdmq pointer returned
|
|
//
|
|
// Returns:
|
|
// S_OK on success
|
|
// E_FAIL no queue matching message type is found
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CDomainEntry::HrGetDestMsgQueue(IN CAQMessageType *paqmt,
|
|
OUT CDestMsgQueue **ppdmq)
|
|
{
|
|
TraceFunctEnterEx((LPARAM) this, "CDomainEntry::HrGetDestMsgQueue");
|
|
HRESULT hr = S_OK;
|
|
PLIST_ENTRY pli = NULL;
|
|
CDestMsgQueue *pdmq = NULL;
|
|
_ASSERT(ppdmq);
|
|
_ASSERT(m_dmap.m_pdentryDomainID == this);
|
|
DEBUG_DO_IT(DWORD cQueues = 0);
|
|
|
|
if (m_dmap.m_pdentryQueueID == m_dmap.m_pdentryDomainID)
|
|
{
|
|
//this must be the primary entry... scan our own list of queues
|
|
m_slPrivateData.ShareLock();
|
|
pli = m_liDestQueues.Flink;
|
|
while (pli != &m_liDestQueues)
|
|
{
|
|
_ASSERT(m_cQueues >= cQueues);
|
|
DEBUG_DO_IT(cQueues++);
|
|
pdmq = CDestMsgQueue::pdmqIsSameMessageType(paqmt, pli);
|
|
if (pdmq)
|
|
break;
|
|
|
|
pli = pli->Flink;
|
|
}
|
|
m_slPrivateData.ShareUnlock();
|
|
|
|
if (!pdmq)
|
|
hr = E_FAIL; //no such queue
|
|
else
|
|
{
|
|
pdmq->AddRef();
|
|
*ppdmq = pdmq;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//we are not primary
|
|
_ASSERT(0 && "Non-primary domain entry... currently only primary entries are supported");
|
|
_ASSERT(IsListEmpty(&m_liDestQueues)); //make sure it matches the profile
|
|
hr = m_dmap.m_pdentryQueueID->HrGetDestMsgQueue(paqmt, ppdmq);
|
|
}
|
|
|
|
TraceFunctLeave();
|
|
return hr;
|
|
}
|
|
|
|
//---[ CDomainEntry::HrAddUniqueDestMsgQueue ]---------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Adds a queue to this entry's list of queues if a queue with the same
|
|
// message type does not already exist.
|
|
//
|
|
// Will appropriately AddRef domain.
|
|
// Parameters:
|
|
// IN pdmqNew - CDestMsgQueue to add
|
|
// OUT ppdmqCurrent - Set to curent CDestMsgQueue on failure
|
|
// Returns:
|
|
// S_OK on success
|
|
// E_FAIL if a CDestMsgQueue with same Message type alread exists.
|
|
// History:
|
|
// 5/28/98 - MikeSwa Created
|
|
// 9/8/98 - MikeSwa Modified to use AddRef/Relase for queues
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CDomainEntry::HrAddUniqueDestMsgQueue(IN CDestMsgQueue *pdmqNew,
|
|
OUT CDestMsgQueue **ppdmqCurrent)
|
|
{
|
|
_ASSERT(pdmqNew);
|
|
_ASSERT(ppdmqCurrent);
|
|
HRESULT hr = S_OK;
|
|
PLIST_ENTRY pli = NULL;
|
|
CDestMsgQueue *pdmq = NULL;
|
|
CAQMessageType *paqmt = pdmqNew->paqmtGetMessageType();
|
|
DEBUG_DO_IT(DWORD cQueues = 0);
|
|
|
|
*ppdmqCurrent = NULL;
|
|
|
|
m_slPrivateData.ExclusiveLock();
|
|
pli = m_liDestQueues.Flink;
|
|
|
|
//First look through list and make sure that there isn't already a
|
|
//queue with this message type
|
|
while (pli != &m_liDestQueues)
|
|
{
|
|
_ASSERT(m_cQueues >= cQueues);
|
|
pdmq = CDestMsgQueue::pdmqIsSameMessageType(paqmt, pli);
|
|
if (pdmq)
|
|
{
|
|
hr = E_FAIL;
|
|
pdmq->AddRef();
|
|
*ppdmqCurrent = pdmq;
|
|
goto Exit;
|
|
}
|
|
DEBUG_DO_IT(cQueues++);
|
|
pli = pli->Flink;
|
|
}
|
|
|
|
pdmqNew->AddRef();
|
|
pdmqNew->InsertQueueInDomainList(&m_liDestQueues);
|
|
m_cQueues++;
|
|
|
|
Exit:
|
|
m_slPrivateData.ExclusiveUnlock();
|
|
return hr;
|
|
}
|
|
|
|
//---[ CDomainEntry::HrGetLinkMsgQueue ]---------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Gets a link for the given schedule id
|
|
// Parameters:
|
|
// IN paqsched - ScheduleID to search for
|
|
// OUT pplmq - returned queue
|
|
// Returns:
|
|
// S_OK on success
|
|
// E_FAIL if no link matching the schudule ID can be found
|
|
// History:
|
|
// 6/11/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CDomainEntry::HrGetLinkMsgQueue(IN CAQScheduleID *paqsched,
|
|
OUT CLinkMsgQueue **pplmq)
|
|
{
|
|
TraceFunctEnterEx((LPARAM) this, "CDomainEntry::HrGetLinkMsgQueue");
|
|
HRESULT hr = S_OK;
|
|
PLIST_ENTRY pli = NULL;
|
|
CLinkMsgQueue *plmq = NULL;
|
|
_ASSERT(pplmq);
|
|
_ASSERT(m_dmap.m_pdentryDomainID == this);
|
|
DEBUG_DO_IT(DWORD cLinks = 0);
|
|
|
|
m_slPrivateData.ShareLock();
|
|
pli = m_liLinks.Flink;
|
|
while (pli != &m_liLinks)
|
|
{
|
|
_ASSERT(m_cLinks >= cLinks);
|
|
DEBUG_DO_IT(cLinks++);
|
|
plmq = CLinkMsgQueue::plmqIsSameScheduleID(paqsched, pli);
|
|
if (plmq)
|
|
{
|
|
plmq->AddRef();
|
|
break;
|
|
}
|
|
|
|
pli = pli->Flink;
|
|
}
|
|
m_slPrivateData.ShareUnlock();
|
|
|
|
if (!plmq)
|
|
hr = E_FAIL; //no such queue
|
|
else
|
|
*pplmq = plmq;
|
|
|
|
TraceFunctLeave();
|
|
return hr;
|
|
}
|
|
|
|
//---[ CDomainEntry::HrAddUniqueLinkMsgQueue ]---------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Inserts a link with a unique schedule ID
|
|
// Parameters:
|
|
// IN plmqNew New link to insert
|
|
// OUT plmqCurrent Current link with schedule ID on insert failure
|
|
// Returns:
|
|
// S_OK if insert succeeds
|
|
// E_FAIL if insert fails
|
|
// History:
|
|
// 6/11/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CDomainEntry::HrAddUniqueLinkMsgQueue(IN CLinkMsgQueue *plmqNew,
|
|
OUT CLinkMsgQueue **pplmqCurrent)
|
|
{
|
|
_ASSERT(plmqNew);
|
|
_ASSERT(pplmqCurrent);
|
|
HRESULT hr = S_OK;
|
|
PLIST_ENTRY pli = NULL;
|
|
CLinkMsgQueue *plmq = NULL;
|
|
CAQScheduleID *paqsched = plmqNew->paqschedGetScheduleID();
|
|
DEBUG_DO_IT(DWORD cLinks = 0);
|
|
|
|
*pplmqCurrent = NULL;
|
|
|
|
m_slPrivateData.ExclusiveLock();
|
|
pli = m_liLinks.Flink;
|
|
|
|
//First look through list and make sure that there isn't already a
|
|
//queue with this schedule ID
|
|
while (pli != &m_liLinks)
|
|
{
|
|
_ASSERT(m_cLinks >= cLinks);
|
|
plmq = CLinkMsgQueue::plmqIsSameScheduleID(paqsched, pli);
|
|
if (plmq)
|
|
{
|
|
hr = E_FAIL;
|
|
*pplmqCurrent = plmq;
|
|
plmq->AddRef();
|
|
goto Exit;
|
|
}
|
|
DEBUG_DO_IT(cLinks++);
|
|
pli = pli->Flink;
|
|
}
|
|
|
|
plmqNew->InsertLinkInList(&m_liLinks);
|
|
plmqNew->AddRef();
|
|
m_cLinks++;
|
|
|
|
Exit:
|
|
m_slPrivateData.ExclusiveUnlock();
|
|
return hr;
|
|
}
|
|
|
|
//---[ CDomainEntry::RemoveDestMsgQueue ]--------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Removes empty DMQ from entry.
|
|
// Parameters:
|
|
// IN pdmq DMQ to remove from domain entry
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 9/14/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void CDomainEntry::RemoveDestMsgQueue(IN CDestMsgQueue *pdmq)
|
|
{
|
|
_ASSERT(pdmq && "INVALID Param for internal function");
|
|
m_slPrivateData.ExclusiveLock();
|
|
pdmq->RemoveQueueFromDomainList();
|
|
pdmq->HrDeinitialize();
|
|
pdmq->Release();
|
|
m_cQueues--;
|
|
m_slPrivateData.ExclusiveUnlock();
|
|
}
|
|
|
|
//---[ CDomainEntry::RemoveLinkMsgQueue ]--------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Removes an empty LinkMsgQueue from the domain entry
|
|
// Parameters:
|
|
// IN plmq Link to remove
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 9/14/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void CDomainEntry::RemoveLinkMsgQueue(IN CLinkMsgQueue *plmq)
|
|
{
|
|
_ASSERT(plmq && "INVALID Param for internal function");
|
|
m_slPrivateData.ExclusiveLock();
|
|
BOOL fRemove = plmq->fRemoveLinkFromList();
|
|
if (fRemove)
|
|
m_cLinks--;
|
|
m_slPrivateData.ExclusiveUnlock();
|
|
|
|
if (fRemove) {
|
|
|
|
//do *NOT* call HrDeinitialize here since it will deadlock
|
|
|
|
plmq->RemovedFromDMT();
|
|
plmq->Release();
|
|
}
|
|
}
|
|
|
|
|
|
//***[ CDomainMappingTable Methods ]*******************************************
|
|
|
|
//---[ CDomainMappingTable::CDomainMappingTable ]------------------------------
|
|
//
|
|
//
|
|
// Description: CDomainMappingTable constructor
|
|
//
|
|
// Parameters: -
|
|
//
|
|
// Returns: -
|
|
//
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CDomainMappingTable::CDomainMappingTable() :
|
|
m_slPrivateData("CDomainMappingTable")
|
|
{
|
|
TraceFunctEnterEx((LPARAM) this, "CDomainMappingTable::CDomainMappingTable");
|
|
m_paqinst = NULL;
|
|
m_dwSignature = DOMAIN_MAPPING_TABLE_SIG;
|
|
m_dwInternalVersion = 0;
|
|
m_cOutstandingExternalShareLocks = 0;
|
|
m_cThreadsForEmptyDMQList = 0;
|
|
|
|
m_plmqLocal = NULL;
|
|
m_plmqCurrentlyUnreachable = NULL;
|
|
m_plmqUnreachable = NULL;
|
|
m_pmmaqPreCategorized = NULL;
|
|
m_pmmaqPreRouting = NULL;
|
|
m_cSpecialRetryMinutes = 0;
|
|
m_cResetRoutesRetriesPending = 0;
|
|
|
|
m_dwFlags = 0;
|
|
InitializeListHead(&m_liEmptyDMQHead);
|
|
TraceFunctLeave();
|
|
}
|
|
|
|
//---[ CDomainMappingTable::~CDomainMappingTable ]------------------------------------------------------------
|
|
//
|
|
//
|
|
// Description: CDomainMappingTable destructor
|
|
//
|
|
// Parameters: -
|
|
//
|
|
// Returns: -
|
|
//
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CDomainMappingTable::~CDomainMappingTable()
|
|
{
|
|
TraceFunctEnterEx((LPARAM) this, "CDomainMappingTable::~CDomainMappingTable");
|
|
|
|
//Remove everything from the table
|
|
m_dnt.HrIterateOverSubDomains(NULL, ReleaseDomainEntryIteratorFn, NULL);
|
|
|
|
if (m_paqinst)
|
|
{
|
|
m_paqinst->Release();
|
|
m_paqinst = NULL;
|
|
}
|
|
|
|
if (m_plmqLocal)
|
|
m_plmqLocal->Release();
|
|
|
|
if (m_plmqCurrentlyUnreachable)
|
|
m_plmqCurrentlyUnreachable->Release();
|
|
|
|
if (m_plmqUnreachable)
|
|
m_plmqUnreachable->Release();
|
|
|
|
if (m_pmmaqPreCategorized)
|
|
m_pmmaqPreCategorized->Release();
|
|
|
|
if (m_pmmaqPreRouting)
|
|
m_pmmaqPreRouting->Release();
|
|
|
|
if (m_pmmaqPreSubmission)
|
|
m_pmmaqPreSubmission->Release();
|
|
|
|
_ASSERT(!m_cOutstandingExternalShareLocks); //there should be no outstanding sharelocks
|
|
TraceFunctLeave();
|
|
}
|
|
|
|
//---[ CDomainMappingTable::HrInitialize ]-------------------------------------
|
|
//
|
|
//
|
|
// Description: Performs initialization that may return an error code
|
|
//
|
|
// Parameters:
|
|
// IN paqinst AQ Svr Inst
|
|
// IN paradmq Local Async Queue (passed to local link as part
|
|
// Returns: S_OK on success
|
|
//
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CDomainMappingTable::HrInitialize(CAQSvrInst *paqinst,
|
|
CAsyncAdminMsgRefQueue *paradmq,
|
|
CAsyncAdminMailMsgQueue *pammqPreCatQ,
|
|
CAsyncAdminMailMsgQueue *pammqPreRoutingQ,
|
|
CAsyncAdminMailMsgQueue *pammqPreSubmissionQ)
|
|
{
|
|
TraceFunctEnterEx((LPARAM) this, "CDomainMappingTable::HrInitialize");
|
|
HRESULT hr = S_OK;
|
|
HRESULT hrCurrent = S_OK;
|
|
_ASSERT(paqinst);
|
|
m_paqinst = paqinst;
|
|
m_paqinst->AddRef();
|
|
|
|
//------Link for local Queue-----------------------------------------------
|
|
m_plmqLocal = new CLocalLinkMsgQueue(paradmq, g_sGuidLocalLink, m_paqinst);
|
|
if (!m_plmqLocal)
|
|
hr = E_OUTOFMEMORY;
|
|
|
|
hrCurrent = HrInializeGlobalLink(LOCAL_LINK_NAME,
|
|
sizeof(LOCAL_LINK_NAME) - sizeof(CHAR),
|
|
(CLinkMsgQueue **) &m_plmqLocal,
|
|
LA_KICK | LA_FREEZE | LA_THAW,
|
|
LI_TYPE_LOCAL_DELIVERY);
|
|
if (FAILED(hrCurrent) && SUCCEEDED(hr))
|
|
hr = hrCurrent;
|
|
|
|
//------Link for currently unreachable Queue-------------------------------
|
|
hrCurrent = HrInializeGlobalLink(CURRENTLY_UNREACHABLE_LINK_NAME,
|
|
sizeof(CURRENTLY_UNREACHABLE_LINK_NAME) - sizeof(CHAR),
|
|
&m_plmqCurrentlyUnreachable,
|
|
0,
|
|
LI_TYPE_CURRENTLY_UNREACHABLE);
|
|
if (FAILED(hrCurrent) && SUCCEEDED(hr))
|
|
hr = hrCurrent;
|
|
|
|
hrCurrent = HrInializeGlobalLink(UNREACHABLE_LINK_NAME,
|
|
sizeof(UNREACHABLE_LINK_NAME) - sizeof(CHAR),
|
|
&m_plmqUnreachable,
|
|
0,
|
|
LI_TYPE_INTERNAL);
|
|
if (FAILED(hrCurrent) && SUCCEEDED(hr))
|
|
hr = hrCurrent;
|
|
|
|
//-------Link for precat Queue---------------------------------------------
|
|
m_pmmaqPreCategorized = new CMailMsgAdminLink(g_sGuidPrecatLink, PRECAT_QUEUE_NAME,
|
|
pammqPreCatQ, LI_TYPE_PENDING_CAT,
|
|
m_paqinst);
|
|
|
|
if (!m_pmmaqPreCategorized)
|
|
hr = E_OUTOFMEMORY;
|
|
|
|
if (FAILED(hrCurrent) && SUCCEEDED(hr))
|
|
hr = hrCurrent;
|
|
|
|
//-------Link for prerouting Queue-----------------------------------------
|
|
m_pmmaqPreRouting = new CMailMsgAdminLink(g_sGuidPreRoutingLink, PREROUTING_QUEUE_NAME,
|
|
pammqPreRoutingQ, LI_TYPE_PENDING_ROUTING,
|
|
m_paqinst);
|
|
|
|
if (!m_pmmaqPreRouting)
|
|
hr = E_OUTOFMEMORY;
|
|
|
|
if (FAILED(hrCurrent) && SUCCEEDED(hr))
|
|
hr = hrCurrent;
|
|
|
|
//-------Link for presubmissionQueue--------------------------------------
|
|
m_pmmaqPreSubmission = new CMailMsgAdminLink(g_sGuidPreSubmissionLink,
|
|
PRESUBMISSION_QUEUE_NAME,
|
|
pammqPreSubmissionQ, LI_TYPE_PENDING_SUBMIT,
|
|
m_paqinst);
|
|
|
|
if (!m_pmmaqPreSubmission)
|
|
hr = E_OUTOFMEMORY;
|
|
|
|
if (FAILED(hrCurrent) && SUCCEEDED(hr))
|
|
hr = hrCurrent;
|
|
//-------------------------------------------------------------------------
|
|
|
|
hrCurrent = m_dnt.HrInit();
|
|
if (FAILED(hrCurrent) && SUCCEEDED(hr))
|
|
hr = hrCurrent;
|
|
|
|
TraceFunctLeave();
|
|
return hr;
|
|
}
|
|
|
|
//---[ CDomainMappingTable::HrDeinitialize ]-----------------------------------
|
|
//
|
|
//
|
|
// Description: Performs cleanup that may return an error code
|
|
//
|
|
// Parameters: -
|
|
//
|
|
// Returns: S_OK on success
|
|
//
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CDomainMappingTable::HrDeinitialize()
|
|
{
|
|
TraceFunctEnterEx((LPARAM) this, "CDomainMappingTable::HrDeinitialize");
|
|
HRESULT hr = S_OK;
|
|
HRESULT hrCurrent = S_OK;
|
|
|
|
hr = m_dnt.HrIterateOverSubDomains(NULL, DeinitDomainEntryIteratorFn, NULL);
|
|
|
|
//Deinitialize global special links
|
|
hrCurrent = HrDeinitializeGlobalLink((CLinkMsgQueue **) &m_plmqLocal);
|
|
if (FAILED(hrCurrent) && SUCCEEDED(hr))
|
|
hr = hrCurrent;
|
|
|
|
hrCurrent = HrDeinitializeGlobalLink(&m_plmqCurrentlyUnreachable);
|
|
if (FAILED(hrCurrent) && SUCCEEDED(hr))
|
|
hr = hrCurrent;
|
|
|
|
hrCurrent = HrDeinitializeGlobalLink(&m_plmqUnreachable);
|
|
if (FAILED(hrCurrent) && SUCCEEDED(hr))
|
|
hr = hrCurrent;
|
|
|
|
//NOTE: This *must* come after Deinitialize of entries
|
|
if (m_paqinst)
|
|
{
|
|
m_paqinst->Release();
|
|
m_paqinst = NULL;
|
|
}
|
|
|
|
TraceFunctLeave();
|
|
return hr;
|
|
}
|
|
|
|
|
|
//---[ <CDomainMappingTable::HrInializeGlobalLink ]-----------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Initializes a single global link for the DMT. Configures link to not
|
|
// send notifications to the connection manager, and to
|
|
// Parameters:
|
|
// IN szLinkName The link name to use for the link
|
|
// IN cbLinkName The string length of the link name
|
|
// OUT pplmq Link to allocate/initialize
|
|
// IN dwSupportedActions Bitmask specifying what actions are supported on this link
|
|
// IN dwLinkType Link type to be returned to admin (LI_TYPE)
|
|
// Returns:
|
|
//
|
|
// History:
|
|
// 1/27/99 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CDomainMappingTable::HrInializeGlobalLink(IN LPCSTR szLinkName,
|
|
IN DWORD cbLinkName,
|
|
OUT CLinkMsgQueue **pplmq,
|
|
DWORD dwSupportedActions,
|
|
DWORD dwLinkType)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
_ASSERT(pplmq);
|
|
|
|
if (!*pplmq)
|
|
*pplmq = new CLinkMsgQueue();
|
|
|
|
if (*pplmq)
|
|
{
|
|
//Initialize local queue
|
|
hr = (*pplmq)->HrInitialize(m_paqinst, NULL, cbLinkName,
|
|
(LPSTR) szLinkName,
|
|
eLinkFlagsAQSpecialLinkInfo, NULL);
|
|
|
|
//Set flags so no connections will be made for this link
|
|
(*pplmq)->dwModifyLinkState( LINK_STATE_PRIV_NO_NOTIFY |
|
|
LINK_STATE_PRIV_NO_CONNECTION,
|
|
LINK_STATE_NO_ACTION );
|
|
|
|
(*pplmq)->SetSupportedActions(dwSupportedActions);
|
|
(*pplmq)->SetLinkType(dwLinkType);
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
|
|
return hr;
|
|
}
|
|
|
|
//---[ CDomainMappingTable::HrDeinitializeGlobalLink ]-------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Deinitializes a single global link for the DMT
|
|
// Parameters:
|
|
// IN OUT pplmq Link to deinitialze / set to NULL
|
|
// Returns:
|
|
// S_OK on success
|
|
// ERROR code from CLinkMsgQueue::HrDeinitialize();
|
|
// History:
|
|
// 1/27/99 - MikeSwa Created
|
|
// 7/21/99 - MikeSwa Modified - removed free of link domain
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CDomainMappingTable::HrDeinitializeGlobalLink(IN OUT CLinkMsgQueue **pplmq)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
_ASSERT(pplmq);
|
|
if (pplmq && *pplmq)
|
|
{
|
|
hr = (*pplmq)->HrDeinitialize();
|
|
(*pplmq)->Release();
|
|
*pplmq = NULL;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
//---[ CDomainMappingTable::HrMapDomainName ]----------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Looks up a DomainName in the DMT. Will create a new entry if necessary
|
|
//
|
|
// Parameters:
|
|
// IN szDomainName Domain Name to map
|
|
// IN paqmtMessageType Message type as returned by routing
|
|
// IN pIMessageRouter IMessageRouter for this message
|
|
// OUT pdmap Mapping returned - allocated by caller
|
|
// OUT ppdmq ptr to Queue
|
|
// Returns:
|
|
// S_OK on success
|
|
// E_OUTOFMEMORY if an allocation fails
|
|
// HRESULT_FROM_WIN32(ERROR_RETRY) if mapping data changes and entire
|
|
// message should be re-mapped
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CDomainMappingTable::HrMapDomainName(
|
|
IN LPSTR szDomainName,
|
|
IN CAQMessageType *paqmtMessageType,
|
|
IN IMessageRouter *pIMessageRouter,
|
|
OUT CDomainMapping *pdmap,
|
|
OUT CDestMsgQueue **ppdmq)
|
|
{
|
|
TraceFunctEnterEx((LPARAM) this, "CDomainMappingTable::HrMapDomainName");
|
|
_ASSERT(pdmap);
|
|
_ASSERT(ppdmq);
|
|
_ASSERT(szDomainName);
|
|
_ASSERT(szDomainName[0] && "unsupported config - RAID #68208");
|
|
_ASSERT(pIMessageRouter);
|
|
|
|
HRESULT hr = S_OK;
|
|
CDomainEntry *pdentryResult = NULL;
|
|
CDomainEntry *pdentryExisting = NULL;
|
|
DWORD cbDomainName = 0;
|
|
CInternalDomainInfo *pIntDomainInfo= NULL;
|
|
BOOL fLocal = FALSE; //Is delivery local?
|
|
BOOL fWalkEmptyList= FALSE;
|
|
DOMAIN_STRING strDomain; //allows quicker lookups/inserts
|
|
CLinkMsgQueue *plmq = NULL;
|
|
|
|
*ppdmq = NULL;
|
|
cbDomainName = strlen(szDomainName)*sizeof(CHAR);
|
|
INIT_DOMAIN_STRING(strDomain, cbDomainName, szDomainName);
|
|
|
|
m_slPrivateData.ShareLock();
|
|
hr = m_dnt.HrFindDomainName(&strDomain, (PVOID *) &pdentryResult);
|
|
|
|
//
|
|
// If succeeded aquire usage lock before we give up DMT lock.
|
|
// Handle failure cases after releasing lock.
|
|
//
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pdentryResult->AddRef();
|
|
}
|
|
|
|
fWalkEmptyList = fNeedToWalkEmptyQueueList();
|
|
m_slPrivateData.ShareUnlock();
|
|
|
|
//
|
|
// Check and see if we need to delete empty queues.
|
|
//
|
|
if (fWalkEmptyList)
|
|
{
|
|
if (fDeleteExpiredQueues())
|
|
{
|
|
//something has changes
|
|
hr = HRESULT_FROM_WIN32(ERROR_RETRY);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (hr == DOMHASH_E_NO_SUCH_DOMAIN) //gotta create a new entry
|
|
{
|
|
DebugTrace((LPARAM) this, "Creating new DMT entry");
|
|
pdentryResult = new CDomainEntry(m_paqinst);
|
|
if (NULL == pdentryResult)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
_ASSERT(m_paqinst);
|
|
hr = m_paqinst->HrGetInternalDomainInfo(cbDomainName, szDomainName, &pIntDomainInfo);
|
|
if (FAILED(hr))
|
|
{
|
|
//It must match the "*" domain at least
|
|
_ASSERT(AQUEUE_E_INVALID_DOMAIN != hr);
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
_ASSERT(pIntDomainInfo);
|
|
if (pIntDomainInfo->m_DomainInfo.dwDomainInfoFlags &
|
|
DOMAIN_INFO_LOCAL_MAILBOX)
|
|
{
|
|
DebugTrace((LPARAM) NULL, "INFO: Local delivery queued.");
|
|
fLocal = TRUE;
|
|
}
|
|
}
|
|
|
|
//perform initialization of domain entry... create queues if needed
|
|
if (fLocal)
|
|
{
|
|
hr = HrInitLocalDomain(pdentryResult, &strDomain,
|
|
paqmtMessageType, pdmap);
|
|
}
|
|
else
|
|
{
|
|
hr = HrInitRemoteDomain(pdentryResult, &strDomain, pIntDomainInfo,
|
|
paqmtMessageType, pIMessageRouter, pdmap, ppdmq, &plmq);
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
ErrorTrace((LPARAM) this, "ERROR: Initializing %s domain %s - hr 0x%08X",
|
|
(fLocal ? "local" : "remote"), szDomainName, hr);
|
|
goto Exit;
|
|
}
|
|
|
|
m_slPrivateData.ExclusiveLock();
|
|
|
|
hr = HrPrvInsertDomainEntry(&strDomain, pdentryResult, FALSE, &pdentryExisting);
|
|
|
|
|
|
//Need to release exclusive lock in if/else clause
|
|
|
|
if (SUCCEEDED(hr)) //the insertion succeeded
|
|
{
|
|
pdentryResult->AddRef();
|
|
m_slPrivateData.ExclusiveUnlock();
|
|
DebugTrace((LPARAM) szDomainName, "INFO: Creating new entry in DMT for domain %s", szDomainName);
|
|
_ASSERT((fLocal || *ppdmq) && "Out param should be set here!"); //skip past getting value from table
|
|
|
|
if (!fLocal)
|
|
{
|
|
hr = plmq->HrAddQueue(*ppdmq);
|
|
if (FAILED(hr))
|
|
{
|
|
// Remove DMQ from link w/o notify since we never added it
|
|
(*ppdmq)->RemoveDMQFromLink(FALSE);
|
|
|
|
// Release the link we couldn't use and get the
|
|
// currently unreachable link
|
|
plmq->Release();
|
|
plmq = plmqGetCurrentlyUnreachable();
|
|
if (plmq)
|
|
{
|
|
(*ppdmq)->SetRouteInfo(plmq);
|
|
hr = plmq->HrAddQueue(*ppdmq);
|
|
if (FAILED(hr))
|
|
(*ppdmq)->RemoveDMQFromLink(FALSE);
|
|
}
|
|
|
|
// Schedule a reset routes to clean up the currently
|
|
// unreachable queue
|
|
m_paqinst->ResetRoutes(RESET_NEXT_HOPS);
|
|
}
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
else if (DOMHASH_E_DOMAIN_EXISTS == hr) //another inserted first
|
|
{
|
|
hr = S_OK; //not really a failure
|
|
DebugTrace((LPARAM) this, "Another thread inserted in the the DMT before us");
|
|
pdentryExisting->AddRef();
|
|
m_slPrivateData.ExclusiveUnlock();
|
|
|
|
_ASSERT(pdentryExisting != pdentryResult);
|
|
|
|
//release entry that we could not insert and replace with entry currently
|
|
//in the table
|
|
pdentryResult->HrDeinitialize();
|
|
pdentryResult->Release();
|
|
pdentryResult = NULL;
|
|
pdentryResult = pdentryExisting;
|
|
|
|
//Release queue if we have one
|
|
if (*ppdmq)
|
|
{
|
|
(*ppdmq)->Release();
|
|
*ppdmq = NULL;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
m_slPrivateData.ExclusiveUnlock();
|
|
//general failure to insert
|
|
|
|
//We must deinitialize the entry to force it to release any
|
|
//queues and links associated with it.
|
|
pdentryResult->HrDeinitialize();
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (!*ppdmq & !fLocal) //we did not create entry in the table
|
|
{
|
|
_ASSERT(pdentryResult);
|
|
|
|
//
|
|
// Prefix wants us to to more than assert.
|
|
// If HrFindDomainName() fails silently or
|
|
// fails with an error other than AQUEUE_E_INVALID_DOMAIN,
|
|
// will will hit this code path
|
|
//
|
|
if (!pdentryResult)
|
|
{
|
|
//
|
|
// Make sure HR is set.
|
|
//
|
|
if (SUCCEEDED(hr))
|
|
hr = E_FAIL;
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
//Domain Name already exists in table
|
|
//At this point, we need to pull an existing entry from the mapping
|
|
//get domain mapping
|
|
hr = pdentryResult->HrGetDomainMapping(pdmap);
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
|
|
//get queue
|
|
hr = pdentryResult->HrGetDestMsgQueue(paqmtMessageType, ppdmq);
|
|
if (FAILED(hr))
|
|
{
|
|
//entry exists, but no queue for our message type
|
|
_ASSERT(NULL == *ppdmq); //cannot fail if we create queue
|
|
|
|
//$$TODO cache domain config on entry
|
|
if (!pIntDomainInfo)
|
|
{
|
|
hr = m_paqinst->HrGetInternalDomainInfo(cbDomainName, szDomainName,
|
|
&pIntDomainInfo);
|
|
if (FAILED(hr))
|
|
{
|
|
//It must match the "*" domain at least
|
|
_ASSERT(AQUEUE_E_INVALID_DOMAIN != hr);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
_ASSERT(pIntDomainInfo);
|
|
if (!(pIntDomainInfo->m_DomainInfo.dwDomainInfoFlags &
|
|
DOMAIN_INFO_LOCAL_MAILBOX))
|
|
{
|
|
//this is a not a local domain entry
|
|
hr = HrCreateQueueForEntry(pdentryResult, &strDomain, pIntDomainInfo,
|
|
paqmtMessageType, pIMessageRouter, pdmap, ppdmq);
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
fLocal = TRUE;
|
|
}
|
|
|
|
}
|
|
}
|
|
_ASSERT((*ppdmq || fLocal) && "Non-local domains must have queue ptrs!");
|
|
|
|
Exit:
|
|
|
|
if (FAILED(hr)) //cleanup
|
|
{
|
|
if (pdentryResult)
|
|
{
|
|
pdentryResult->Release();
|
|
}
|
|
|
|
if (*ppdmq)
|
|
{
|
|
(*ppdmq)->Release();
|
|
*ppdmq = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (*ppdmq) {
|
|
// send link state notification saying that the link has
|
|
// been created
|
|
(*ppdmq)->SendLinkStateNotification();
|
|
}
|
|
if (pdentryResult) pdentryResult->Release();
|
|
}
|
|
|
|
if (plmq)
|
|
plmq->Release();
|
|
|
|
if (pIntDomainInfo)
|
|
pIntDomainInfo->Release();
|
|
|
|
TraceFunctLeave();
|
|
return hr;
|
|
}
|
|
|
|
//---[ CDomainMappingTable::HrPrvGetDomainEntry ]------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Internal private function to lookup Domain entry for given domain
|
|
// Parameters:
|
|
// IN cbDomainnameLength Length of string to search for
|
|
// IN szDomainName Domain Name to search for
|
|
// IN fDMTLocked TRUE if locks are already
|
|
// OUT ppdentry Domain Entry for domain (from DMT)
|
|
// Returns:
|
|
// S_OK on success
|
|
// AQUEUE_E_INVALID_DOMAIN if domain is not found
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CDomainMappingTable::HrPrvGetDomainEntry(IN DWORD cbDomainNameLength,
|
|
IN LPSTR szDomainName, BOOL fDMTLocked,
|
|
OUT CDomainEntry **ppdentry)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL fLocked = FALSE;
|
|
DOMAIN_STRING strDomain;
|
|
|
|
_ASSERT(cbDomainNameLength);
|
|
_ASSERT(szDomainName);
|
|
_ASSERT(ppdentry);
|
|
|
|
INIT_DOMAIN_STRING(strDomain, cbDomainNameLength, szDomainName);
|
|
|
|
if (!fDMTLocked)
|
|
{
|
|
m_slPrivateData.ShareLock();
|
|
fLocked = TRUE;
|
|
}
|
|
|
|
hr = m_dnt.HrFindDomainName(&strDomain, (PVOID *) ppdentry);
|
|
if (FAILED(hr))
|
|
{
|
|
if (DOMHASH_E_NO_SUCH_DOMAIN == hr)
|
|
hr = AQUEUE_E_INVALID_DOMAIN;
|
|
_ASSERT(NULL == *ppdentry);
|
|
goto Exit;
|
|
}
|
|
|
|
(*ppdentry)->AddRef();
|
|
|
|
Exit:
|
|
|
|
if (fLocked)
|
|
m_slPrivateData.ShareUnlock();
|
|
|
|
return hr;
|
|
}
|
|
|
|
//---[ CDomainMappingTable::HrPrvInsertDomainEntry ]---------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Private wrapper function for HrInsertDomainName
|
|
// Parameters:
|
|
// IN pstrDomainName Domain Name to insert in DNT
|
|
// IN pdnetryNew DomainEntry to insert
|
|
// IN fTreadAsWildcard TRUE if DNT to be told to treat as wildcard
|
|
// OUT pdentryOld Existing DomainEntry if there is one
|
|
// Returns:
|
|
//
|
|
// History:
|
|
// 10/5/1999 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CDomainMappingTable::HrPrvInsertDomainEntry(
|
|
IN PDOMAIN_STRING pstrDomainName,
|
|
IN CDomainEntry *pdentryNew,
|
|
IN BOOL fTreatAsWildcard,
|
|
OUT CDomainEntry **ppdentryOld)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
hr = m_dnt.HrInsertDomainName(pstrDomainName, pdentryNew, fTreatAsWildcard,
|
|
(PVOID *) ppdentryOld);
|
|
if (E_INVALIDARG == hr)
|
|
hr = PHATQ_BAD_DOMAIN_SYNTAX;
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
//---[ CDomainMappingTable::HrInitLocalDomain ]--------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Performs initialization needed for a local domain when an entry is
|
|
// created in the DMT
|
|
// Parameters:
|
|
// IN OUT pdentry - entry to init
|
|
// IN pStrDomain - domain name of entry
|
|
// IN paqmtMessageType Message Type of message
|
|
// OUT pdmap - Domain Mapping for domain
|
|
// Returns:
|
|
// S_OK - when all succeeds
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CDomainMappingTable::HrInitLocalDomain(
|
|
IN CDomainEntry *pdentry,
|
|
IN DOMAIN_STRING *pStrDomain,
|
|
IN CAQMessageType *paqmtMessageType,
|
|
OUT CDomainMapping *pdmap)
|
|
{
|
|
TraceFunctEnterEx((LPARAM) this, "CDomainMappingTable::HrInitLocalDomain");
|
|
HRESULT hr = S_OK;
|
|
LPSTR szKey = NULL;
|
|
|
|
_ASSERT(pdentry);
|
|
_ASSERT(pStrDomain);
|
|
_ASSERT(pdmap);
|
|
_ASSERT('\0' == pStrDomain->Buffer[pStrDomain->Length]);
|
|
|
|
//make copy of string to store in domain entry
|
|
szKey = (LPSTR) pvMalloc(pStrDomain->Length + sizeof(CHAR));
|
|
if (szKey == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
strcpy(szKey, pStrDomain->Buffer);
|
|
|
|
//passes ownership of szKey
|
|
hr = pdentry->HrInitialize(pStrDomain->Length, szKey, pdentry, NULL, NULL);
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
|
|
hr = pdentry->HrGetDomainMapping(pdmap);
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
|
|
Exit:
|
|
|
|
if (FAILED(hr) && szKey)
|
|
FreePv(szKey);
|
|
|
|
TraceFunctLeave();
|
|
return hr;
|
|
}
|
|
|
|
//---[ CDomainMappingTable::HrInitRemoteDomain ]--------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Performs initialization needed for a remote domain when an entry is
|
|
// created in the DMT.
|
|
// Parameters:
|
|
// IN pdentry - entry to init
|
|
// IN pStrDomain - domain name of entry
|
|
// IN pIntDomainInfo - Internal config info for domain
|
|
// IN paqmtMessageType - Message type returned by routing
|
|
// IN pIMessageRouter - Message Router interface for this message
|
|
// OUT pdmap - Domain Mapping for domain
|
|
// OUT ppdmq - destmsgqueue for domain
|
|
// OUT pplmq - LinkMsgQueue that this queue should be associated with
|
|
// caller should call HrAddQueue once entry is in DMT
|
|
// Returns:
|
|
// S_OK on success.
|
|
// E_OUTOFMEMORY when allocations fail
|
|
// History:
|
|
// 6/24/98 - Mikeswa Modified... added pplmq param and removed call to
|
|
// HrAddQueue
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CDomainMappingTable::HrInitRemoteDomain(
|
|
IN CDomainEntry *pdentry,
|
|
IN DOMAIN_STRING *pStrDomain,
|
|
IN CInternalDomainInfo *pIntDomainInfo,
|
|
IN CAQMessageType *paqmtMessageType,
|
|
IN IMessageRouter *pIMessageRouter,
|
|
OUT CDomainMapping *pdmap,
|
|
OUT CDestMsgQueue **ppdmq,
|
|
OUT CLinkMsgQueue **pplmq)
|
|
{
|
|
TraceFunctEnterEx((LPARAM) this, "CDomainMappingTable::HrInitRemoteDomain");
|
|
HRESULT hr = S_OK;
|
|
HRESULT hrRoutingDiag = S_OK;
|
|
LPSTR szKey = NULL;
|
|
CDestMsgQueue *pdmq = NULL;
|
|
CLinkMsgQueue *plmq = NULL;
|
|
BOOL fEntryInit = FALSE;
|
|
|
|
_ASSERT(ppdmq);
|
|
_ASSERT(pplmq);
|
|
_ASSERT(pdentry);
|
|
_ASSERT(pStrDomain);
|
|
_ASSERT(pdmap);
|
|
_ASSERT(pIMessageRouter);
|
|
_ASSERT('\0' == pStrDomain->Buffer[pStrDomain->Length]);
|
|
|
|
//Initialze out params
|
|
*ppdmq = NULL;
|
|
*pplmq = NULL;
|
|
|
|
//make copy of string to store in domain entry
|
|
szKey = (LPSTR) pvMalloc(pStrDomain->Length + sizeof(CHAR));
|
|
if (szKey == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
strcpy(szKey, pStrDomain->Buffer);
|
|
|
|
hr = HrGetNextHopLink(pdentry, szKey, pStrDomain->Length, pIntDomainInfo,
|
|
paqmtMessageType, pIMessageRouter, FALSE, &plmq, &hrRoutingDiag);
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
|
|
pdmq = new CDestMsgQueue(m_paqinst, paqmtMessageType, pIMessageRouter);
|
|
if (!pdmq)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
//passes ownership of szKey & pdmq
|
|
hr = pdentry->HrInitialize(pStrDomain->Length, szKey, pdentry, pdmq, NULL);
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
|
|
fEntryInit = TRUE; //we cannot delete pdmq or szKey now
|
|
|
|
//get the newly created domain mapping so we can initialize the queue
|
|
hr = pdentry->HrGetDomainMapping(pdmap);
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
|
|
//Initialize queue to use this domain mapping using the DomainMapping we just got
|
|
hr = pdmq->HrInitialize(pdmap);
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
|
|
//Associate link with DMQ
|
|
pdmq->SetRouteInfo(plmq);
|
|
|
|
//Set routing error if there was one.
|
|
pdmq->SetRoutingDiagnostic(hrRoutingDiag);
|
|
|
|
*ppdmq = pdmq;
|
|
*pplmq = plmq;
|
|
|
|
Exit:
|
|
|
|
//Cleanup failure cases
|
|
if (FAILED(hr) && !fEntryInit)
|
|
{
|
|
if (szKey)
|
|
FreePv(szKey);
|
|
|
|
if (NULL != pdmq)
|
|
{
|
|
//once domain entry has been initialized, it owns pdmq
|
|
pdmq->HrDeinitialize();
|
|
pdmq->Release();
|
|
_ASSERT(NULL == *ppdmq);
|
|
}
|
|
|
|
if (NULL != plmq)
|
|
{
|
|
plmq->HrDeinitialize();
|
|
}
|
|
}
|
|
|
|
if (plmq && !*pplmq) //we haven't passed refernce to OUT param
|
|
plmq->Release();
|
|
|
|
TraceFunctLeave();
|
|
return hr;
|
|
}
|
|
|
|
//---[ CDomainMappingTable::HrCreateQueueForEntry ]----------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Create a new queue for an already existing domain entry.
|
|
//
|
|
// Currently, this is done by creating a new queue and link, and
|
|
// attempting to associate the queue with the domain entry.
|
|
// Parameters:
|
|
// IN pdentry - entry to add queue to
|
|
// IN pStrDomain - domain name of entry
|
|
// IN pIntDomainInfo - Internal config info for domain
|
|
// IN paqmtMessageType - Message type returned by routing
|
|
// IN pIMessageRouter - Message Router interface for this message
|
|
// IN pdmap - Domain Mapping for domain
|
|
// OUT ppdmq - destmsgqueue for domain
|
|
// Returns:
|
|
// S_OK on succcess
|
|
// History:
|
|
// 6/2/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CDomainMappingTable::HrCreateQueueForEntry(
|
|
IN CDomainEntry *pdentry,
|
|
IN DOMAIN_STRING *pStrDomain,
|
|
IN CInternalDomainInfo *pIntDomainInfo,
|
|
IN CAQMessageType *paqmtMessageType,
|
|
IN IMessageRouter *pIMessageRouter,
|
|
IN CDomainMapping *pdmap,
|
|
OUT CDestMsgQueue **ppdmq)
|
|
{
|
|
TraceFunctEnterEx((LPARAM) this, "CDomainMappingTable::HrCreateQueueForEntry");
|
|
_ASSERT(pdentry);
|
|
HRESULT hr = S_OK;
|
|
HRESULT hrRoutingDiag = S_OK;
|
|
CDestMsgQueue *pdmq = NULL;
|
|
CLinkMsgQueue *plmq = NULL;
|
|
LPSTR szKey = pdentry->szGetDomainName();
|
|
|
|
*ppdmq = NULL;
|
|
_ASSERT(pStrDomain);
|
|
_ASSERT(pdmap);
|
|
_ASSERT(pIMessageRouter);
|
|
_ASSERT('\0' == pStrDomain->Buffer[pStrDomain->Length]);
|
|
|
|
|
|
hr = HrGetNextHopLink(pdentry, szKey, pStrDomain->Length, pIntDomainInfo,
|
|
paqmtMessageType, pIMessageRouter, FALSE, &plmq, &hrRoutingDiag);
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
|
|
pdmq = new CDestMsgQueue(m_paqinst, paqmtMessageType, pIMessageRouter);
|
|
if (NULL == pdmq)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
DebugTrace((LPARAM) szKey, "INFO: Creating new Destination Message Queue for domain %s", szKey);
|
|
|
|
hr = pdmq->HrInitialize(pdmap);
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
|
|
//Associate link with DMQ
|
|
pdmq->SetRouteInfo(plmq);
|
|
|
|
//Set routing error if there was one.
|
|
pdmq->SetRoutingDiagnostic(hrRoutingDiag);
|
|
|
|
//Now attempt to associate newly created queue/link pair with domain entry
|
|
hr = pdentry->HrAddUniqueDestMsgQueue(pdmq, ppdmq);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*ppdmq = pdmq;
|
|
|
|
//Only add the queue in this case... if a queue is already in the entry, then
|
|
//the other thread must have already (or soon will) call HrAddQueue... we
|
|
//should not call it twice.
|
|
hr = plmq->HrAddQueue(*ppdmq);
|
|
if (FAILED(hr))
|
|
{
|
|
// Remove DMQ from link w/o notify since we never added it
|
|
(*ppdmq)->RemoveDMQFromLink(FALSE);
|
|
|
|
// Release the link we couldn't use and get the
|
|
// currently unreachable link
|
|
plmq->Release();
|
|
plmq = plmqGetCurrentlyUnreachable();
|
|
if (plmq)
|
|
{
|
|
pdmq->SetRouteInfo(plmq);
|
|
hr = plmq->HrAddQueue(*ppdmq);
|
|
if (FAILED(hr))
|
|
{
|
|
(*ppdmq)->RemoveDMQFromLink(FALSE);
|
|
}
|
|
}
|
|
|
|
// Schedule a reset routes to clean up the currently
|
|
// unreachable queue
|
|
m_paqinst->ResetRoutes(RESET_NEXT_HOPS);
|
|
|
|
// If we still have a failed hr, goto exit
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DebugTrace((LPARAM) this, "INFO: Thread swap while trying to add queue for domain %s", szKey);
|
|
_ASSERT(*ppdmq != pdmq);
|
|
_ASSERT(*ppdmq && "HrAddUniqueDestMsgQueue failed without returning an error code");
|
|
hr = S_OK; //return new value
|
|
|
|
//Remove link from DMQ... since we will never call HrAddQueue
|
|
//don't notify link since it was never added
|
|
pdmq->RemoveDMQFromLink(FALSE);
|
|
}
|
|
|
|
|
|
Exit:
|
|
|
|
//Cleanup failure cases (including if queue created is not used)
|
|
if (FAILED(hr) || (*ppdmq != pdmq))
|
|
{
|
|
if (NULL != pdmq)
|
|
{
|
|
//once domain entry has been initialized, it owns pdmq
|
|
pdmq->Release();
|
|
}
|
|
}
|
|
|
|
if (NULL != plmq)
|
|
plmq->Release();
|
|
|
|
TraceFunctLeave();
|
|
return hr;
|
|
}
|
|
|
|
//---[ CDomainMappingTable::LogDomainUnreachableEvent] ------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Logs an event for an unreachable domain
|
|
// Parameters:
|
|
// IN fCurrentlyUnreachable Is the domain currently unreachable or
|
|
// completely unreachable?
|
|
// IN szDomain Final destination domain
|
|
// History:
|
|
// 3/8/99 - AWetmore Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void CDomainMappingTable::LogDomainUnreachableEvent(BOOL fCurrentlyUnreachable,
|
|
LPCSTR szDomain)
|
|
{
|
|
|
|
DWORD dwMessageId =
|
|
(fCurrentlyUnreachable) ? AQUEUE_DOMAIN_CURRENTLY_UNREACHABLE
|
|
: AQUEUE_DOMAIN_UNREACHABLE;
|
|
|
|
LPSTR rgszSubStrings[1];
|
|
|
|
rgszSubStrings[0] = (char*)szDomain;
|
|
|
|
if (m_paqinst)
|
|
{
|
|
m_paqinst->HrTriggerLogEvent(
|
|
dwMessageId, // Message ID
|
|
TRAN_CAT_QUEUE_ENGINE, // Category
|
|
1, // Word count of substring
|
|
(const char**)&rgszSubStrings[0], // Substring
|
|
EVENTLOG_WARNING_TYPE, // Type of the message
|
|
0, // No error code
|
|
LOGEVENT_LEVEL_MINIMUM, // Logging level
|
|
"", // Key to identify this event
|
|
LOGEVENT_FLAG_PERIODIC // Event logging option
|
|
);
|
|
}
|
|
}
|
|
|
|
//---[ CDomainMappingTable::HrGetNextHopLink ]------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Creates and initializes the CLinkMsgQueue object for this message
|
|
// (if neccessary). Calls router to get next hop info
|
|
// Parameters:
|
|
// IN pdentry Entry that is being initialized for destination
|
|
// IN szDomain Final destination domain
|
|
// IN cbDomain string length in bytes of domain (without \0)
|
|
// IN pIntDomainInfo Domain info for final destination domain
|
|
// IN paqmtMessageType Message type of this message
|
|
// IN pIMessageRouter Routing interface for this message
|
|
// IN fDMTLocked TRUE if DMT is already locked
|
|
// OUT pplmq Resulting link msg queue
|
|
// OUT phrRoutingDiag If next hop is unreachable, this tells us why
|
|
// Returns:
|
|
// S_OK on success
|
|
// History:
|
|
// 6/19/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CDomainMappingTable::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)
|
|
{
|
|
TraceFunctEnterEx((LPARAM) this, "CDomainMappingTable::HrGetNextHopLink");
|
|
HRESULT hr = S_OK;
|
|
BOOL fCalledGetNextHop = FALSE;
|
|
BOOL fValidSMTP = FALSE;
|
|
BOOL fOwnsScheduleId = FALSE;
|
|
LPSTR szRouteAddressType = NULL;
|
|
LPSTR szRouteAddress = NULL;
|
|
LPSTR szRouteAddressClass = NULL;
|
|
LPSTR szConnectorName = NULL;
|
|
DWORD dwScheduleID = 0;
|
|
DWORD dwNextHopType = 0;
|
|
CLinkMsgQueue *plmq = NULL;
|
|
CLinkMsgQueue *plmqTmp = NULL;
|
|
LPSTR szOwnedDomain = NULL; // string buffer that is "owned" by an entry
|
|
CDomainEntry *pdentryLink = NULL; //entry for link
|
|
CDomainEntry *pdentryTmp = NULL;
|
|
DOMAIN_STRING strNextHop;
|
|
DWORD cbRouteAddress = 0;
|
|
CAQScheduleID aqsched;
|
|
IMessageRouterLinkStateNotification *pILinkStateNotify = NULL;
|
|
LinkFlags lf = eLinkFlagsExternalSMTPLinkInfo;
|
|
*phrRoutingDiag = S_OK;
|
|
|
|
_ASSERT(pdentry);
|
|
_ASSERT(szDomain);
|
|
_ASSERT(pIntDomainInfo);
|
|
_ASSERT(paqmtMessageType);
|
|
_ASSERT(pIMessageRouter);
|
|
_ASSERT(pplmq);
|
|
|
|
hr = pIMessageRouter->QueryInterface(IID_IMessageRouterLinkStateNotification,
|
|
(VOID **) &pILinkStateNotify);
|
|
if (FAILED(hr))
|
|
{
|
|
pILinkStateNotify = NULL;
|
|
hr = S_OK;
|
|
}
|
|
|
|
//If we can route this domain.... call router to get next hop
|
|
//We do not route TURN/ETRN domains... or local drop domains
|
|
//Also check to see if the domain is configured as a local domain...
|
|
//if it is, return the local link
|
|
if (DOMAIN_INFO_LOCAL_MAILBOX & pIntDomainInfo->m_DomainInfo.dwDomainInfoFlags)
|
|
{
|
|
//The likely scenario of this happening is if a domain was previously
|
|
//configured as remote and then reconfigured as local.
|
|
m_plmqLocal->AddRef();
|
|
*pplmq = m_plmqLocal;
|
|
goto Exit;
|
|
}
|
|
else if (!(pIntDomainInfo->m_DomainInfo.dwDomainInfoFlags &
|
|
(DOMAIN_INFO_TURN_ONLY | DOMAIN_INFO_ETRN_ONLY | DOMAIN_INFO_LOCAL_DROP)))
|
|
{
|
|
hr = pIMessageRouter->GetNextHop(MTI_ROUTING_ADDRESS_TYPE_SMTP, szDomain,
|
|
paqmtMessageType->dwGetMessageType(), &szRouteAddressType,
|
|
&szRouteAddress, &dwScheduleID, &szRouteAddressClass,
|
|
&szConnectorName, &dwNextHopType);
|
|
|
|
fCalledGetNextHop = TRUE;
|
|
*pplmq = NULL;
|
|
|
|
if(MTI_NEXT_HOP_TYPE_UNREACHABLE == dwNextHopType)
|
|
{
|
|
//If the next hop is unreachable, store the reason for the unreachable
|
|
//error into *phrRoutingDiag (which is used by aqueue DSN generation).
|
|
|
|
const char *rgszStrings[2] = { szDomain, NULL };
|
|
|
|
if (m_paqinst)
|
|
{
|
|
m_paqinst->HrTriggerLogEvent(
|
|
PHATQ_UNREACHABLE_DOMAIN, // Message ID
|
|
TRAN_CAT_QUEUE_ENGINE, // Category
|
|
2, // Word count of substring
|
|
rgszStrings, // Substring
|
|
EVENTLOG_WARNING_TYPE, // Type of the message
|
|
hr, // error code
|
|
LOGEVENT_LEVEL_FIELD_ENGINEERING, // Logging level
|
|
"phatq", // key to this event
|
|
LOGEVENT_FLAG_PERIODIC, // Logging option
|
|
1, // index of format message string in rgszStrings
|
|
GetModuleHandle(AQ_MODULE_NAME) // module handle to format a message
|
|
);
|
|
}
|
|
|
|
ReUnreachableErrorToAqueueError(hr, phrRoutingDiag);
|
|
hr = S_OK;
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
RequestResetRoutesRetryIfNecessary();
|
|
ErrorTrace((LPARAM) this,
|
|
"GetNextHop failed with hr - 0x%08X", hr);
|
|
|
|
//treat all failures as a routing currently unreachable
|
|
hr = S_OK;
|
|
dwNextHopType = MTI_NEXT_HOP_TYPE_CURRENTLY_UNREACHABLE;
|
|
}
|
|
|
|
|
|
if (MTI_NEXT_HOP_TYPE_CURRENTLY_UNREACHABLE == dwNextHopType)
|
|
{
|
|
LogDomainUnreachableEvent(TRUE, szDomain);
|
|
*pplmq = m_plmqCurrentlyUnreachable;
|
|
}
|
|
else if (MTI_NEXT_HOP_TYPE_UNREACHABLE == dwNextHopType)
|
|
{
|
|
LogDomainUnreachableEvent(FALSE, szDomain);
|
|
*pplmq = m_plmqUnreachable;
|
|
}
|
|
else if ((MTI_NEXT_HOP_TYPE_SAME_VIRTUAL_SERVER == dwNextHopType) ||
|
|
(szRouteAddressType &&
|
|
lstrcmpi(MTI_ROUTING_ADDRESS_TYPE_SMTP, szRouteAddressType)))
|
|
{
|
|
//Handle any cases that might be considered local delivery
|
|
*pplmq = m_plmqLocal;
|
|
}
|
|
else if (!szRouteAddressType || ('\0' == *szRouteAddressType) ||
|
|
!szRouteAddress || ('\0' == *szRouteAddress))
|
|
{
|
|
//This is a bogus combination of values... try try again
|
|
hr = E_FAIL;
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
fValidSMTP = TRUE;
|
|
fOwnsScheduleId = TRUE;
|
|
//At this point we should have valid SMTP values for the address
|
|
_ASSERT(szRouteAddressType);
|
|
_ASSERT(szRouteAddress);
|
|
_ASSERT(!lstrcmpi(MTI_ROUTING_ADDRESS_TYPE_SMTP, szRouteAddressType));
|
|
|
|
if (MTI_NEXT_HOP_TYPE_PEER_SMTP1_BYPASS_CONFIG_LOOKUP == dwNextHopType ||
|
|
MTI_NEXT_HOP_TYPE_PEER_SMTP2_BYPASS_CONFIG_LOOKUP == dwNextHopType) {
|
|
lf = eLinkFlagsInternalSMTPLinkInfo;
|
|
}
|
|
|
|
}
|
|
|
|
if (!fValidSMTP)
|
|
{
|
|
//Must be going to one of them-there global queues.
|
|
hr = S_OK;
|
|
|
|
if (*pplmq)
|
|
(*pplmq)->AddRef();
|
|
else
|
|
hr = E_FAIL;
|
|
|
|
//Our work here is done
|
|
goto Exit;
|
|
}
|
|
|
|
}
|
|
|
|
if ((!fCalledGetNextHop) || (!lstrcmpi(szDomain, szRouteAddress)))
|
|
{
|
|
//final destination and next hop are the same
|
|
DebugTrace((LPARAM) this, "DEBUG: Routing case 1 - same next hop and final dest");
|
|
|
|
// See if there is already a link for this schedule ID
|
|
aqsched.Init(pIMessageRouter, dwScheduleID);
|
|
|
|
hr = pdentry->HrGetLinkMsgQueue(&aqsched, &plmq);
|
|
if (FAILED(hr))
|
|
{
|
|
hr = S_OK;
|
|
|
|
//link does not exist for this schedule id yet
|
|
plmq = new CLinkMsgQueue(dwScheduleID, pIMessageRouter,
|
|
pILinkStateNotify);
|
|
if (!plmq)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
fOwnsScheduleId = FALSE; //link now owns it
|
|
DebugTrace((LPARAM) szDomain, "INFO: Creating new Link for domain %s", szDomain);
|
|
|
|
hr = plmq->HrInitialize(m_paqinst, pdentry, cbDomain, szDomain,
|
|
lf, szConnectorName);
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
|
|
hr = pdentry->HrAddUniqueLinkMsgQueue(plmq, &plmqTmp);
|
|
if (FAILED(hr))
|
|
{
|
|
//Another link was inserted since we called get link msg queue
|
|
DebugTrace((LPARAM) this, "DEBUG: Routing case 2(a) - next hop link created by other thread");
|
|
_ASSERT(plmqTmp);
|
|
plmq->HrDeinitialize();
|
|
plmq->Release();
|
|
plmq = plmqTmp;
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DebugTrace((LPARAM) this, "DEBUG: Routing case 2(b) - next hop link created by other thread");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//next hop is different from final destination
|
|
cbRouteAddress = strlen(szRouteAddress);
|
|
|
|
//First see if there is an entry for this link
|
|
hr = HrPrvGetDomainEntry(cbRouteAddress, szRouteAddress, fDMTLocked, &pdentryLink);
|
|
if (AQUEUE_E_INVALID_DOMAIN == hr)
|
|
{
|
|
//an entry for this link does not exist... add one for this link
|
|
hr = S_OK;
|
|
DebugTrace((LPARAM) this, "DEBUG: Routing case 3 - next hop entry does not exist");
|
|
|
|
szOwnedDomain = (LPSTR) pvMalloc(sizeof(CHAR)*(cbRouteAddress+1));
|
|
if (!szOwnedDomain)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
lstrcpy(szOwnedDomain, szRouteAddress);
|
|
|
|
pdentryLink = new CDomainEntry(m_paqinst);
|
|
if (!pdentryLink)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
plmq = new CLinkMsgQueue(dwScheduleID, pIMessageRouter,
|
|
pILinkStateNotify);
|
|
if (!plmq)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
fOwnsScheduleId = FALSE; //link now owns it
|
|
|
|
//passes ownership of szOwnedDomain
|
|
hr = pdentryLink->HrInitialize(cbRouteAddress, szOwnedDomain,
|
|
pdentryLink, NULL, plmq);
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
|
|
hr = plmq->HrInitialize(m_paqinst, pdentryLink, cbRouteAddress,
|
|
szOwnedDomain, lf, szConnectorName);
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
|
|
//insert entry in DMT
|
|
strNextHop.Length = (USHORT) cbRouteAddress;
|
|
strNextHop.Buffer = szOwnedDomain;
|
|
strNextHop.MaximumLength = (USHORT) cbRouteAddress;
|
|
|
|
if (!fDMTLocked)
|
|
m_slPrivateData.ExclusiveLock();
|
|
|
|
hr = HrPrvInsertDomainEntry(&strNextHop, pdentryLink, FALSE, &pdentryTmp);
|
|
|
|
if (hr == DOMHASH_E_DOMAIN_EXISTS)
|
|
{
|
|
DebugTrace((LPARAM) this, "DEBUG: Routing case 4 - next hop entry did not exist... inserted by other thread");
|
|
plmq->Release();
|
|
plmq = NULL;
|
|
pdentryTmp->AddRef();
|
|
pdentryLink->HrDeinitialize();
|
|
pdentryLink->Release();
|
|
pdentryLink = pdentryTmp;
|
|
hr = S_OK;
|
|
|
|
//Will fall through to case as if an entry was found by HrGetDomainEntry
|
|
}
|
|
else if (SUCCEEDED(hr))
|
|
{
|
|
pdentryLink->AddRef();
|
|
}
|
|
|
|
if (!fDMTLocked)
|
|
m_slPrivateData.ExclusiveUnlock();
|
|
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
|
|
}
|
|
else if (FAILED(hr))
|
|
{
|
|
ErrorTrace((LPARAM) this, "ERROR: General DMT failure - hr 0x%08X", hr);
|
|
//general failure... bail
|
|
goto Exit;
|
|
}
|
|
|
|
if (!plmq)
|
|
{
|
|
DebugTrace((LPARAM) this, "DEBUG: Routing case 5 - next hop entry exists");
|
|
//An entry exists for this next hop... use link if possible
|
|
// 1 - Get link for this schedule ID.. if it exists use it
|
|
// 2 - Create another link and attempt to insert it
|
|
_ASSERT(pdentryLink);
|
|
aqsched.Init(pIMessageRouter, dwScheduleID);
|
|
|
|
hr = pdentryLink->HrGetLinkMsgQueue(&aqsched, &plmq);
|
|
if (FAILED(hr))
|
|
{
|
|
hr = S_OK;
|
|
//link does not exist for this schedule id yet
|
|
DebugTrace((LPARAM) this, "DEBUG: Routing case 6 - next hop link does not exist");
|
|
|
|
szOwnedDomain = pdentryLink->szGetDomainName();
|
|
|
|
plmq = new CLinkMsgQueue(dwScheduleID, pIMessageRouter,
|
|
pILinkStateNotify);
|
|
if (!plmq)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
fOwnsScheduleId = FALSE; //link now owns it
|
|
|
|
hr = plmq->HrInitialize(m_paqinst, pdentryLink, cbRouteAddress,
|
|
szOwnedDomain, lf, szConnectorName);
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
|
|
hr = pdentryLink->HrAddUniqueLinkMsgQueue(plmq, &plmqTmp);
|
|
if (FAILED(hr))
|
|
{
|
|
//Another link was inserted since we called get link msg queue
|
|
DebugTrace((LPARAM) this, "DEBUG: Routing case 7 - next hop link created by other thread");
|
|
_ASSERT(plmqTmp);
|
|
plmq->Release();
|
|
plmq = plmqTmp;
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DebugTrace((LPARAM) this, "DEBUG: Routing case 8 - next hop link exists");
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ASSERT(plmq && "We should have allocated a link by this point");
|
|
*pplmq = plmq;
|
|
|
|
Exit:
|
|
|
|
if (pdentryLink)
|
|
pdentryLink->Release();
|
|
|
|
if (fCalledGetNextHop)
|
|
{
|
|
//
|
|
// If we have not passed the schedule ID on to a link, we
|
|
// must notify routing that we are not using it (to avoid a leak).
|
|
// This needs to be be done *before* we release the strings
|
|
// and routing interfaces. If we hit this case, we have
|
|
// either failed to create a link, or another link has
|
|
// been created by another thread.
|
|
//
|
|
if (fOwnsScheduleId && pILinkStateNotify && pIMessageRouter)
|
|
{
|
|
FILETIME ftNotUsed = {0,0};
|
|
DWORD dwSetNotUsed = LINK_STATE_NO_ACTION;
|
|
DWORD dwUnsetNotUsed = LINK_STATE_NO_ACTION;
|
|
pILinkStateNotify->LinkStateNotify(
|
|
szDomain,
|
|
pIMessageRouter->GetTransportSinkID(),
|
|
dwScheduleID,
|
|
szConnectorName,
|
|
LINK_STATE_LINK_NO_LONGER_USED,
|
|
0, //consecutive failures
|
|
&ftNotUsed,
|
|
&dwSetNotUsed,
|
|
&dwUnsetNotUsed);
|
|
}
|
|
|
|
//
|
|
// Free Strings returned by GetNextHop
|
|
//
|
|
_VERIFY(SUCCEEDED(pIMessageRouter->GetNextHopFree(
|
|
MTI_ROUTING_ADDRESS_TYPE_SMTP,
|
|
szDomain,
|
|
szConnectorName,
|
|
szRouteAddressType,
|
|
szRouteAddress,
|
|
szRouteAddressClass)));
|
|
|
|
|
|
}
|
|
|
|
if (pILinkStateNotify)
|
|
pILinkStateNotify->Release();
|
|
|
|
if (FAILED(hr) && plmq)
|
|
plmq->Release();
|
|
|
|
TraceFunctLeave();
|
|
return hr;
|
|
}
|
|
|
|
//---[ CDomainMappingTable::HrGetOrCreateLink ]--------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Gets or creates a link object for a domain name
|
|
// Parameters:
|
|
// IN szRouteAddress Final destination domain
|
|
// IN cbRouteAddress string length in bytes of domain (without \0)
|
|
// IN dwScheduleID Schedule ID for link (used in create)
|
|
// IN szConnectorName Name (stringized GUID) of connector in DS
|
|
// IN pIMessageRouter Routing interface for this message (used in create)
|
|
// IN fCreateIfNotExist Create the link if it doesn't exist?
|
|
// OUT pplmq Resulting link msg queue
|
|
// OUT pfRemoveOwnedSchedule FALSE if a new link was created, TRUE on errors
|
|
// and in case the link was addref'ed.
|
|
// Returns:
|
|
// S_OK on success
|
|
// History:
|
|
// 7/6/1999 - AWetmore Created
|
|
// 12/30/1999 - MikeSwa Modified to not notify until connection attempt
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CDomainMappingTable::HrGetOrCreateLink(
|
|
IN LPSTR szRouteAddress,
|
|
IN DWORD cbRouteAddress,
|
|
IN DWORD dwScheduleID,
|
|
IN LPSTR szConnectorName,
|
|
IN IMessageRouter *pIMessageRouter,
|
|
IN BOOL fCreateIfNotExist,
|
|
IN DWORD linkFlags,
|
|
OUT CLinkMsgQueue **pplmq,
|
|
OUT BOOL *pfRemoveOwnedSchedule)
|
|
{
|
|
TraceFunctEnter("CDomainMappingTable::HrGetOrCreateLink");
|
|
HRESULT hr = S_OK;
|
|
BOOL fValidSMTP = FALSE;
|
|
LPSTR szRouteAddressType = NULL;
|
|
LPSTR szRouteAddressClass = NULL;
|
|
DWORD dwNextHopType = 0;
|
|
CLinkMsgQueue *plmq = NULL;
|
|
CLinkMsgQueue *plmqTmp = NULL;
|
|
LPSTR szOwnedDomain = NULL; // string buffer that is "owned" by an entry
|
|
CDomainEntry *pdentryLink = NULL; //entry for link
|
|
CDomainEntry *pdentryTmp = NULL;
|
|
DOMAIN_STRING strNextHop;
|
|
CAQScheduleID aqsched;
|
|
IMessageRouterLinkStateNotification *pILinkStateNotify = NULL;
|
|
CAQStats aqstats;
|
|
*pfRemoveOwnedSchedule = TRUE;
|
|
|
|
_ASSERT(szRouteAddress);
|
|
_ASSERT(pplmq);
|
|
|
|
//First see if there is an entry for this link
|
|
hr = HrPrvGetDomainEntry(cbRouteAddress, szRouteAddress, FALSE, &pdentryLink);
|
|
if (AQUEUE_E_INVALID_DOMAIN == hr && fCreateIfNotExist)
|
|
{
|
|
_ASSERT(pIMessageRouter);
|
|
hr = pIMessageRouter->QueryInterface(IID_IMessageRouterLinkStateNotification,
|
|
(VOID **) &pILinkStateNotify);
|
|
if (FAILED(hr)) {
|
|
pILinkStateNotify = NULL;
|
|
goto Exit;
|
|
}
|
|
|
|
//an entry for this link does not exist... add one for this link
|
|
hr = S_OK;
|
|
DebugTrace((LPARAM) this, "DEBUG: Routing case 3 - next hop entry does not exist");
|
|
|
|
szOwnedDomain = (LPSTR) pvMalloc(sizeof(CHAR)*(cbRouteAddress+1));
|
|
if (!szOwnedDomain)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
lstrcpy(szOwnedDomain, szRouteAddress);
|
|
|
|
pdentryLink = new CDomainEntry(m_paqinst);
|
|
if (!pdentryLink)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
plmq = new CLinkMsgQueue(dwScheduleID, pIMessageRouter,
|
|
pILinkStateNotify);
|
|
if (!plmq)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
*pfRemoveOwnedSchedule = FALSE;
|
|
|
|
//passes ownership of szOwnedDomain
|
|
hr = pdentryLink->HrInitialize(cbRouteAddress, szOwnedDomain,
|
|
pdentryLink, NULL, plmq);
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
|
|
hr = plmq->HrInitialize(m_paqinst, pdentryLink, cbRouteAddress,
|
|
szOwnedDomain, (LinkFlags) linkFlags, szConnectorName);
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
|
|
//insert entry in DMT
|
|
strNextHop.Length = (USHORT) cbRouteAddress;
|
|
strNextHop.Buffer = szOwnedDomain;
|
|
strNextHop.MaximumLength = (USHORT) cbRouteAddress;
|
|
|
|
m_slPrivateData.ExclusiveLock();
|
|
|
|
hr = HrPrvInsertDomainEntry(&strNextHop, pdentryLink, FALSE, &pdentryTmp);
|
|
|
|
if (hr == DOMHASH_E_DOMAIN_EXISTS)
|
|
{
|
|
DebugTrace((LPARAM) this, "DEBUG: Routing case 4 - next hop entry did not exist... inserted by other thread");
|
|
plmq->Release();
|
|
plmq = NULL;
|
|
pdentryTmp->AddRef();
|
|
pdentryLink->HrDeinitialize();
|
|
pdentryLink->Release();
|
|
pdentryLink = pdentryTmp;
|
|
hr = S_OK;
|
|
|
|
//Will fall through to case as if an entry was found by HrGetDomainEntry
|
|
}
|
|
else if (SUCCEEDED(hr))
|
|
{
|
|
pdentryLink->AddRef();
|
|
}
|
|
|
|
m_slPrivateData.ExclusiveUnlock();
|
|
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
|
|
}
|
|
else if (FAILED(hr))
|
|
{
|
|
ErrorTrace((LPARAM) this, "ERROR: General DMT failure - hr 0x%08X", hr);
|
|
//general failure... bail
|
|
goto Exit;
|
|
}
|
|
|
|
if (!plmq) {
|
|
_ASSERT(pdentryLink);
|
|
DebugTrace((LPARAM) this, "DEBUG: Routing case 5 - next hop entry exists");
|
|
//An entry exists for this next hop... use link if possible
|
|
// 1 - Get link for this schedule ID.. if it exists use it
|
|
// 2 - Create another link and attempt to insert it
|
|
_ASSERT(pdentryLink);
|
|
aqsched.Init(pIMessageRouter, dwScheduleID);
|
|
|
|
hr = pdentryLink->HrGetLinkMsgQueue(&aqsched, &plmq);
|
|
if (FAILED(hr) && fCreateIfNotExist)
|
|
{
|
|
hr = S_OK;
|
|
//link does not exist for this schedule id yet
|
|
DebugTrace((LPARAM) this, "DEBUG: Routing case 6 - next hop link does not exist");
|
|
|
|
szOwnedDomain = pdentryLink->szGetDomainName();
|
|
|
|
if (!pILinkStateNotify) {
|
|
_ASSERT(pIMessageRouter);
|
|
hr = pIMessageRouter->QueryInterface(IID_IMessageRouterLinkStateNotification,
|
|
(VOID **) &pILinkStateNotify);
|
|
if (FAILED(hr)) {
|
|
pILinkStateNotify = NULL;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
plmq = new CLinkMsgQueue(dwScheduleID, pIMessageRouter,
|
|
pILinkStateNotify);
|
|
if (!plmq)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
*pfRemoveOwnedSchedule = FALSE;
|
|
|
|
hr = plmq->HrInitialize(m_paqinst, pdentryLink, cbRouteAddress,
|
|
szOwnedDomain, (LinkFlags) linkFlags, szConnectorName);
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
|
|
hr = pdentryLink->HrAddUniqueLinkMsgQueue(plmq, &plmqTmp);
|
|
if (FAILED(hr))
|
|
{
|
|
//Another link was inserted since we called get link msg queue
|
|
DebugTrace((LPARAM) this, "DEBUG: Routing case 7 - next hop link created by other thread");
|
|
_ASSERT(plmqTmp);
|
|
plmq->Release();
|
|
plmq = plmqTmp;
|
|
hr = S_OK;
|
|
}
|
|
} else {
|
|
DebugTrace((LPARAM) this, "DEBUG: Routing case 8 - next hop link exists");
|
|
}
|
|
|
|
}
|
|
|
|
_ASSERT(plmq && "We should have allocated a link by this point");
|
|
|
|
if (plmq) {
|
|
|
|
//We count a SetLinkState as a link state notification.... do not
|
|
//notify again before attempting a connection or we will break link
|
|
//state notifications.
|
|
plmq->dwModifyLinkState(LINK_STATE_PRIV_HAVE_SENT_NOTIFICATION,
|
|
LINK_STATE_NO_ACTION);
|
|
aqstats.m_dwNotifyType = NotifyTypeNewLink | NotifyTypeLinkMsgQueue;
|
|
aqstats.m_plmq = plmq;
|
|
hr = m_paqinst->HrNotify(&aqstats, TRUE);
|
|
}
|
|
|
|
if (SUCCEEDED(hr)) *pplmq = plmq;
|
|
|
|
Exit:
|
|
|
|
if (pdentryLink)
|
|
pdentryLink->Release();
|
|
|
|
if (pILinkStateNotify)
|
|
pILinkStateNotify->Release();
|
|
|
|
if (FAILED(hr) && plmq)
|
|
plmq->Release();
|
|
|
|
TraceFunctLeave();
|
|
return hr;
|
|
}
|
|
|
|
//---[ CDomainMappingTable::AddDMQToEmptyList ]--------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Used by DMQ to add itself to the list of empty queues. This function
|
|
// will aquire (and release) the appropriate locks
|
|
// Parameters:
|
|
// IN pdmq DestMsgQueue to add to list
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 9/12/98 - MikeSwa Created
|
|
// 5/5/99 - MikeSwa Changed to TryExclusiveLock to avoid potential
|
|
// deadlock.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void CDomainMappingTable::AddDMQToEmptyList(CDestMsgQueue *pdmq)
|
|
{
|
|
TraceFunctEnterEx((LPARAM) this, "CDomainMappingTable::AddDMQToEmptyList");
|
|
if (m_slPrivateData.TryExclusiveLock())
|
|
{
|
|
pdmq->InsertQueueInEmptyQueueList(&m_liEmptyDMQHead);
|
|
m_slPrivateData.ExclusiveUnlock();
|
|
}
|
|
else
|
|
{
|
|
DebugTrace((LPARAM) this,
|
|
"AddDMQToEmptyList could not get m_slPrivateData Lock");
|
|
}
|
|
TraceFunctLeave();
|
|
}
|
|
|
|
//---[ CDomainMappingTable::fNeedToWalkEmptyQueueList ]------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Checks list of Empty queues to see if we need to call
|
|
// fDeleteExpiredQueues(). The caller of this function *must* have
|
|
// m_slPrivateData is shared mode, if it returns TRUE, the caller *must*
|
|
// release the lock and call fDeleteExpiredQueues() (which will aquire
|
|
// the lock Exclusively)
|
|
// Parameters:
|
|
// -
|
|
// Returns:
|
|
// TRUE if fDeleteExpiredQueues should be called.
|
|
// History:
|
|
// 9/12/98 - MikeSwa Created
|
|
// 6/27/2000 - MikeSwa fixed short-circuit logic
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
BOOL CDomainMappingTable::fNeedToWalkEmptyQueueList()
|
|
{
|
|
BOOL fRet = FALSE;
|
|
PLIST_ENTRY pli = m_liEmptyDMQHead.Flink;
|
|
CDestMsgQueue *pdmq = NULL;
|
|
DWORD dwDMQState = 0;
|
|
DWORD cMisplacedQueues = 0; //# of queues in list that should not be
|
|
|
|
_ASSERT(pli);
|
|
|
|
// Only one thread at a time should be searching.
|
|
if (1 != InterlockedIncrement((PLONG) &m_cThreadsForEmptyDMQList))
|
|
{
|
|
InterlockedDecrement((PLONG) &m_cThreadsForEmptyDMQList);
|
|
return FALSE;
|
|
}
|
|
|
|
if(m_dwFlags & DMT_FLAGS_RESET_ROUTES_IN_PROGRESS)
|
|
return fRet; // we shouldn't remove queues during reset routes
|
|
|
|
while (&m_liEmptyDMQHead != pli)
|
|
{
|
|
pdmq = CDestMsgQueue::pdmqGetDMQFromEmptyListEntry(pli);
|
|
_ASSERT(pdmq);
|
|
dwDMQState = pdmq->dwGetDMQState();
|
|
|
|
//See if it is empty and expired.. if so, we have a winner
|
|
if (dwDMQState & CDestMsgQueue::DMQ_EMPTY)
|
|
{
|
|
if (dwDMQState & CDestMsgQueue::DMQ_EXPIRED)
|
|
{
|
|
fRet = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The queue is no longer empty... we will remove it
|
|
// from this list the next time we have the exclusive
|
|
// lock.
|
|
//
|
|
cMisplacedQueues++;
|
|
|
|
//
|
|
// If there are a large number of non-empty DMQs, we
|
|
// want wish to return TRUE even though there are no DMQs to
|
|
// delete... just so we can clean the list of non-empty DMQs.
|
|
//
|
|
if (MAX_MISPLACED_QUEUES_IN_EMPTY_LIST < cMisplacedQueues)
|
|
{
|
|
fRet = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
pli = pli->Flink;
|
|
|
|
}
|
|
|
|
// If we decide to delete queues we keep this count until we have done
|
|
// the work, if we decide not to then we release the count so someone
|
|
// else will walk the list again
|
|
if(fRet == FALSE)
|
|
{
|
|
InterlockedDecrement((PLONG) &m_cThreadsForEmptyDMQList);
|
|
}
|
|
|
|
//NOTE: The reason there may be non-empty queues in this list, is that
|
|
//we cannot remove the queues from the list when we ENQUEUE a message
|
|
//because it would deadlock (we already have the m_slPrivateData lock
|
|
//in shared mode), and removing the queue requires this lock in exclusive
|
|
//mode.
|
|
return fRet;
|
|
}
|
|
|
|
//---[ CDomainMappingTable::fDeleteExpiredQueues ]-----------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Removes DMQs from empty list. DMQs will be deleted if they have expired
|
|
// and do not have any messages on them. Non-empty DMQs will be removed
|
|
// from the list as well.
|
|
//
|
|
// The DMT m_slPrivateData lock should *not* be held when this is called.
|
|
// This function will aquire it exclusively.
|
|
//
|
|
// Parameters:
|
|
// -
|
|
// Returns:
|
|
// TRUE if any queues, links, or entries were deleted
|
|
// FALSE if no queues were deleted
|
|
// History:
|
|
// 9/12/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
BOOL CDomainMappingTable::fDeleteExpiredQueues()
|
|
{
|
|
PLIST_ENTRY pli = NULL;
|
|
CDestMsgQueue *pdmq = NULL;
|
|
CLinkMsgQueue *plmq = NULL;
|
|
DWORD dwDMQState = 0;
|
|
CDomainMapping *pdmap = NULL;
|
|
CDomainEntry *pdentry = NULL;
|
|
CDomainEntry *pdentryOld = NULL;
|
|
BOOL fRemovedQueues = FALSE;
|
|
HRESULT hr = S_OK;
|
|
DOMAIN_STRING strDomain;
|
|
|
|
// Don't attempt this if it would require waiting for a lock. This
|
|
// decreases contention and also prevents multiple threads from trying
|
|
// to do this work
|
|
if (!m_slPrivateData.TryExclusiveLock())
|
|
goto Exit;
|
|
|
|
pli = m_liEmptyDMQHead.Flink;
|
|
while (&m_liEmptyDMQHead != pli)
|
|
{
|
|
_ASSERT(pli);
|
|
pdmq = CDestMsgQueue::pdmqGetDMQFromEmptyListEntry(pli);
|
|
_ASSERT(pdmq);
|
|
dwDMQState = pdmq->dwGetDMQState();
|
|
|
|
if (!(dwDMQState & CDestMsgQueue::DMQ_EMPTY))
|
|
{
|
|
//If it is not empty - remove it from the list
|
|
pli = pli->Flink;
|
|
pdmq->RemoveQueueFromEmptyQueueList();
|
|
continue;
|
|
}
|
|
else if (!(dwDMQState & CDestMsgQueue::DMQ_EXPIRED))
|
|
{
|
|
//If this queue hasn't expired... check the next to see if it is empty
|
|
pli = pli->Flink;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
//We need to delete this DMQ
|
|
pli = pli->Flink; //get next LIST_ENTRY before we delete the queue
|
|
|
|
//Add a reference to the DMQ, so we can guarantee its lifespan
|
|
pdmq->AddRef();
|
|
|
|
//Remove the queue from the list of empty queues
|
|
pdmq->RemoveQueueFromEmptyQueueList();
|
|
|
|
//Get the domain mapping (and domain entry) for this DMQ
|
|
pdmq->GetDomainMapping(&pdmap);
|
|
_ASSERT(pdmap);
|
|
pdentry = pdmap->pdentryGetQueueEntry();
|
|
|
|
//Remove the DMQ from its associated link
|
|
plmq = pdmq->plmqGetLink();
|
|
pdmq->RemoveDMQFromLink(TRUE);
|
|
if (plmq)
|
|
{
|
|
//Remove the link from the DMT if it is empty
|
|
plmq->RemoveLinkIfEmpty();
|
|
plmq->Release();
|
|
plmq = NULL;
|
|
}
|
|
|
|
//Now that we have the domain entry, we can remove it the DMQ
|
|
//from it.
|
|
_ASSERT(pdentry);
|
|
pdentry->RemoveDestMsgQueue(pdmq);
|
|
|
|
//Remove Entry if needed
|
|
if (pdentry->fSafeToRemove())
|
|
{
|
|
//There are no links or queues left on this entry... we
|
|
//can remove it from the hash table and delete it
|
|
pdentry->InitDomainString(&strDomain);
|
|
hr = m_dnt.HrRemoveDomainName(&strDomain, (void **) &pdentryOld);
|
|
|
|
_ASSERT(DOMHASH_E_NO_SUCH_DOMAIN != hr);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_ASSERT(pdentryOld == pdentry);
|
|
pdentryOld = NULL;
|
|
pdentry->Release();
|
|
pdentry = NULL;
|
|
}
|
|
}
|
|
|
|
//If there are no enqueues pending, this will be the last reference
|
|
//for the DMQ. If there is a enqueue pending, then there may be
|
|
//references outstanding that will be released when they see the
|
|
//updated DMT version number.
|
|
pdmq->Release();
|
|
|
|
fRemovedQueues = TRUE;
|
|
}
|
|
}
|
|
|
|
//Update version number, so other threads will know that queues
|
|
//have been removed from the DMT.
|
|
if (fRemovedQueues)
|
|
m_dwInternalVersion++;
|
|
|
|
m_slPrivateData.ExclusiveUnlock();
|
|
|
|
Exit:
|
|
// Release our count so someone else can search the list and possibly
|
|
// remove queues ...
|
|
InterlockedDecrement((PLONG) &m_cThreadsForEmptyDMQList);
|
|
return fRemovedQueues;
|
|
}
|
|
|
|
|
|
//---[ CDomainMappingTable::RequestResetRoutesRetryIfNecessary ]---------------
|
|
//
|
|
//
|
|
// Description:
|
|
// This is called when routing fails and we need to call reset routes
|
|
// at a later time to try it.
|
|
// Parameters:
|
|
// -
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 11/15/1999 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void CDomainMappingTable::RequestResetRoutesRetryIfNecessary()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
dwInterlockedSetBits(&m_dwFlags, DMT_FLAGS_GET_NEXT_HOP_FAILED);
|
|
|
|
if (DMT_FLAGS_RESET_ROUTES_IN_PROGRESS & m_dwFlags)
|
|
return;
|
|
|
|
//We need to reqest a callback for a later reset routes. We should
|
|
//only allow one callback pending at a time. If this thread increments
|
|
//the count for a 0->1 transition, then we can request the callback
|
|
if (1 == InterlockedIncrement((PLONG) &m_cResetRoutesRetriesPending))
|
|
{
|
|
hr = m_paqinst->SetCallbackTime(
|
|
CDomainMappingTable::RetryResetRoutes,
|
|
this, g_cResetRoutesRetryMinutes);
|
|
|
|
if (FAILED(hr))
|
|
InterlockedDecrement((PLONG) &m_cResetRoutesRetriesPending);
|
|
}
|
|
}
|
|
|
|
//---[ CDomainMappingTable::RetryResetRoutes ]--------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Handles callback for reset routes. This codepath will be used if
|
|
// GetNextHop failes. Routing has no internal logic to remember if
|
|
// a failure has happened... and no method of sceduling a callback. By
|
|
// periodically call reset routes, we can centrally solve this problem
|
|
// for all routing sinks.
|
|
// Parameters:
|
|
// pvThis "this" pointer for CDomainMappingTable
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 11/15/1999 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void CDomainMappingTable::RetryResetRoutes(PVOID pvThis)
|
|
{
|
|
_ASSERT(pvThis);
|
|
CDomainMappingTable *pdmt = (CDomainMappingTable *)pvThis;
|
|
CAQSvrInst *paqinst = pdmt->m_paqinst;
|
|
|
|
//Make sure shutdown has not been started. This instance is waits for
|
|
//all threads before deleting itself, so it is safe to call in to the local
|
|
//variable
|
|
if (!paqinst)
|
|
return;
|
|
|
|
//Decrement the count, so another request can be queued up.
|
|
InterlockedDecrement((PLONG) &(pdmt->m_cResetRoutesRetriesPending));
|
|
|
|
//Kick off another reset routes
|
|
paqinst->ResetRoutes(RESET_NEXT_HOPS);
|
|
}
|
|
|
|
//---[ CDomainMappingTable::HrBeginRerouteDomains ]----------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Begins the process of handing a ResetRoutes. This function sets
|
|
// DMT_FLAGS_RESET_ROUTES_IN_PROGRESS and then moves all queues into
|
|
// the CurrentlyUnreachable link so that they will not be processed
|
|
// until they have been re-routed.
|
|
// Parameters:
|
|
// NONE
|
|
// Returns:
|
|
// S_OK on success
|
|
// History:
|
|
// 11/5/1998 - MikeSwa Created
|
|
// 10/31/2000 - dbraun - renamed / was part of HrRerouteDomains
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CDomainMappingTable::HrBeginRerouteDomains()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DWORD dwFlags = 0;
|
|
|
|
m_slPrivateData.ExclusiveLock();
|
|
|
|
//Clear the failed bit before we make any calls into routing. This way
|
|
//we can detect a failure that has happened during this reset routes
|
|
dwInterlockedUnsetBits(&m_dwFlags, DMT_FLAGS_GET_NEXT_HOP_FAILED);
|
|
|
|
//Make sure this flag is set. This will prevent a reset routes request
|
|
//from being generated while this thread is resetting routes. If
|
|
//GetNextHop is still failing, we want to start the retry timer
|
|
//*after* we finish we reset routes, or we will get stuck in a loop
|
|
//of constant reset routes until GetNextHop succeeds.
|
|
dwInterlockedSetBits(&m_dwFlags, DMT_FLAGS_RESET_ROUTES_IN_PROGRESS);
|
|
|
|
//Move all of the domains to the currently unreachable link
|
|
hr = m_dnt.HrIterateOverSubDomains(NULL,
|
|
CDomainMappingTable::MakeSingleDomainCurrentlyUnreachable, this);
|
|
m_slPrivateData.ExclusiveUnlock();
|
|
|
|
//Handle a failure to move the domains - if this function fails then
|
|
//the caller won't call HrCompleteRerouteDomains so we need to clear
|
|
//the reset routes in progress bit and potentially request another
|
|
//ResetRoutes
|
|
if(FAILED(hr)) {
|
|
dwFlags = dwInterlockedUnsetBits(&m_dwFlags, DMT_FLAGS_RESET_ROUTES_IN_PROGRESS);
|
|
|
|
if (DMT_FLAGS_GET_NEXT_HOP_FAILED & dwFlags) {
|
|
//This reset routes failed... we must try again later.
|
|
RequestResetRoutesRetryIfNecessary();
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//---[ CDomainMappingTable::HrCompleteRerouteDomains ]-------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// This function completes the handling of a ResetRoutes. Mail flow has
|
|
// been restarted and this function will go through all the domains, now
|
|
// sitting in CurrentlyUnreachable, and will re-route them according to
|
|
// the new routing configuration.
|
|
// Parameters:
|
|
// NONE
|
|
// Returns:
|
|
// S_OK on success
|
|
// History:
|
|
// 11/5/1998 - MikeSwa Created
|
|
// 10/31/2000 - dbraun - renamed / was part of HrRerouteDomains
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CDomainMappingTable::HrCompleteRerouteDomains()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DWORD dwFlags = 0;
|
|
|
|
/* -- this would only be applicable if we supported a "partial retry
|
|
reset routes" that only called this function not the Begin above
|
|
|
|
// It is possible that we are being called WITHOUT HrBeginRerouteDomains
|
|
// being called first (if we failed here before the retry will only retry
|
|
// this part of rerouting) ... we need to make sure that
|
|
// DMT_FLAGS_RESET_ROUTES_IN_PROGRESS is set to ensure this proceeds safely
|
|
// and we need to make sure that DMT_FLAGS_GET_NEXT_HOP_FAILED is not set
|
|
// so we can pick up a failure during rerouting
|
|
dwInterlockedSetBits(&m_dwFlags, DMT_FLAGS_RESET_ROUTES_IN_PROGRESS);
|
|
dwInterlockedUnsetBits(&m_dwFlags, DMT_FLAGS_GET_NEXT_HOP_FAILED);
|
|
*/
|
|
|
|
// Rebuild routing information queues in the CurrentlyUnreachable link.
|
|
hr = HrRerouteLink(m_plmqCurrentlyUnreachable);
|
|
|
|
dwFlags = dwInterlockedUnsetBits(&m_dwFlags, DMT_FLAGS_RESET_ROUTES_IN_PROGRESS);
|
|
|
|
if (DMT_FLAGS_GET_NEXT_HOP_FAILED & dwFlags)
|
|
{
|
|
// This reset routes failed... we must try again later.
|
|
RequestResetRoutesRetryIfNecessary();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//---[ CDomainMappingTable::MakeSingleDomainCurrentlyUnreachable ]-------------
|
|
//
|
|
//
|
|
// Description:
|
|
// This iterator moves a given domain's queues to the CurrentlyUnreachable
|
|
// link to prevent SMTP from operating on them until they can be re-routed.
|
|
// Parameters:
|
|
// IN pvContext - pointer to context
|
|
// (pointer to BOOL that tells if we force a re-route)
|
|
// IN pvData - CDomainEntry for the given domain
|
|
// IN fWildcardData - TRUE if data is a wildcard entry
|
|
// OUT pfContinue - TRUE if iterator should continue to the next entry
|
|
// OUT pfDelete - TRUE if entry should be deleted
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 10/31/2000 - dbraun - created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
VOID CDomainMappingTable::MakeSingleDomainCurrentlyUnreachable(
|
|
PVOID pvContext, PVOID pvData,
|
|
BOOL fWildcard, BOOL *pfContinue,
|
|
BOOL *pfDelete)
|
|
{
|
|
TraceFunctEnterEx((LPARAM) pvContext, "CDomainMappingTable::MakeSingleDomainCurrentlyUnreachable");
|
|
|
|
CDomainEntry *pdentry = (CDomainEntry *) pvData;
|
|
HRESULT hr = S_OK;
|
|
CDestMsgQueue *pdmq = NULL;
|
|
CDomainMappingTable *pThis = (CDomainMappingTable *) pvContext;
|
|
CDomainEntryQueueIterator deqit;
|
|
CAQStats aqstat;
|
|
|
|
_ASSERT(pfContinue);
|
|
_ASSERT(pfDelete);
|
|
|
|
*pfDelete = FALSE;
|
|
*pfContinue = TRUE;
|
|
|
|
if (fWildcard)
|
|
return; //we shouldn't care about wildcard entries
|
|
|
|
_ASSERT(pdentry);
|
|
|
|
//
|
|
// Initialize the aqstat to type reroute
|
|
//
|
|
aqstat.m_dwNotifyType = NotifyTypeReroute;
|
|
|
|
//Loop through dmq's and blow away their routing information -
|
|
//then route them to the currently unreachable link
|
|
hr = deqit.HrInitialize(pdentry);
|
|
if (FAILED(hr))
|
|
pdmq = NULL;
|
|
else
|
|
pdmq = deqit.pdmqGetNextDestMsgQueue(pdmq);
|
|
while (pdmq)
|
|
{
|
|
//Remove this queue from it's link
|
|
pdmq->RemoveDMQFromLink(TRUE);
|
|
|
|
//
|
|
// Figure out if it is useful to route this queue
|
|
// before we call into routing
|
|
//
|
|
if (pdmq->fIsEmptyAndAbandoned())
|
|
pdentry->RemoveDestMsgQueue(pdmq);
|
|
else
|
|
{
|
|
//Add this queue to the currently unreachable link
|
|
hr = pThis->m_plmqCurrentlyUnreachable->HrAddQueue(pdmq);
|
|
if (FAILED(hr)) {
|
|
//We failed to add the queue to the currently unreachable link
|
|
ErrorTrace((LPARAM) pvContext,
|
|
"HrAddQueue failed on reroute hr - 0x%08X", hr);
|
|
hr = S_OK;
|
|
}
|
|
pdmq->SetRouteInfo(pThis->m_plmqCurrentlyUnreachable);
|
|
|
|
//
|
|
// Make sure the link is associated with the connection manager
|
|
//
|
|
hr = pThis->m_plmqCurrentlyUnreachable->HrNotify(&aqstat, TRUE);
|
|
if (FAILED(hr)) {
|
|
//If this fails... we *may* leak an unused link until the
|
|
//next reset routes (or shutdown).
|
|
ErrorTrace((LPARAM) pvContext,
|
|
"HrNotify failed on reroute hr - 0x%08X", hr);
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
|
|
//On to the next queue for this entry
|
|
pdmq = deqit.pdmqGetNextDestMsgQueue(pdmq);
|
|
}
|
|
|
|
//
|
|
// If it is safe to clean this up... then do so
|
|
//
|
|
if (pdentry->fSafeToRemove())
|
|
{
|
|
*pfDelete = TRUE;
|
|
pdentry->Release();
|
|
pdentry = NULL;
|
|
}
|
|
|
|
TraceFunctLeave();
|
|
}
|
|
|
|
//---[ CDomainMappingTable::HrRerouteLink ]------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Function that is used to re-route all queues on a given link.
|
|
// Parameters:
|
|
// IN plmq - pointer link to be rerouted
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 11/8/2000 - dbraun created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CDomainMappingTable::HrRerouteLink(CLinkMsgQueue *plmqReroute)
|
|
{
|
|
TraceFunctEnterEx((LPARAM) this, "CDomainMappingTable::HrRerouteLink");
|
|
CDomainEntry *pdentry = NULL;
|
|
CDestMsgQueue *pdmq = NULL;
|
|
CLinkMsgQueue *plmq = NULL;
|
|
CInternalDomainInfo *pIntDomainInfo = NULL;
|
|
HRESULT hr = S_OK;
|
|
HRESULT hrRoutingDiag = S_OK;
|
|
CQuickList *pqlQueueList = NULL;
|
|
DWORD cQueues = 0;
|
|
DWORD dwIndex = 0;
|
|
PVOID pvContext = NULL;
|
|
CDomainMapping *pdmap = NULL;
|
|
CAQStats aqstat;
|
|
|
|
_ASSERT(plmqReroute);
|
|
|
|
// Initialize the aqstat to type reroute
|
|
aqstat.m_dwNotifyType = NotifyTypeReroute;
|
|
|
|
// Loop through dmq's and re-route them
|
|
hr = plmqReroute->HrGetQueueListSnapshot(&pqlQueueList);
|
|
if (FAILED(hr))
|
|
{
|
|
cQueues = 0;
|
|
}
|
|
else
|
|
{
|
|
cQueues = pqlQueueList->dwGetCount();
|
|
}
|
|
|
|
for (dwIndex = 0; dwIndex < cQueues; dwIndex++)
|
|
{
|
|
// Get the dest msg queue
|
|
pdmq = (CDestMsgQueue *) pqlQueueList->pvGetItem(dwIndex, &pvContext);
|
|
|
|
// Get the domain for this queue
|
|
pdmq->GetDomainMapping(&pdmap);
|
|
_ASSERT(pdmap);
|
|
pdentry = pdmap->pdentryGetQueueEntry();
|
|
|
|
// Get the internal domain info for this domain
|
|
hr = m_paqinst->HrGetInternalDomainInfo(
|
|
pdentry->cbGetDomainNameLength(),
|
|
pdentry->szGetDomainName(),
|
|
&pIntDomainInfo);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
ErrorTrace((LPARAM) this, "ERROR: Unable to get config for domain - hr 0x%08X", hr);
|
|
pIntDomainInfo = NULL;
|
|
}
|
|
|
|
if (pIntDomainInfo)
|
|
{
|
|
hr = HrGetNextHopLink(
|
|
pdentry, pdentry->szGetDomainName(),
|
|
pdentry->cbGetDomainNameLength(), pIntDomainInfo,
|
|
pdmq->paqmtGetMessageType(), pdmq->pIMessageRouterGetRouter(),
|
|
FALSE, // DMT is not locked
|
|
&plmq, &hrRoutingDiag);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
//$$TODO - Deal with more exotic Get next hop errors
|
|
ErrorTrace((LPARAM) this,
|
|
"ERROR: Unable to get next hop for domain - hr 0x%08X", hr);
|
|
}
|
|
|
|
// Update with new routing info if we can
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Remove from the old link
|
|
pdmq->RemoveDMQFromLink(TRUE);
|
|
|
|
// Add to the new one
|
|
hr = plmq->HrAddQueue(pdmq);
|
|
if (FAILED(hr))
|
|
{
|
|
// Release the link we couldn't use and get the
|
|
// currently unreachable link
|
|
plmq->Release();
|
|
plmq = plmqGetCurrentlyUnreachable();
|
|
if (plmq)
|
|
hr = plmq->HrAddQueue(pdmq);
|
|
|
|
// If we still have a failed hr, remove the DMQ from
|
|
// it's link, otherwise, make sure it is associated with
|
|
// the link we just added it to
|
|
if (FAILED(hr))
|
|
pdmq->RemoveDMQFromLink(TRUE);
|
|
else
|
|
pdmq->SetRouteInfo(plmq);
|
|
|
|
// Request reset routes retry
|
|
RequestResetRoutesRetryIfNecessary();
|
|
}
|
|
else
|
|
pdmq->SetRouteInfo(plmq);
|
|
|
|
// Set routing error if there was one.
|
|
pdmq->SetRoutingDiagnostic(hrRoutingDiag);
|
|
|
|
//
|
|
// Make sure the link is associated with the connection manager
|
|
//
|
|
hr = plmq->HrNotify(&aqstat, TRUE);
|
|
if (FAILED(hr))
|
|
{
|
|
// If this fails... we *may* leak an unused link until the
|
|
// next reset routes (or shutdown).
|
|
ErrorTrace((LPARAM) this,
|
|
"HrNotify failed on reroute hr - 0x%08X", hr);
|
|
hr = S_OK;
|
|
}
|
|
plmq->Release();
|
|
plmq = NULL;
|
|
}
|
|
|
|
// Release the domain info
|
|
pIntDomainInfo->Release();
|
|
pIntDomainInfo = NULL;
|
|
}
|
|
}
|
|
|
|
_ASSERT(!pIntDomainInfo);
|
|
|
|
if (pqlQueueList)
|
|
delete pqlQueueList;
|
|
|
|
TraceFunctLeave();
|
|
|
|
return hr;
|
|
}
|
|
|
|
//---[ CDomainMappingTable::ProcessSpecialLinks ]------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Processes all the special global links to handle things like local
|
|
// delivery.
|
|
// Parameters:
|
|
// IN cSpecialRetryMinutes minutes to retry currently unreachable
|
|
// if 0, will use previous value
|
|
// IN fRoutingLockHeld
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 1/26/99 - MikeSwa Created
|
|
// 3/25/99 - MikeSwa Added fRoutingLockHeld to fix GetNextMsgRef deadlock
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void CDomainMappingTable::ProcessSpecialLinks(DWORD cSpecialRetryMinutes,
|
|
BOOL fRoutingLockHeld)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CMsgRef *pmsgref = NULL;
|
|
CAQSvrInst *paqinst = m_paqinst;
|
|
BOOL fSchedRetry = FALSE;
|
|
BOOL fShutdownLock = FALSE;
|
|
|
|
//If this thread has the routing lock... we are safe from shutdown
|
|
_ASSERT(m_paqinst || !fRoutingLockHeld);
|
|
if (!paqinst)
|
|
return; //we must be shutting down
|
|
|
|
if (!fRoutingLockHeld)
|
|
{
|
|
//It is safe to access paqinst to acquire the shutdown lock because
|
|
//all threads that can call this function will be gone before the
|
|
//reference count on the server instance is 0 (however, this call
|
|
//could happen during deinialization which would cause m_aqinst
|
|
//to be NULL and the following call to return FALSE).
|
|
if (!paqinst->fTryShutdownLock())
|
|
return; //we are shutting down
|
|
|
|
//Now we have the shutdown lock... m_aqinst must be safe
|
|
_ASSERT(m_paqinst);
|
|
if (!m_paqinst)
|
|
{
|
|
//might as well be defensive in retail about this.
|
|
paqinst->ShutdownUnlock();
|
|
return;
|
|
}
|
|
fShutdownLock = TRUE;
|
|
}
|
|
|
|
if (!(DMT_FLAGS_SPECIAL_DELIVERY_SPINLOCK &
|
|
dwInterlockedSetBits(&m_dwFlags, DMT_FLAGS_SPECIAL_DELIVERY_SPINLOCK)))
|
|
{
|
|
//we have the lock... only 1 thread at a time should do this
|
|
|
|
//Loop over queue and enqueue for local delivery
|
|
while (SUCCEEDED(hr) && m_plmqLocal)
|
|
{
|
|
hr = m_plmqLocal->HrGetNextMsgRef(fRoutingLockHeld, &pmsgref);
|
|
if (FAILED(hr))
|
|
break;
|
|
|
|
_ASSERT(pmsgref);
|
|
m_paqinst->QueueMsgForLocalDelivery(pmsgref, TRUE);
|
|
pmsgref->Release();
|
|
pmsgref = NULL;
|
|
}
|
|
|
|
//Tell link to walk queues so they will be added to the empty list
|
|
//for deletion
|
|
if (m_plmqLocal)
|
|
{
|
|
m_plmqLocal->GenerateDSNsIfNecessary(TRUE /*check if empty */,
|
|
TRUE /*merge retry queue only*/);
|
|
}
|
|
|
|
//NDR all messages in the unreachable link
|
|
if (m_plmqUnreachable)
|
|
{
|
|
//We must hold the routing lock for DSN generation
|
|
if (!fRoutingLockHeld)
|
|
m_paqinst->RoutingShareLock();
|
|
|
|
m_plmqUnreachable->SetLastConnectionFailure(AQUEUE_E_NDR_ALL);
|
|
m_plmqUnreachable->GenerateDSNsIfNecessary(FALSE, FALSE);
|
|
|
|
if (!fRoutingLockHeld)
|
|
m_paqinst->RoutingShareUnlock();
|
|
|
|
}
|
|
|
|
//Process the currently unreachable link ... unless we are currently
|
|
//doing a ResetRoutes. If we are we must skip this because the
|
|
//currently unreachable link contains queues we should not process
|
|
//here - this function will be called again when ResetRoutes completes
|
|
if (m_plmqCurrentlyUnreachable &&
|
|
!(m_dwFlags & DMT_FLAGS_RESET_ROUTES_IN_PROGRESS))
|
|
{
|
|
if (cSpecialRetryMinutes)
|
|
{
|
|
//new time is sooner... we better ask for a retry
|
|
if (cSpecialRetryMinutes < m_cSpecialRetryMinutes)
|
|
fSchedRetry = TRUE;
|
|
|
|
InterlockedExchange((PLONG)&m_cSpecialRetryMinutes,
|
|
cSpecialRetryMinutes);
|
|
}
|
|
|
|
if (!(DMT_FLAGS_SPECIAL_DELIVERY_CALLBACK &
|
|
dwInterlockedSetBits(&m_dwFlags, DMT_FLAGS_SPECIAL_DELIVERY_CALLBACK)))
|
|
{
|
|
//We are the only thread doing this
|
|
|
|
//We must hold the routing lock for DSN generation
|
|
if (!fRoutingLockHeld)
|
|
m_paqinst->RoutingShareLock();
|
|
|
|
m_plmqCurrentlyUnreachable->GenerateDSNsIfNecessary(FALSE, FALSE);
|
|
dwInterlockedUnsetBits(&m_dwFlags, DMT_FLAGS_SPECIAL_DELIVERY_CALLBACK);
|
|
fSchedRetry = TRUE;
|
|
|
|
if (!fRoutingLockHeld)
|
|
m_paqinst->RoutingShareUnlock();
|
|
}
|
|
|
|
//periodically check the currently unreachable link... to genrate NDRs
|
|
if (fSchedRetry)
|
|
{
|
|
dwInterlockedSetBits(&m_dwFlags, DMT_FLAGS_SPECIAL_DELIVERY_CALLBACK);
|
|
if (m_paqinst)
|
|
{
|
|
hr = m_paqinst->SetCallbackTime(
|
|
CDomainMappingTable::SpecialRetryCallback, this,
|
|
m_cSpecialRetryMinutes);
|
|
if (FAILED(hr))
|
|
{
|
|
//Unmark bits to we try next time
|
|
dwInterlockedUnsetBits(&m_dwFlags,
|
|
DMT_FLAGS_SPECIAL_DELIVERY_CALLBACK);
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//Release the lock
|
|
dwInterlockedUnsetBits(&m_dwFlags, DMT_FLAGS_SPECIAL_DELIVERY_SPINLOCK);
|
|
}
|
|
|
|
if (fShutdownLock)
|
|
{
|
|
_ASSERT(m_paqinst);
|
|
m_paqinst->ShutdownUnlock();
|
|
}
|
|
}
|
|
|
|
|
|
//---[ CDomainMappingTable::HrPrepareForLocalDelivery ]------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Prepares a message for local delivery using the m_plmqLocal Link.
|
|
// Parameters:
|
|
// IN pmsgref MsgRef to prepare for delivery
|
|
// IN fLocal Prepare delivery for all domains with NULL queues
|
|
// IN fDelayDSN Check/Set Delay bitmap (only send 1 Delay DSN).
|
|
// IN pqlstQueues QuickList of DMQ's
|
|
// IN OUT pdcntxt context that must be returned on Ack
|
|
// OUT pcRecips # of recips to deliver for
|
|
// OUT prgdwRecips Array of recipient indexes
|
|
// Returns:
|
|
// S_OK on success
|
|
// E_FAIL if m_plmqLocal is not initialized
|
|
// History:
|
|
// 1/26/99 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CDomainMappingTable::HrPrepareForLocalDelivery(
|
|
IN CMsgRef *pmsgref,
|
|
IN BOOL fDelayDSN,
|
|
IN OUT CDeliveryContext *pdcntxt,
|
|
OUT DWORD *pcRecips,
|
|
OUT DWORD **prgdwRecips)
|
|
{
|
|
if (m_plmqLocal)
|
|
{
|
|
return m_plmqLocal->HrPrepareDelivery(pmsgref, TRUE, fDelayDSN,
|
|
pdcntxt, pcRecips, prgdwRecips);
|
|
}
|
|
else
|
|
return E_FAIL;
|
|
}
|
|
|
|
DWORD CDomainMappingTable::GetCurrentlyUnreachableTotalMsgCount() {
|
|
return m_plmqCurrentlyUnreachable->cGetTotalMsgCount();
|
|
}
|
|
|
|
//---[ CDomainMappingTable::plmqGetLocalLink ]---------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Returns an addref'd pointer to the local link object
|
|
// Parameters:
|
|
// -
|
|
// Returns:
|
|
// AddRef'd pointer to the local link object
|
|
// History:
|
|
// 2/22/99 - MikeSwa Created
|
|
// 1/28/2000 - MikeSwa Modified to make shutdown safe
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CLinkMsgQueue *CDomainMappingTable::plmqGetLocalLink()
|
|
{
|
|
CAQSvrInst *paqinst = m_paqinst;
|
|
CLinkMsgQueue *plmq = NULL;
|
|
|
|
//
|
|
// If paqinst is non-NULL, then we have the possibility of not being
|
|
// shutdown. If we are shutdown, it is possible that m_plmqLocal is
|
|
// in the process of being released (which would be a really bad
|
|
// time to addref/release it). We can safely access paqinst, since
|
|
// this class is a member of CAQSvrInst.
|
|
//
|
|
if (paqinst && paqinst->fTryShutdownLock())
|
|
{
|
|
plmq = m_plmqLocal;
|
|
if (plmq)
|
|
plmq->AddRef();
|
|
paqinst->ShutdownUnlock();
|
|
}
|
|
|
|
return plmq;
|
|
}
|
|
|
|
//---[ CDomainMappingTable::plmqGetCurrentlyUnreachable]---------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Returns an addref'd pointer to the currently unreachable link object
|
|
// Parameters:
|
|
// -
|
|
// Returns:
|
|
// AddRef'd pointer to the currently unreachable link object
|
|
// History:
|
|
// 6/21/99 - GPulla Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CLinkMsgQueue *CDomainMappingTable::plmqGetCurrentlyUnreachable()
|
|
{
|
|
if(m_plmqCurrentlyUnreachable)
|
|
m_plmqCurrentlyUnreachable->AddRef();
|
|
|
|
return m_plmqCurrentlyUnreachable;
|
|
}
|
|
|
|
//---[ CDomainMappingTable::pmmaqGetPreCategorized]---------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Returns an addref'd pointer to the precategorized queue object
|
|
// Parameters:
|
|
// -
|
|
// AddRef'd pointer to the precategorized link object.
|
|
// History:
|
|
// 6/21/99 - GPulla Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CMailMsgAdminLink *CDomainMappingTable::pmmaqGetPreCategorized()
|
|
{
|
|
if(m_pmmaqPreCategorized)
|
|
m_pmmaqPreCategorized->AddRef();
|
|
|
|
return m_pmmaqPreCategorized;
|
|
}
|
|
|
|
//---[ CDomainMappingTable::pmmaqGetPreRouting]---------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Returns an addref'd pointer to the pre routing queue object
|
|
// Parameters:
|
|
// -
|
|
// AddRef'd pointer to the precategorized link object.
|
|
// History:
|
|
// 6/21/99 - GPulla Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CMailMsgAdminLink *CDomainMappingTable::pmmaqGetPreRouting()
|
|
{
|
|
if(m_pmmaqPreRouting)
|
|
m_pmmaqPreRouting->AddRef();
|
|
|
|
return m_pmmaqPreRouting;
|
|
}
|
|
|
|
//---[ CDomainMappingTable::pmmaqGetPreRouting]---------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Returns an addref'd pointer to the pre routing queue object
|
|
// Parameters:
|
|
// -
|
|
// AddRef'd pointer to the precategorized link object.
|
|
// History:
|
|
// 6/21/99 - GPulla Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CMailMsgAdminLink *CDomainMappingTable::pmmaqGetPreSubmission()
|
|
{
|
|
if(m_pmmaqPreSubmission)
|
|
m_pmmaqPreSubmission->AddRef();
|
|
|
|
return m_pmmaqPreSubmission;
|
|
}
|
|
|
|
//---[ CDomainEntryIterator::CDomainEntryIterator ]----------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Constructor for CDomainEntryIterator
|
|
// Parameters:
|
|
// -
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 8/19/99 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CDomainEntryIterator::CDomainEntryIterator()
|
|
{
|
|
m_dwSignature = DOMAIN_ENTRY_ITERATOR_SIG;
|
|
m_cItems = 0;
|
|
m_iCurrentItem = 0;
|
|
m_rgpvItems = NULL;
|
|
}
|
|
|
|
//---[ CDomainEntryIterator::Recycle ]-----------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// CDomainEntryIterator Recycle... destroys using virtual functions
|
|
// Parameters:
|
|
// -
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 8/19/99 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
VOID CDomainEntryIterator::Recycle()
|
|
{
|
|
DWORD iCurrentItem = 0;
|
|
|
|
for (iCurrentItem = 0; iCurrentItem < m_cItems; iCurrentItem++)
|
|
{
|
|
_ASSERT(m_rgpvItems);
|
|
if (!m_rgpvItems)
|
|
break;
|
|
|
|
if (m_rgpvItems[iCurrentItem])
|
|
{
|
|
ReleaseItem(m_rgpvItems[iCurrentItem]);
|
|
m_rgpvItems[iCurrentItem] = NULL;
|
|
}
|
|
}
|
|
|
|
if (m_rgpvItems)
|
|
{
|
|
FreePv(m_rgpvItems);
|
|
m_rgpvItems = NULL;
|
|
}
|
|
m_cItems = 0;
|
|
m_iCurrentItem = 0;
|
|
}
|
|
|
|
//---[ CDomainEntryIterator::pvGetNext ]---------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Gets the next Item
|
|
// Parameters:
|
|
// -
|
|
// Returns:
|
|
// The next item in the iterator
|
|
// NULL if at last item
|
|
// History:
|
|
// 8/19/99 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
PVOID CDomainEntryIterator::pvGetNext()
|
|
{
|
|
PVOID pvRet = NULL;
|
|
if (m_rgpvItems && (m_iCurrentItem < m_cItems))
|
|
{
|
|
pvRet = m_rgpvItems[m_iCurrentItem];
|
|
_ASSERT(pvRet);
|
|
m_iCurrentItem++;
|
|
}
|
|
return pvRet;
|
|
}
|
|
|
|
//---[ CDomainEntryIterator::HrInitialize ]------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Initializes a CDomainEntryIterator from a given CDomainEntry
|
|
// Parameters:
|
|
// pdentry CDomainEntry to initialize from
|
|
// Returns:
|
|
// S_OK on success
|
|
// E_POINTER if pdentry is NULL
|
|
// E_INVALIDARG if HrInitialize has already be called for this iterator
|
|
// E_OUTOFMEMORY if we cannot allocate memory for iterator
|
|
// History:
|
|
// 8/20/99 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CDomainEntryIterator::HrInitialize(CDomainEntry *pdentry)
|
|
{
|
|
TraceFunctEnterEx((LPARAM) this, "CDomainEntryIterator::HrInitialize");
|
|
HRESULT hr = S_OK;
|
|
BOOL fEntryLocked = FALSE;
|
|
DWORD cAddedItems = 0;
|
|
DWORD cItems = 0;
|
|
PLIST_ENTRY pli = NULL;
|
|
PLIST_ENTRY pliHead = NULL;
|
|
CLinkMsgQueue *plmq = NULL;
|
|
|
|
_ASSERT(pdentry);
|
|
_ASSERT(!m_rgpvItems && "Iterator initialized twice");
|
|
|
|
if (!pdentry)
|
|
{
|
|
hr = E_POINTER;
|
|
ErrorTrace((LPARAM) this, "NULL pdentry used to initialized iterator");
|
|
goto Exit;
|
|
}
|
|
|
|
if (m_rgpvItems)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
ErrorTrace((LPARAM) this, "Iterator initialized twice!");
|
|
goto Exit;
|
|
}
|
|
|
|
pdentry->m_slPrivateData.ShareLock();
|
|
fEntryLocked = TRUE;
|
|
|
|
cItems = cItemsFromDomainEntry(pdentry);
|
|
if (!cItems) //Empty entry
|
|
goto Exit;
|
|
|
|
m_rgpvItems = (PVOID *) pvMalloc(sizeof(PVOID) * cItems);
|
|
|
|
if (!m_rgpvItems)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
ErrorTrace((LPARAM) this,
|
|
"Unable to allocate memory for iterator of size %d", cItems);
|
|
goto Exit;
|
|
}
|
|
|
|
ZeroMemory(m_rgpvItems, sizeof(PVOID)*cItems);
|
|
|
|
pliHead = pliHeadFromDomainEntry(pdentry);
|
|
_ASSERT(pliHead);
|
|
pli = pliHead->Flink;
|
|
|
|
while(pliHead != pli)
|
|
{
|
|
_ASSERT(pli);
|
|
m_rgpvItems[cAddedItems] = pvItemFromListEntry(pli);
|
|
_ASSERT(m_rgpvItems[cAddedItems]);
|
|
if (m_rgpvItems[cAddedItems])
|
|
cAddedItems++;
|
|
|
|
pli = pli->Flink;
|
|
_ASSERT(cAddedItems <= cItems); //We've run out of room
|
|
if (cAddedItems > cItems)
|
|
break;
|
|
}
|
|
_ASSERT(cAddedItems == cItems);
|
|
m_cItems = cAddedItems;
|
|
|
|
Exit:
|
|
if (fEntryLocked)
|
|
pdentry->m_slPrivateData.ShareUnlock();
|
|
|
|
TraceFunctLeave();
|
|
return hr;
|
|
}
|
|
|
|
//---[ CDomainEntryLinkIterator ]---------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Releases a CLinkMsgQueue during the CDomainEntryQueueIterator
|
|
// destructor
|
|
// Parameters:
|
|
// pvItem LMQ to release
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 8/19/99 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
VOID CDomainEntryLinkIterator::ReleaseItem(PVOID pvItem)
|
|
{
|
|
CLinkMsgQueue *plmq = (CLinkMsgQueue *) pvItem;
|
|
_ASSERT(plmq);
|
|
plmq->Release();
|
|
}
|
|
|
|
//---[ CDomainEntryLinkIterator::pvItemFromListEntry ]-------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Returns a CLinkMsgQueue item from the given LIST_ENTRY
|
|
// Parameters:
|
|
// pli LIST_ENTRY to get LMQ from... *must* be non-NULL
|
|
// Returns:
|
|
// PVOID for Addref'd CLinkMsgQueue
|
|
// History:
|
|
// 8/20/99 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
PVOID CDomainEntryLinkIterator::pvItemFromListEntry(PLIST_ENTRY pli)
|
|
{
|
|
CLinkMsgQueue *plmq = CLinkMsgQueue::plmqGetLinkMsgQueue(pli);
|
|
_ASSERT(plmq);
|
|
_ASSERT(pli);
|
|
|
|
plmq->AddRef();
|
|
return plmq;
|
|
}
|
|
|
|
//---[ CDomainEntryLinkIterator::plmqGetNextLinkMsgQueue ]---------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Gets the next LMQ for this iterator
|
|
// Parameters:
|
|
// plmq to release... addref'd by previous call
|
|
// Returns:
|
|
// Addref'd Next LMQ.
|
|
// NULL if no more left
|
|
// History:
|
|
// 8/19/99 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CLinkMsgQueue *CDomainEntryLinkIterator::plmqGetNextLinkMsgQueue(
|
|
CLinkMsgQueue *plmq)
|
|
{
|
|
CLinkMsgQueue *plmqNext = (CLinkMsgQueue *) pvGetNext();
|
|
|
|
if (plmqNext)
|
|
plmqNext->AddRef();
|
|
|
|
if (plmq)
|
|
plmq->Release();
|
|
|
|
return plmqNext;
|
|
}
|
|
|
|
//---[ CDomainEntryQueueIterator::pdmqGetNextDestMsgQueue ]--------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Get the next DMQ in the iterator
|
|
// Parameters:
|
|
// pdmq CDestMsgQueue to release... addref'd by previous call
|
|
// Returns:
|
|
// Addref'd next DMQ
|
|
// NULL if no more DMQ's for iterator
|
|
// History:
|
|
// 8/19/99 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CDestMsgQueue *CDomainEntryQueueIterator::pdmqGetNextDestMsgQueue(
|
|
CDestMsgQueue *pdmq)
|
|
{
|
|
CDestMsgQueue *pdmqNext = (CDestMsgQueue *) pvGetNext();
|
|
|
|
if (pdmqNext)
|
|
{
|
|
pdmqNext->AssertSignature();
|
|
pdmqNext->AddRef();
|
|
}
|
|
|
|
if (pdmq)
|
|
{
|
|
pdmq->AssertSignature();
|
|
pdmq->Release();
|
|
}
|
|
|
|
return pdmqNext;
|
|
}
|
|
|
|
//---[ CDomainEntryDestIterator::pvItemFromListEntry ]-------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Returns a CDestMsgQueue item from the given LIST_ENTRY
|
|
// Parameters:
|
|
// pli LIST_ENTRY to get DMQ from... *must* be non-NULL
|
|
// Returns:
|
|
// PVOID for Addref'd CDestMsgQueue
|
|
// History:
|
|
// 8/20/99 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
PVOID CDomainEntryQueueIterator::pvItemFromListEntry(PLIST_ENTRY pli)
|
|
{
|
|
CDestMsgQueue *pdmq = CDestMsgQueue::pdmqGetDMQFromDomainListEntry(pli);
|
|
_ASSERT(pdmq);
|
|
_ASSERT(pli);
|
|
|
|
pdmq->AssertSignature();
|
|
pdmq->AddRef();
|
|
return (PVOID) pdmq;
|
|
}
|
|
|
|
//---[ CDomainEntryQueueIterator ]---------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Releases a CDestMsgQueue during the CDomainEntryQueueIterator
|
|
// destructor
|
|
// Parameters:
|
|
// pvItem DMQ to release
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 8/19/99 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
VOID CDomainEntryQueueIterator::ReleaseItem(PVOID pvItem)
|
|
{
|
|
CDestMsgQueue *pdmq = (CDestMsgQueue *) pvItem;
|
|
_ASSERT(pdmq);
|
|
pdmq->AssertSignature();
|
|
pdmq->Release();
|
|
}
|
|
|