//----------------------------------------------------------------------------- // // // File: DomCfg.cpp // // Description: Implementation of CDomainConfigTable and CInternalDomainInfo // // Author: mikeswa // // Copyright (C) 1998 Microsoft Corporation // //----------------------------------------------------------------------------- #include "aqprecmp.h" #include #include "domcfg.h" #include "aqutil.h" //Encourage compiler to include symbols for enums eDomainInfoFlags g_eDomainInfo = DOMAIN_INFO_USE_SSL; eIntDomainInfoFlags g_eIntDomainInfo = INT_DOMAIN_INFO_INVALID; //---[ RemoveConfigEntryIteratorFn ]-------------------------------------------- // // // 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 RemoveConfigEntryIteratorFn(PVOID pvContext, PVOID pvData, BOOL fWildcard, BOOL *pfContinue, BOOL *pfDelete) { CInternalDomainInfo *pIntDomainInfo = (CInternalDomainInfo *) pvData; *pfDelete = TRUE; *pfContinue = TRUE; _ASSERT(INT_DOMAIN_INFO_SIG == pIntDomainInfo->m_dwSignature); pIntDomainInfo->m_dwIntDomainInfoFlags |= INT_DOMAIN_INFO_INVALID; pIntDomainInfo->Release(); } //---[ RemoveOutdatedConfigEntryIteratorFn ]------------------------------------ // // // Description: // Deletes and releases all outdated internal domain info objects in table. // An internal domain entry is considered outdated if its version number // does not match the global version number. // Parameters: // IN pvContext - pointer to context (current version number) // 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: // 9/29/98 - MikeSwa Created // //----------------------------------------------------------------------------- VOID RemoveOutdatedConfigEntryIteratorFn(PVOID pvContext, PVOID pvData, BOOL fWildcard, BOOL *pfContinue, BOOL *pfDelete) { CInternalDomainInfo *pIntDomainInfo = (CInternalDomainInfo *) pvData; _ASSERT(INT_DOMAIN_INFO_SIG == pIntDomainInfo->m_dwSignature); _ASSERT(pvContext); *pfContinue = TRUE; if (pIntDomainInfo->m_dwVersion != *((DWORD *)pvContext)) { //make sure it is not the root entry if ((1*sizeof(CHAR) == pIntDomainInfo->m_DomainInfo.cbDomainNameLength) && ('*' == pIntDomainInfo->m_DomainInfo.szDomainName[0]) && ('\0' == pIntDomainInfo->m_DomainInfo.szDomainName[1])) { // //NOTE - It is not expected for this to happen. We expect //SMTP to always provide a default entry. If SMTP changes such //that does not, then dynamic updates would fail, because we never //mark the default entry that we create as invalid. Once a link //picks up this entry, it will never let it go. // _ASSERT(0 && "SMTP did not supply a * entry"); //It is the root entry... update verision number //NOTE: The Root entry is a special case... it is required to be //present at all times, and is not always inserted into the //metabase. Because of this, it's version number may be incorrect, //so we need to make sure that we don't remove it. *pfDelete = FALSE; pIntDomainInfo->m_dwVersion = *((DWORD *)pvContext); } else { //The entry is old *pfDelete = TRUE; pIntDomainInfo->m_dwIntDomainInfoFlags |= INT_DOMAIN_INFO_INVALID; pIntDomainInfo->Release(); } } else { *pfDelete = FALSE; } } //---[ HrCopyStringProperty ]-------------------------------------------------- // // // Description: // Private helper function that is used to allocate and copy string // properties in the DomainInfo struct // Parameters: // OUT (ref) szDest Dest string to allocate and copy to // IN szSource Source string to copy // IN cbLength Length of string (without ending '\0' // Returns: // S_OK on success // //----------------------------------------------------------------------------- HRESULT inline HrCopyStringProperty(LPSTR &szDest, const LPSTR szSource, DWORD cbLength) { HRESULT hr = S_OK; _ASSERT(szSource); //must have a buffer to copy _ASSERT(!szDest); szDest = (LPSTR) pvMalloc(cbLength + sizeof(CHAR)); if (!szDest) { hr = E_OUTOFMEMORY; goto Exit; } //copy data memcpy(szDest, szSource, cbLength); //copy string szDest[cbLength] = '\0'; Exit: return hr; } //---[ COPY_DOMAIN_INFO_STRING_PROP ]------------------------------------------ // // // Description: // Macro used to call private helper function HrCopyStringProperty // Parameters: // pOldDomainInfo DomainInfo Struct to copy // pNewDomainInfo New DomainInfo stuct to copy to // cbProp Name of size property // szProp Name of string proptery // Returns: // // //----------------------------------------------------------------------------- #define COPY_DOMAIN_INFO_STRING_PROP(pOldDomainInfo, pNewDomainInfo, cbProp, szProp) \ { \ _ASSERT(!(pNewDomainInfo)->cbProp); \ _ASSERT(!(pNewDomainInfo)->szProp); \ (pNewDomainInfo)->cbProp = (pOldDomainInfo)->cbProp; \ if ((pOldDomainInfo)->cbProp) \ hr = HrCopyStringProperty((pNewDomainInfo)->szProp, \ (pOldDomainInfo)->szProp, (pOldDomainInfo)->cbProp); \ } //---[ CInternalDomainInfo::CInternalDomainInfo ]------------------------------ // // // Description: // Object constructor. Initializes DomainInfo struct // Parameters: // DWORD dwVersion Current DomainConfigTable version // Returns: // - // History: // 9/29/98 - MikeSwa - added version // //----------------------------------------------------------------------------- CInternalDomainInfo::CInternalDomainInfo(DWORD dwVersion) { ZeroMemory(&m_DomainInfo.dwDomainInfoFlags, sizeof(DomainInfo) - sizeof(DWORD)); m_dwIntDomainInfoFlags = INT_DOMAIN_INFO_OK; m_dwSignature = INT_DOMAIN_INFO_SIG; m_dwVersion = dwVersion; //make sure our assumptions about the struct of DomainInfo are valid _ASSERT(1 == ((DWORD *) &m_DomainInfo.dwDomainInfoFlags) - ((DWORD *) &m_DomainInfo)); m_DomainInfo.cbVersion = sizeof(DomainInfo); } //---[ CInternalDomainInfo::~CInternalDomainInfo ]----------------------------- // // // Description: // Class destructor... deallocates any memory associated with the // DomainInfo struct // Parameters: // - // Returns: // - // //----------------------------------------------------------------------------- CInternalDomainInfo::~CInternalDomainInfo() { _ASSERT(m_DomainInfo.cbVersion == sizeof(DomainInfo)); if (m_DomainInfo.szDomainName) FreePv(m_DomainInfo.szDomainName); if (m_DomainInfo.szETRNDomainName) FreePv(m_DomainInfo.szETRNDomainName); if (m_DomainInfo.szSmartHostDomainName) FreePv(m_DomainInfo.szSmartHostDomainName); if (m_DomainInfo.szDropDirectory) FreePv(m_DomainInfo.szDropDirectory); if (m_DomainInfo.szAuthType) FreePv(m_DomainInfo.szAuthType); if (m_DomainInfo.szUserName) FreePv(m_DomainInfo.szUserName); if (m_DomainInfo.szPassword) FreePv(m_DomainInfo.szPassword); if (m_DomainInfo.pvBlob) FreePv(m_DomainInfo.pvBlob); } //---[ CInternalDomainInfo::HrInit ]------------------------------------------- // // // Description: // Clones a DomainInfo struct and allocates needed memory // Parameters: // IN pDomainInfo DomainInfo struct to copy // Returns: // S_OK on success // E_OUTOFMEMORY if allocations fail //----------------------------------------------------------------------------- HRESULT CInternalDomainInfo::HrInit(DomainInfo *pDomainInfo) { HRESULT hr = S_OK; m_DomainInfo.dwDomainInfoFlags = pDomainInfo->dwDomainInfoFlags; COPY_DOMAIN_INFO_STRING_PROP(pDomainInfo, &m_DomainInfo, cbDomainNameLength, szDomainName); if (FAILED(hr)) goto Exit; COPY_DOMAIN_INFO_STRING_PROP(pDomainInfo, &m_DomainInfo, cbETRNDomainNameLength, szETRNDomainName); if (FAILED(hr)) goto Exit; COPY_DOMAIN_INFO_STRING_PROP(pDomainInfo, &m_DomainInfo, cbSmartHostDomainNameLength, szSmartHostDomainName); if (FAILED(hr)) goto Exit; COPY_DOMAIN_INFO_STRING_PROP(pDomainInfo, &m_DomainInfo, cbDropDirectoryLength, szDropDirectory); if (FAILED(hr)) goto Exit; COPY_DOMAIN_INFO_STRING_PROP(pDomainInfo, &m_DomainInfo, cbAuthTypeLength, szAuthType); if (FAILED(hr)) goto Exit; COPY_DOMAIN_INFO_STRING_PROP(pDomainInfo, &m_DomainInfo, cbUserNameLength, szUserName); if (FAILED(hr)) goto Exit; COPY_DOMAIN_INFO_STRING_PROP(pDomainInfo, &m_DomainInfo, cbPasswordLength, szPassword); if (FAILED(hr)) goto Exit; m_DomainInfo.cEtrnDelayTime = pDomainInfo->cEtrnDelayTime; //Copy the blob if (pDomainInfo->cbBlob) { _ASSERT(pDomainInfo->pvBlob); m_DomainInfo.pvBlob = (DWORD *) pvMalloc(pDomainInfo->cbBlob); if (!m_DomainInfo.pvBlob) { hr = E_OUTOFMEMORY; goto Exit; } //copy data memcpy(m_DomainInfo.pvBlob, pDomainInfo->pvBlob, pDomainInfo->cbBlob); m_DomainInfo.cbBlob = pDomainInfo->cbBlob; } Exit: return hr; } //---[ CDomainConfigTable::CDomainConfigTable ]-------------------------------- // // // Description: // Constuctor for CDomainConfig Table // Parameters: // - // Returns: // - // //----------------------------------------------------------------------------- CDomainConfigTable::CDomainConfigTable() : m_slPrivateData("CDomainConfigTable") { m_dwFlags = 0; m_dwSignature = DOMAIN_CONFIG_SIG; m_dwCurrentConfigVersion = 0; m_pLastStarDomainInfo = NULL; m_pDefaultDomainConfig = NULL; } //---[ CDomainConfigTable::~CDomainConfigTable ]------------------------------- // // // Description: // Destructor for CDomainConfig Table // Parameters: // - // Returns: // - // //----------------------------------------------------------------------------- CDomainConfigTable::~CDomainConfigTable() { TraceFunctEnterEx((LPARAM) this, "CDomainConfigTable::~CDomainConfigTable"); HRESULT hr = S_OK; //Delete all entries from the table if (DOMCFG_DOMAIN_NAME_TABLE_INIT & m_dwFlags) { //iterate over all domains and delete entries in hash table m_dnt.HrIterateOverSubDomains(NULL, RemoveConfigEntryIteratorFn, NULL); if (m_pDefaultDomainConfig != NULL) { m_pDefaultDomainConfig->Release(); m_pDefaultDomainConfig = NULL; } m_dwFlags ^= DOMCFG_DOMAIN_NAME_TABLE_INIT; } TraceFunctLeave(); } //---[ CDomainConfigTable::HrInit ]-------------------------------------------- // // // Description: // Initializes DOMAIN_NAME_TABLE hash table // Parameters: // - // Returns: // S_OK on success // //----------------------------------------------------------------------------- HRESULT CDomainConfigTable::HrInit() { TraceFunctEnterEx((LPARAM) this, "CDomainConfigTable::HrInit"); HRESULT hr = S_OK; CInternalDomainInfo *pIntDomainInfoDefault = NULL; DOMAIN_STRING strDomain; hr = m_dnt.HrInit(); if (FAILED(hr)) goto Exit; m_dwFlags |= DOMCFG_DOMAIN_NAME_TABLE_INIT; //Part 1 // //Allocate an InternalDomainInfo for m_pDefaultDomainConfig. This //will be used for links/connections for which a routing sink has returned //no connector m_pDefaultDomainConfig = new CInternalDomainInfo(m_dwCurrentConfigVersion); if (!m_pDefaultDomainConfig) { hr = E_OUTOFMEMORY; goto Exit; } m_pDefaultDomainConfig->m_DomainInfo.cbDomainNameLength = 1; m_pDefaultDomainConfig->m_DomainInfo.szDomainName = (LPSTR) pvMalloc(2*sizeof(CHAR)); if (!m_pDefaultDomainConfig->m_DomainInfo.szDomainName) { hr = E_OUTOFMEMORY; goto Exit; } memcpy(m_pDefaultDomainConfig->m_DomainInfo.szDomainName, "*", 2); //Part 2 // //Create default "*" entry in the DCT so that arbitrary domains will have //something to match against. Note that this entry is separate from the //m_pDefaultDomainConfig entry that is *not* part of the DCT, and is used //for links/connections that are *not* supposed to match anything in the DCT pIntDomainInfoDefault = new CInternalDomainInfo(m_dwCurrentConfigVersion); if (!pIntDomainInfoDefault) { hr = E_OUTOFMEMORY; goto Exit; } pIntDomainInfoDefault->m_DomainInfo.cbDomainNameLength = 1; pIntDomainInfoDefault->m_DomainInfo.szDomainName = (LPSTR) pvMalloc(2*sizeof(CHAR)); if (!pIntDomainInfoDefault->m_DomainInfo.szDomainName) { hr = E_OUTOFMEMORY; goto Exit; } memcpy(pIntDomainInfoDefault->m_DomainInfo.szDomainName, "*", 2); strDomain.Length = (USHORT) pIntDomainInfoDefault->m_DomainInfo.cbDomainNameLength; strDomain.MaximumLength = strDomain.Length; strDomain.Buffer = pIntDomainInfoDefault->m_DomainInfo.szDomainName; hr = m_dnt.HrInsertDomainName(&strDomain, (PVOID) pIntDomainInfoDefault); if (FAILED(hr)) goto Exit; Exit: TraceFunctLeave(); return hr; } //---[ CDomainConfigTable::HrSetInternalDomainInfo ]--------------------------- // // // Description: // Inserts InternalDomainInfo into hash table based on szDomain // Parameters: // IN pIntDomainInfo Internal DomainInfo to insert // Returns: // S_OK on success // //----------------------------------------------------------------------------- HRESULT CDomainConfigTable::HrSetInternalDomainInfo( IN CInternalDomainInfo *pIntDomainInfo) { TraceFunctEnterEx((LPARAM) this, "CDomainConfigTable::SetInternalDomainInfo"); HRESULT hr = S_OK; DOMAIN_STRING strDomain; CInternalDomainInfo *pIntDomainInfoCurrent = NULL; _ASSERT(pIntDomainInfo); _ASSERT(pIntDomainInfo->m_DomainInfo.szDomainName); _ASSERT(pIntDomainInfo->m_DomainInfo.cbDomainNameLength); _ASSERT(pIntDomainInfo->m_dwVersion == m_dwCurrentConfigVersion); strDomain.Length = (USHORT) pIntDomainInfo->m_DomainInfo.cbDomainNameLength; strDomain.MaximumLength = (USHORT) pIntDomainInfo->m_DomainInfo.cbDomainNameLength; strDomain.Buffer = pIntDomainInfo->m_DomainInfo.szDomainName; pIntDomainInfo->AddRef(); //Get Lock on table m_slPrivateData.ExclusiveLock(); //HACK ALERT // //SMTP calls this routine with info from one of two places: //1. For each domain configured in the /smtpsvc/1/domains container in the // metabase. In PT, these entries are populated from address spaces on // connectors. //2. For outbound-security configured at the /smtpsvc/1 (ie, at the VS) // level, it creates a "dummy" entry for the "*" domain. // //Unfortunately, this creates a problem if you have a connector with the // * address space, because the insertion in item 2 will overwrite the // insertion for * in item 1. //To handle this case, we keep the "last" * entry received in the // m_pLastStarDomainInfo. If we receive a second entry, then we know the // m_pLastStarDomainInfo is a domain entry from item 1. Otherwise, it // is the default config info. // // //If this is the "*" domain, then store it in m_pLastStarDomainInfo, until //we decide whether this is really the star domain info from item 1 or //the info from item 2 // if (pIntDomainInfo->m_DomainInfo.cbDomainNameLength == 1 && pIntDomainInfo->m_DomainInfo.szDomainName[0] == '*') { if (m_pLastStarDomainInfo == NULL) { m_pLastStarDomainInfo = pIntDomainInfo; hr = S_OK; goto Exit; } else { // This is the second * domain entry we have seen. Insert the // previous entry into the DMT, and keep this one as the last star // entry seen. pIntDomainInfoCurrent = pIntDomainInfo; pIntDomainInfo = m_pLastStarDomainInfo; m_pLastStarDomainInfo = pIntDomainInfoCurrent; pIntDomainInfoCurrent = NULL; dwInterlockedSetBits(&m_dwFlags, DOMCFG_MULTIPLE_STAR_DOMAINS); } } hr = m_dnt.HrInsertDomainName(&strDomain, (PVOID) pIntDomainInfo); if (FAILED(hr)) { if (DOMHASH_E_DOMAIN_EXISTS == hr) { //someone already inserted for this domain hr = m_dnt.HrRemoveDomainName(&strDomain, (PVOID *) &pIntDomainInfoCurrent); _ASSERT(DOMHASH_E_NO_SUCH_DOMAIN != hr); //someone violated write-lock if (FAILED(hr)) goto Exit; _ASSERT(pIntDomainInfoCurrent); //Mark old info as invalid & release pIntDomainInfoCurrent->m_dwIntDomainInfoFlags |= INT_DOMAIN_INFO_INVALID; _ASSERT(pIntDomainInfoCurrent->m_dwVersion <= pIntDomainInfo->m_dwVersion); pIntDomainInfoCurrent->Release(); pIntDomainInfoCurrent = NULL; hr = m_dnt.HrInsertDomainName(&strDomain, (PVOID) pIntDomainInfo); _ASSERT(DOMHASH_E_DOMAIN_EXISTS != hr); //someone violated write-lock if (FAILED(hr)) goto Exit; } else { goto Exit; } } Exit: m_slPrivateData.ExclusiveUnlock(); TraceFunctLeave(); return hr; } //---[ CDomainConfigTable::HrGetInternalDomainInfo ]--------------------------- // // // Description: // Gets internal Domaininfo stuct from hash table. Will use wildcard // matching // Parameters: // IN cbDomainnameLength Length of string to search for // IN szDomainName Domain Name to search for // OUT ppIntDomainInfo Domain info returned (must be released) // Returns: // S_OK if match is found // AQUEUE_E_INVALID_DOMAIN if no match is found // //----------------------------------------------------------------------------- HRESULT CDomainConfigTable::HrGetInternalDomainInfo( IN DWORD cbDomainNameLength, IN LPSTR szDomainName, OUT CInternalDomainInfo **ppIntDomainInfo) { TraceFunctEnterEx((LPARAM) this, "CDomainConfigTable::GetInternalDomainInfo"); HRESULT hr = S_OK; DOMAIN_STRING strDomain; _ASSERT(cbDomainNameLength); _ASSERT(szDomainName); _ASSERT(ppIntDomainInfo); strDomain.Length = (USHORT) cbDomainNameLength; strDomain.MaximumLength = (USHORT) cbDomainNameLength; strDomain.Buffer = szDomainName; m_slPrivateData.ShareLock(); //Use wildcard lookup hr = m_dnt.HrFindDomainName(&strDomain, (PVOID *) ppIntDomainInfo, FALSE); if (FAILED(hr)) { //It should at least match the default domain _ASSERT(DOMHASH_E_NO_SUCH_DOMAIN != hr); goto Exit; } _ASSERT(*ppIntDomainInfo); (*ppIntDomainInfo)->AddRef(); Exit: m_slPrivateData.ShareUnlock(); TraceFunctLeave(); return hr; } //---[ CDomainConfigTable::HrGetDefaultDomainInfo ]--------------------------- // // // Description: // Gets internal Default Domaininfo stuct. // Parameters: // OUT ppIntDomainInfo Domain info returned (must be released) // Returns: // S_OK if successful // AQUEUE_E_INVALID_DOMAIN if error // //----------------------------------------------------------------------------- HRESULT CDomainConfigTable::HrGetDefaultDomainInfo( OUT CInternalDomainInfo **ppIntDomainInfo) { TraceFunctEnterEx((LPARAM) this, "CDomainConfigTable::GetDefaultDomainInfo"); HRESULT hr = S_OK; *ppIntDomainInfo = NULL; m_slPrivateData.ShareLock(); if (m_pDefaultDomainConfig != NULL) { *ppIntDomainInfo = m_pDefaultDomainConfig; } else { hr = AQUEUE_E_INVALID_DOMAIN; goto Exit; } _ASSERT(*ppIntDomainInfo); (*ppIntDomainInfo)->AddRef(); Exit: m_slPrivateData.ShareUnlock(); TraceFunctLeave(); return hr; } //---[ CDomainConfigTable::StartConfigUpdate ]--------------------------------- // // // Description: // Signals that the metabase has been updated, and we will now begin to // get updated information. Increments an internal version number that // will be used to removed old, outdated entries once all of the config // info has been updated. // Parameters: // - // Returns: // - // History: // 9/29/98 - MikeSwa Created // //----------------------------------------------------------------------------- void CDomainConfigTable::StartConfigUpdate() { InterlockedIncrement((PLONG) &m_dwCurrentConfigVersion); _ASSERT(!(m_dwFlags & DOMCFG_FINISH_UPDATE_PENDING)); _ASSERT(m_pLastStarDomainInfo == NULL); dwInterlockedSetBits(&m_dwFlags, DOMCFG_FINISH_UPDATE_PENDING); dwInterlockedUnsetBits(&m_dwFlags, DOMCFG_MULTIPLE_STAR_DOMAINS); } //---[ CDomainConfigTable::FinishConfigUpdate ]-------------------------------- // // // Description: // Used to signal when all updated configuration information has been // passed in. Will then walk all of our cached configuration information // and remove outdated entries // Parameters: // - // Returns: // - // History: // 9/29/98 - MikeSwa Created // //----------------------------------------------------------------------------- void CDomainConfigTable::FinishConfigUpdate() { HRESULT hr = S_OK; DWORD dwCurrentVersion = m_dwCurrentConfigVersion; CInternalDomainInfo *pIntDomainInfo = NULL; //There should be a matching start _ASSERT(m_dwFlags & DOMCFG_FINISH_UPDATE_PENDING); // // Get the last "*" domain in case we need to explicitly update the table // with it. // m_slPrivateData.ShareLock(); pIntDomainInfo = m_pLastStarDomainInfo; if (pIntDomainInfo) pIntDomainInfo->AddRef(); m_slPrivateData.ShareUnlock(); // // If only 1 "*" domain has been configured, then we have not inserted // it in the DNT, and the actual data there is out of date. We // need to insert it into the table. // if (pIntDomainInfo && !(DOMCFG_MULTIPLE_STAR_DOMAINS & m_dwFlags)) { HrSetInternalDomainInfo(pIntDomainInfo); } // // Release the internal domain info if we got it. // if (pIntDomainInfo) pIntDomainInfo->Release(); dwInterlockedUnsetBits(&m_dwFlags, DOMCFG_FINISH_UPDATE_PENDING); //Lock table, remove outdated entries, and update the default domain config m_slPrivateData.ExclusiveLock(); if (m_pLastStarDomainInfo) { m_pDefaultDomainConfig->m_dwIntDomainInfoFlags |= INT_DOMAIN_INFO_INVALID; m_pDefaultDomainConfig->Release(); m_pDefaultDomainConfig = m_pLastStarDomainInfo; m_pLastStarDomainInfo = NULL; } hr = m_dnt.HrIterateOverSubDomains(NULL, RemoveOutdatedConfigEntryIteratorFn, &dwCurrentVersion); m_slPrivateData.ExclusiveUnlock(); if (FAILED(hr)) _ASSERT(DOMHASH_E_NO_SUCH_DOMAIN == hr); }