//+------------------------------------------------------------ // // Copyright (C) 1999, Microsoft Corporation // // File: cnfgmgr.cpp // // Contents: Implementation of the classes defined in cnfgmgr.h // // Classes: // CLdapCfgMgr // CLdapCfg // CLdapHost // CCfgConnectionCache // CCfgConnection // // Functions: // // History: // jstamerj 1999/06/16 14:41:45: Created. // //------------------------------------------------------------- #include "precomp.h" #include "cnfgmgr.h" // // Globals // CExShareLock CLdapServerCfg::m_listlock; LIST_ENTRY CLdapServerCfg::m_listhead; DWORD CLdapServerCfg::m_dwCostConnectedLocal = DEFAULT_COST_CONNECTED_LOCAL; DWORD CLdapServerCfg::m_dwCostConnectedRemote = DEFAULT_COST_CONNECTED_REMOTE; DWORD CLdapServerCfg::m_dwCostInitialLocal = DEFAULT_COST_INITIAL_LOCAL; DWORD CLdapServerCfg::m_dwCostInitialRemote = DEFAULT_COST_INITIAL_REMOTE; DWORD CLdapServerCfg::m_dwCostRetryLocal = DEFAULT_COST_RETRY_LOCAL; DWORD CLdapServerCfg::m_dwCostRetryRemote = DEFAULT_COST_RETRY_REMOTE; //+------------------------------------------------------------ // // Function: CLdapCfgMgr::CLdapCfgMgr // // Synopsis: Initialize member data // // Arguments: Optional: // fAutomaticConfigUpdate: TRUE indicates that the object is to // periodicly automaticly update the list of // GCs. // FALSE disables this functionality // // bt: Default bindtype to use // pszAccount: Default account for LDAP bind // pszPassword: Password of above account // pszNamingContext: Naming context to use for all LDAP searches // // Returns: NOTHING // // History: // jstamerj 1999/06/16 14:42:39: Created. // //------------------------------------------------------------- CLdapCfgMgr::CLdapCfgMgr( ISMTPServerEx *pISMTPServerEx, BOOL fAutomaticConfigUpdate, ICategorizerParameters *pICatParams, LDAP_BIND_TYPE bt, LPSTR pszAccount, LPSTR pszPassword, LPSTR pszNamingContext) : m_LdapConnectionCache(pISMTPServerEx) { CatFunctEnterEx((LPARAM)this, "CLdapCfgMgr::CLdapCfgMgr"); m_dwSignature = SIGNATURE_CLDAPCFGMGR; m_pCLdapCfg = NULL; ZeroMemory(&m_ulLastUpdateTime, sizeof(m_ulLastUpdateTime)); m_dwUpdateInProgress = FALSE; m_fAutomaticConfigUpdate = fAutomaticConfigUpdate; m_pISMTPServerEx = pISMTPServerEx; if(m_pISMTPServerEx) m_pISMTPServerEx->AddRef(); // // Copy default // m_bt = bt; if(pszAccount) lstrcpyn(m_szAccount, pszAccount, sizeof(m_szAccount)); else m_szAccount[0] = '\0'; if(pszPassword) lstrcpyn(m_szPassword, pszPassword, sizeof(m_szPassword)); else m_szPassword[0] = '\0'; if(pszNamingContext) lstrcpyn(m_szNamingContext, pszNamingContext, sizeof(m_szNamingContext)); else m_szNamingContext[0] = '\0'; m_pICatParams = pICatParams; m_pICatParams->AddRef(); m_LdapConnectionCache.AddRef(); m_dwRebuildGCListMaxInterval = DEFAULT_REBUILD_GC_LIST_MAX_INTERVAL; m_dwRebuildGCListMaxFailures = DEFAULT_REBUILD_GC_LIST_MAX_FAILURES; m_dwRebuildGCListMinInterval = DEFAULT_REBUILD_GC_LIST_MIN_INTERVAL; InitializeFromRegistry(); CatFunctLeaveEx((LPARAM)this); } // CLdapCfgMgr::CLdapCfgMgr //+---------------------------------------------------------------------------- // // Function: CLdapCfgMgr::InitializeFromRegistry // // Synopsis: Helper function that looks up parameters from the registry. // Configurable parameters are: // REBUILD_GC_LIST_MAX_INTERVAL_VALUE // REBUILD_GC_LIST_MAX_FAILURES_VALUE // REBUILD_GC_LIST_MIN_INTERVAL_VALUE // // Arguments: None // // Returns: Nothing. // //----------------------------------------------------------------------------- VOID CLdapCfgMgr::InitializeFromRegistry() { HKEY hkey; DWORD dwErr, dwType, dwValue, cbValue; dwErr = RegOpenKey(HKEY_LOCAL_MACHINE, REBUILD_GC_LIST_PARAMETERS_KEY, &hkey); if (dwErr == ERROR_SUCCESS) { cbValue = sizeof(dwValue); dwErr = RegQueryValueEx( hkey, REBUILD_GC_LIST_MAX_INTERVAL_VALUE, NULL, &dwType, (LPBYTE) &dwValue, &cbValue); if (dwErr == ERROR_SUCCESS && dwType == REG_DWORD && dwValue > 0) { m_dwRebuildGCListMaxInterval = dwValue; } cbValue = sizeof(dwValue); dwErr = RegQueryValueEx( hkey, REBUILD_GC_LIST_MAX_FAILURES_VALUE, NULL, &dwType, (LPBYTE) &dwValue, &cbValue); if (dwErr == ERROR_SUCCESS && dwType == REG_DWORD && dwValue > 0) { m_dwRebuildGCListMaxFailures = dwValue; } cbValue = sizeof(dwValue); dwErr = RegQueryValueEx( hkey, REBUILD_GC_LIST_MIN_INTERVAL_VALUE, NULL, &dwType, (LPBYTE) &dwValue, &cbValue); if (dwErr == ERROR_SUCCESS && dwType == REG_DWORD && dwValue > 0) { m_dwRebuildGCListMinInterval = dwValue; } RegCloseKey( hkey ); } } //+------------------------------------------------------------ // // Function: CLdapCfgMgr::~CLdapCfgMgr // // Synopsis: Release member data/pointers // // Arguments: NONE // // Returns: NOTHING // // History: // jstamerj 1999/06/16 14:44:28: Created. // //------------------------------------------------------------- CLdapCfgMgr::~CLdapCfgMgr() { CatFunctEnterEx((LPARAM)this, "CLdapCfgMgr::~CLdapCfgMgr"); if(m_pCLdapCfg) { // // Release it // m_pCLdapCfg->Release(); m_pCLdapCfg = NULL; } if(m_pICatParams) { m_pICatParams->Release(); m_pICatParams = NULL; } // // This will not return until all ldap connections have been released/destroyed // m_LdapConnectionCache.Release(); if(m_pISMTPServerEx) m_pISMTPServerEx->Release(); _ASSERT(m_dwSignature == SIGNATURE_CLDAPCFGMGR); m_dwSignature = SIGNATURE_CLDAPCFGMGR_INVALID; CatFunctLeaveEx((LPARAM)this); } // CLdapCfgMgr::~CLdapCfgMgr //+------------------------------------------------------------ // // Function: CLdapCfgMgr::HrInit // // Synopsis: Initialize with a list of available GCs // // Arguments: // fRediscoverGCs: TRUE: pass in the force rediscovery flag to DsGetDcName // FALSE: Attempt to call DsGetDcName first without // passing in the force rediscovery flag. // // Returns: // S_OK: Success // error from NT5 (DsGetDcName) // CAT_E_NO_GC_SERVERS: THere are no GC servers available to build // the list of GCs // // History: // jstamerj 1999/06/16 14:48:11: Created. // //------------------------------------------------------------- HRESULT CLdapCfgMgr::HrInit( BOOL fRediscoverGCs) { HRESULT hr = S_OK; DWORD dwcServerConfig = 0; DWORD dwCount = 0; PLDAPSERVERCONFIG prgServerConfig = NULL; ICategorizerLdapConfig *pICatLdapConfigInterface = NULL; ICategorizerParametersEx *pIPhatCatParams = NULL; CatFunctEnterEx((LPARAM)this, "CLdapCfgMgr::HrInit"); if(m_pICatParams) { hr = m_pICatParams->QueryInterface(IID_ICategorizerParametersEx, (LPVOID *)&pIPhatCatParams); _ASSERT(SUCCEEDED(hr) && "Unable to get phatcatparams interface"); pIPhatCatParams->GetLdapConfigInterface(&pICatLdapConfigInterface); } if(pICatLdapConfigInterface) { DebugTrace((LPARAM)this, "Getting GC list from sink supplied interface"); // // Get GC servers from sink supplied interface // hr = HrGetGCServers( pICatLdapConfigInterface, m_bt, m_szAccount, m_szPassword, m_szNamingContext, &dwcServerConfig, &prgServerConfig); if(FAILED(hr)) { ERROR_LOG("HrGetGCServers"); hr = CAT_E_NO_GC_SERVERS; goto CLEANUP; } } else { DebugTrace((LPARAM)this, "Getting internal GC list"); // // Build an array of server configs consisting of available GCs // hr = HrBuildGCServerArray( m_bt, m_szAccount, m_szPassword, m_szNamingContext, fRediscoverGCs, &dwcServerConfig, &prgServerConfig); if(FAILED(hr)) { ERROR_LOG("HrBuildGCServerArray"); if(fRediscoverGCs == FALSE) { // // Attempt to build the array again. This time, force // rediscovery of available GCs. This is expensive which is // why we initially try to find all available GCs without // forcing rediscovery. // hr = HrBuildGCServerArray( m_bt, m_szAccount, m_szPassword, m_szNamingContext, TRUE, // fRediscoverGCs &dwcServerConfig, &prgServerConfig); if(FAILED(hr)) { ERROR_LOG("HrBuildGCServerArray - 2nd time"); hr = CAT_E_NO_GC_SERVERS; goto CLEANUP; } } else { // // We already forced rediscovery and failed // hr = CAT_E_NO_GC_SERVERS; goto CLEANUP; } } } LogCnfgInit(); for(dwCount = 0; dwCount < dwcServerConfig; dwCount++) { LogCnfgEntry(& (prgServerConfig[dwCount])); } if(dwcServerConfig == 0) { ErrorTrace((LPARAM)this, "No GC servers found."); ERROR_LOG("--dwcServerConfig == 0 --"); hr = CAT_E_NO_GC_SERVERS; goto CLEANUP; } // // Call the other init function with the array // hr = HrInit( dwcServerConfig, prgServerConfig); ERROR_CLEANUP_LOG("HrInit"); CLEANUP: if(pIPhatCatParams) pIPhatCatParams->Release(); if(prgServerConfig != NULL) delete prgServerConfig; DebugTrace((LPARAM)this, "returning %08lx", hr); CatFunctLeaveEx((LPARAM)this); return hr; } // CLdapCfgMgr::HrInit //+------------------------------------------------------------ // // Function: CLdapCfgMgr::HrGetGCServers // // Synopsis: Get the list of GCs from dsaccess.dll // // Arguments: // bt: Bind type to use for each server // pszAccount: Account to use for each server // pszPassword: password of above account // pszNamingContext: naming context to use for each server // fRediscoverGCs: Attempt to rediscover GCs -- this is expensive and should // only be TRUE after the function has failed once // pdwcServerConfig: Out parameter for the size of the array // pprgServerConfig: Out parameter for the array pointer -- this // should be free'd with the delete operator // // Returns: // S_OK: Success // E_OUTOFMEMORY // CAT_E_NO_GC_SERVERS: There are no available GC servers to build // the list of GCs // error from ntdsapi // // History: // jstamerj 1999/07/01 17:53:02: Created. // //------------------------------------------------------------- HRESULT CLdapCfgMgr::HrGetGCServers( IN ICategorizerLdapConfig *pICatLdapConfigInterface, IN LDAP_BIND_TYPE bt, IN LPSTR pszAccount, IN LPSTR pszPassword, IN LPSTR pszNamingContext, OUT DWORD *pdwcServerConfig, OUT PLDAPSERVERCONFIG *pprgServerConfig) { HRESULT hr = S_OK; DWORD dwNumGCs = 0; DWORD dwIdx = 0; IServersListInfo *pIServersList = NULL; CatFunctEnterEx((LPARAM)this, "CLdapCfgMgr::HrBuildArrayFromDCInfo"); _ASSERT(pdwcServerConfig); _ASSERT(pprgServerConfig); _ASSERT(m_pICatParams); *pdwcServerConfig = 0; hr = pICatLdapConfigInterface->GetGCServers(&pIServersList); if(FAILED(hr)) { ErrorTrace((LPARAM)this, "Unable to get the list of GC servers"); //$$BUGBUG: Why are we asserting here? _ASSERT(0 && "Failed to get GC servers!"); ERROR_LOG("pICatLdapConfigInterface->GetGCServers"); goto CLEANUP; } hr = pIServersList->GetNumGC(&dwNumGCs); _ASSERT(SUCCEEDED(hr) && "GetNumGC should always succeed!"); DebugTrace((LPARAM)this, "Got %d GCs", dwNumGCs); if(dwNumGCs == 0) { DebugTrace((LPARAM)this, "There are no GC servers"); hr = CAT_E_NO_GC_SERVERS; ERROR_LOG("--dwNumGCs == 0 --"); goto CLEANUP; } // // Allocate array // *pprgServerConfig = new LDAPSERVERCONFIG[dwNumGCs]; if(*pprgServerConfig == NULL) { ErrorTrace((LPARAM)this, "Out of memory allocating array of %d LDAPSERVERCONFIGs", dwNumGCs); hr = E_OUTOFMEMORY; ERROR_LOG("new LDAPSERVERCONFIG[]"); goto CLEANUP; } // // Fill in LDAPSERVERCONFIG structures // for(dwIdx = 0; dwIdx < dwNumGCs; dwIdx++) { PLDAPSERVERCONFIG pServerConfig; LPSTR pszName = NULL; pServerConfig = &((*pprgServerConfig)[dwIdx]); // // Copy bindtype, account, password, naming context // pServerConfig->bt = bt; if(pszNamingContext) lstrcpyn(pServerConfig->szNamingContext, pszNamingContext, sizeof(pServerConfig->szNamingContext)); else pServerConfig->szNamingContext[0] = '\0'; if(pszAccount) lstrcpyn(pServerConfig->szAccount, pszAccount, sizeof(pServerConfig->szAccount)); else pServerConfig->szAccount[0] = '\0'; if(pszPassword) lstrcpyn(pServerConfig->szPassword, pszPassword, sizeof(pServerConfig->szPassword)); else pServerConfig->szPassword[0] = '\0'; // // Initialize priority and TCP port // pServerConfig->pri = 0; hr = pIServersList->GetItem( dwIdx, &pServerConfig->dwPort, &pszName); // //$$BUGBUG: Why should this always succeed? It is a sink // supplied interface, isn't it? If the last call fails, we // will set *pdwcServerConfig below, but free the array. // _ASSERT(SUCCEEDED(hr) && "GetItem should always succeed"); // // Copy the name // lstrcpyn(pServerConfig->szHost, pszName, sizeof(pServerConfig->szHost)); DebugTrace((LPARAM)this, "GC: %s on Port: %d", pServerConfig->szHost, pServerConfig->dwPort); } // // Set the out parameter for the array size // *pdwcServerConfig = dwNumGCs; CLEANUP: if(FAILED(hr)) { // // Free the allocated array if we're failing // if(*pprgServerConfig) { delete *pprgServerConfig; *pprgServerConfig = NULL; } } if(pIServersList) pIServersList->Release(); DebugTrace((LPARAM)this, "returning %08lx", hr); CatFunctLeaveEx((LPARAM)this); return hr; } // CLdapCfgMgr::HrBuildArrayFromDCInfo //+------------------------------------------------------------ // // Function: CLdapCfgMgr::HrBuildGCServerArray // // Synopsis: Allocate/build an array of LDAPSERVERCONFIG structures -- // one for each available GC // // Arguments: // bt: Bind type to use for each server // pszAccount: Account to use for each server // pszPassword: password of above account // pszNamingContext: naming context to use for each server // fRediscoverGCs: Attempt to rediscover GCs -- this is expensive and should // only be TRUE after the function has failed once // pdwcServerConfig: Out parameter for the size of the array // pprgServerConfig: Out parameter for the array pointer -- this // should be free'd with the delete operator // // Returns: // S_OK: Success // E_OUTOFMEMORY // CAT_E_NO_GC_SERVERS: There are no available GC servers to build // the list of GCs // error from ntdsapi // // History: // jstamerj 1999/07/01 17:53:02: Created. // //------------------------------------------------------------- HRESULT CLdapCfgMgr::HrBuildGCServerArray( IN LDAP_BIND_TYPE bt, IN LPSTR pszAccount, IN LPSTR pszPassword, IN LPSTR pszNamingContext, IN BOOL fRediscoverGCs, OUT DWORD *pdwcServerConfig, OUT PLDAPSERVERCONFIG *pprgServerConfig) { HRESULT hr = S_OK; DWORD dwErr; ULONG ulFlags; PDOMAIN_CONTROLLER_INFO pDCInfo = NULL; HANDLE hDS = INVALID_HANDLE_VALUE; DWORD cDSDCInfo; PDS_DOMAIN_CONTROLLER_INFO_2 prgDSDCInfo = NULL; CatFunctEnterEx((LPARAM)this, "CLdapCfgMgr::HrBuildGCServerArray"); // // Find one GC using DsGetDcName() // ulFlags = DS_DIRECTORY_SERVICE_REQUIRED | DS_GC_SERVER_REQUIRED; if(fRediscoverGCs) ulFlags |= DS_FORCE_REDISCOVERY; dwErr = DsGetDcName( NULL, // Computername to process this function -- local computer NULL, // Domainname -- primary domain of this computer NULL, // Domain GUID NULL, // Sitename -- site of this computer ulFlags, // Flags; we want a GC &pDCInfo); // Out parameter for the returned info hr = HRESULT_FROM_WIN32(dwErr); if(FAILED(hr)) { ERROR_LOG("DGetDcName"); // // Map one error code // if(hr == HRESULT_FROM_WIN32(ERROR_NO_SUCH_DOMAIN)) hr = CAT_E_NO_GC_SERVERS; pDCInfo = NULL; goto CLEANUP; } DebugTrace((LPARAM)this, "Binding to DC %s", pDCInfo->DomainControllerName); // // Bind to the DC // dwErr = DsBind( pDCInfo->DomainControllerName, // DomainControllerAddress NULL, // DnsDomainName &hDS); // Out param -- handle to DS hr = HRESULT_FROM_WIN32(dwErr); if(FAILED(hr)) { ERROR_LOG("DsBind"); hDS = INVALID_HANDLE_VALUE; goto CLEANUP; } // // Prefix says we need to check this case too // if ((NULL == hDS) || (INVALID_HANDLE_VALUE == hDS)) { FatalTrace((LPARAM)this, "DsBind returned invalid handle"); hDS = INVALID_HANDLE_VALUE; hr = E_FAIL; ERROR_LOG("--DsBind returned invalid handle--"); goto CLEANUP; } DebugTrace((LPARAM)this, "Finding all domain controllers for %s", pDCInfo->DomainName); // // Get information about all the domain controllers // dwErr = DsGetDomainControllerInfo( hDS, // Handle to the DS pDCInfo->DomainName, // Domain name -- use the same domain // as the GC found above 2, // Retrive struct version 2 &cDSDCInfo, // Out param for array size (PVOID *) &prgDSDCInfo); // Out param for array ptr hr = HRESULT_FROM_WIN32(dwErr); if(FAILED(hr)) { ERROR_LOG("DsGetDomainControllerInfo"); prgDSDCInfo = NULL; goto CLEANUP; } hr = HrBuildArrayFromDCInfo( bt, pszAccount, pszPassword, pszNamingContext, cDSDCInfo, prgDSDCInfo, pdwcServerConfig, pprgServerConfig); ERROR_CLEANUP_LOG("HrBuildArrayFromDCInfo"); CLEANUP: if(prgDSDCInfo != NULL) DsFreeDomainControllerInfo( 2, // Free struct version 2 cDSDCInfo, // size of array prgDSDCInfo); // array ptr if(hDS != INVALID_HANDLE_VALUE) DsUnBind(&hDS); if(pDCInfo != NULL) NetApiBufferFree(pDCInfo); DebugTrace((LPARAM)this, "returning %08lx", hr); CatFunctLeaveEx((LPARAM)this); return hr; } // CLdapCfgMgr::HrBuildGCServerArray //+------------------------------------------------------------ // // Function: CLdapCfgMgr::HrBuildArrayFromDCInfo // // Synopsis: Allocate/build an array of LDAPSERVERCONFIG structures -- // one for each available GC in the array // // Arguments: // bt: Bind type to use for each server // pszAccount: Account to use for each server // pszPassword: password of above account // pszNamingContext: naming context to use for each server // dwDSDCInfo: size of the prgDSDCInfo array // prgDSDCInfo: array of domain controller info structures // pdwcServerConfig: Out parameter for the size of the array // pprgServerConfig: Out parameter for the array pointer -- this // should be free'd with the delete operator // // Returns: // S_OK: Success // E_OUTOFMEMORY // CAT_E_NO_GC_SERVERS: There were no GCs in the array // // History: // jstamerj 1999/06/17 10:40:46: Created. // //------------------------------------------------------------- HRESULT CLdapCfgMgr::HrBuildArrayFromDCInfo( IN LDAP_BIND_TYPE bt, IN LPSTR pszAccount, IN LPSTR pszPassword, IN LPSTR pszNamingContext, IN DWORD dwcDSDCInfo, IN PDS_DOMAIN_CONTROLLER_INFO_2 prgDSDCInfo, OUT DWORD *pdwcServerConfig, OUT PLDAPSERVERCONFIG *pprgServerConfig) { HRESULT hr = S_OK; DWORD dwNumGCs = 0; DWORD dwSrcIdx; DWORD dwDestIdx; CatFunctEnterEx((LPARAM)this, "CLdapCfgMgr::HrBuildArrayFromDCInfo"); _ASSERT(pdwcServerConfig); _ASSERT(pprgServerConfig); for(dwSrcIdx = 0; dwSrcIdx < dwcDSDCInfo; dwSrcIdx++) { LPSTR pszName; pszName = SzConnectNameFromDomainControllerInfo( &(prgDSDCInfo[dwSrcIdx])); if(pszName == NULL) { ErrorTrace((LPARAM)this, "DC \"%s\" has no dns/netbios names", prgDSDCInfo[dwSrcIdx].ServerObjectName ? prgDSDCInfo[dwSrcIdx].ServerObjectName : "unknown"); } else if(prgDSDCInfo[dwSrcIdx].fIsGc) { dwNumGCs++; DebugTrace((LPARAM)this, "Discovered GC #%d: %s", dwNumGCs, pszName); } else { DebugTrace((LPARAM)this, "Discarding non-GC: %s", pszName); } } // // Allocate array // *pprgServerConfig = new LDAPSERVERCONFIG[dwNumGCs]; if(*pprgServerConfig == NULL) { ErrorTrace((LPARAM)this, "Out of memory alloacting array of %d LDAPSERVERCONFIGs", dwNumGCs); hr = E_OUTOFMEMORY; ERROR_LOG("new LDAPSERVERCONFIG[]"); goto CLEANUP; } // // Fill in LDAPSERVERCONFIG structures // for(dwSrcIdx = 0, dwDestIdx = 0; dwSrcIdx < dwcDSDCInfo; dwSrcIdx++) { LPSTR pszName; pszName = SzConnectNameFromDomainControllerInfo( &(prgDSDCInfo[dwSrcIdx])); if((pszName != NULL) && (prgDSDCInfo[dwSrcIdx].fIsGc)) { PLDAPSERVERCONFIG pServerConfig; _ASSERT(dwDestIdx < dwNumGCs); pServerConfig = &((*pprgServerConfig)[dwDestIdx]); // // Copy bindtype, account, password, naming context // pServerConfig->bt = bt; if(pszNamingContext) lstrcpyn(pServerConfig->szNamingContext, pszNamingContext, sizeof(pServerConfig->szNamingContext)); else pServerConfig->szNamingContext[0] = '\0'; if(pszAccount) lstrcpyn(pServerConfig->szAccount, pszAccount, sizeof(pServerConfig->szAccount)); else pServerConfig->szAccount[0] = '\0'; if(pszPassword) lstrcpyn(pServerConfig->szPassword, pszPassword, sizeof(pServerConfig->szPassword)); else pServerConfig->szPassword[0] = '\0'; // // Initialize priority and TCP port // pServerConfig->pri = 0; pServerConfig->dwPort = LDAP_GC_PORT; // // Copy the name // lstrcpyn(pServerConfig->szHost, pszName, sizeof(pServerConfig->szHost)); dwDestIdx++; } } // // Assert check -- we should have filled in the entire array // _ASSERT(dwDestIdx == dwNumGCs); // // Set the out parameter for the array size // *pdwcServerConfig = dwNumGCs; CLEANUP: if(FAILED(hr)) { // // Free the allocated array if we're failing // if(*pprgServerConfig) { delete *pprgServerConfig; *pprgServerConfig = NULL; } } DebugTrace((LPARAM)this, "returning %08lx", hr); CatFunctLeaveEx((LPARAM)this); return hr; } // CLdapCfgMgr::HrBuildArrayFromDCInfo //+------------------------------------------------------------ // // Function: CLdapCfgMgr::HrInit // // Synopsis: Initialize given an array of LDAPSERVERCONFIG structs // // Arguments: // dwcServers: Size of the array // prgServerConfig: Array of LDAPSERVERCONFIG structs // // Returns: // S_OK: Success // E_OUTOFMEMORY // // History: // jstamerj 1999/06/17 12:32:11: Created. // //------------------------------------------------------------- HRESULT CLdapCfgMgr::HrInit( DWORD dwcServers, PLDAPSERVERCONFIG prgServerConfig) { HRESULT hr = S_OK; CLdapCfg *pCLdapCfgOld = NULL; CLdapCfg *pCLdapCfg = NULL; BOOL fHaveLock = FALSE; CatFunctEnterEx((LPARAM)this, "CLdapCfgMgr::HrInit"); pCLdapCfg = new (dwcServers) CLdapCfg(GetISMTPServerEx()); if(pCLdapCfg == NULL) { hr = E_OUTOFMEMORY; ERROR_LOG("new CLdapCfg"); goto CLEANUP; } // // Allow only one config change at a time // m_sharelock.ExclusiveLock(); fHaveLock = TRUE; // // Grab the current m_pCLdapCfg into pCLdapCfgOld // pCLdapCfgOld = m_pCLdapCfg; hr = pCLdapCfg->HrInit( dwcServers, prgServerConfig, pCLdapCfgOld); ERROR_CLEANUP_LOG("pCLdapCfg->HrInit"); // // Put the new configuration in place // Swap pointers // m_pCLdapCfg = pCLdapCfg; // // Set the last update time // GetSystemTimeAsFileTime((LPFILETIME)&m_ulLastUpdateTime); CLEANUP: if(fHaveLock) m_sharelock.ExclusiveUnlock(); if(pCLdapCfgOld) pCLdapCfgOld->Release(); DebugTrace((LPARAM)this, "returning %08lx", hr); CatFunctLeaveEx((LPARAM)this); return hr; } // CLdapCfgMgr::HrInit //+------------------------------------------------------------ // // Function: CLdapCfgMgr::HrGetConnection // // Synopsis: Select/return a connection // // Arguments: // ppConn: Out parameter to receive ptr to connection // // Returns: // S_OK: Success // E_FAIL: not initialized // error from CLdapConnectionCache // // History: // jstamerj 1999/06/17 15:25:51: Created. // //------------------------------------------------------------- HRESULT CLdapCfgMgr::HrGetConnection( CCfgConnection **ppConn) { HRESULT hr = S_OK; CatFunctEnterEx((LPARAM)this, "CLdapCfgMgr::HrGetConnection"); hr = HrUpdateConfigurationIfNecessary(); ERROR_CLEANUP_LOG("HrUpdateConfigurationIfNecessary"); m_sharelock.ShareLock(); if(m_pCLdapCfg) { DWORD dwcAttempts = 0; do { dwcAttempts++; hr = m_pCLdapCfg->HrGetConnection(ppConn, &m_LdapConnectionCache); } while((hr == HRESULT_FROM_WIN32(ERROR_RETRY)) && (dwcAttempts <= m_pCLdapCfg->DwNumServers())); // // If we retried DwNumServers() times and still couldn't get a // connection, fail with E_DBCONNECTION. // if(FAILED(hr)) { ERROR_LOG("m_pCLdapCfg->HrGetConnection"); if(hr == HRESULT_FROM_WIN32(ERROR_RETRY)) hr = CAT_E_DBCONNECTION; } } else { hr = E_FAIL; _ASSERT(0 && "HrInit not called or did not succeed"); } m_sharelock.ShareUnlock(); CLEANUP: DebugTrace((LPARAM)this, "returning %08lx", hr); CatFunctLeaveEx((LPARAM)this); return hr; } // CLdapCfgMgr::HrGetConnection //+------------------------------------------------------------ // // Function: CLdapCfgMgr::LogCnfgInit // // Synopsis: Log cnfgmgr init event // // Arguments: none // // Returns: Nothing // // History: // jstamerj 2001/12/13 00:57:18: Created. // //------------------------------------------------------------- VOID CLdapCfgMgr::LogCnfgInit() { CatLogEvent( GetISMTPServerEx(), CAT_EVENT_CNFGMGR_INIT, 0, NULL, S_OK, "", LOGEVENT_FLAG_ALWAYS, LOGEVENT_LEVEL_MEDIUM); } //+------------------------------------------------------------ // // Function: CLdapCfgMgr::LogCnfgEntry // // Synopsis: Log cnfgmgr entry event // // Arguments: pConfig: entry to log // // Returns: Nothing // // History: // jstamerj 2001/12/13 00:57:30: Created. // //------------------------------------------------------------- VOID CLdapCfgMgr::LogCnfgEntry( PLDAPSERVERCONFIG pConfig) { LPCSTR rgSubStrings[6]; CHAR szPort[16], szPri[16], szBindType[16]; _snprintf(szPort, sizeof(szPort), "%d", pConfig->dwPort); _snprintf(szPri, sizeof(szPri), "%d", pConfig->pri); _snprintf(szBindType, sizeof(szBindType), "%d", pConfig->bt); rgSubStrings[0] = pConfig->szHost; rgSubStrings[1] = szPort; rgSubStrings[2] = szPri; rgSubStrings[3] = szBindType; rgSubStrings[4] = pConfig->szNamingContext; rgSubStrings[5] = pConfig->szAccount; CatLogEvent( GetISMTPServerEx(), CAT_EVENT_CNFGMGR_ENTRY, 6, rgSubStrings, S_OK, pConfig->szHost, LOGEVENT_FLAG_ALWAYS, LOGEVENT_LEVEL_MEDIUM); } //+------------------------------------------------------------ // // Function: CLdapCfg::operator new // // Synopsis: Allocate memory for a CLdapCfg object // // Arguments: // size: size of C++ object // dwcServers: Number of servers in this configuration // // Returns: // void pointer to the new object // // History: // jstamerj 1999/06/17 13:40:56: Created. // //------------------------------------------------------------- void * CLdapCfg::operator new( size_t size, DWORD dwcServers) { CLdapCfg *pCLdapCfg; DWORD dwAllocatedSize; CatFunctEnterEx((LPARAM)0, "CLdapCfg::operator new"); _ASSERT(size == sizeof(CLdapCfg)); // // Allocate space fo the CLdapServerCfg * array contigously after // the memory for the C++ object // dwAllocatedSize = sizeof(CLdapCfg) + (dwcServers * sizeof(CLdapServerCfg)); pCLdapCfg = (CLdapCfg *) new BYTE[dwAllocatedSize]; if(pCLdapCfg) { pCLdapCfg->m_dwSignature = SIGNATURE_CLDAPCFG; pCLdapCfg->m_dwcServers = dwcServers; pCLdapCfg->m_prgpCLdapServerCfg = (CLdapServerCfg **) (pCLdapCfg + 1); } CatFunctLeaveEx((LPARAM)pCLdapCfg); return pCLdapCfg; } // CLdapCfg::operator new //+------------------------------------------------------------ // // Function: CLdapCfg::CLdapCfg // // Synopsis: Initialize member data // // Arguments: // // Returns: NOTHING // // History: // jstamerj 1999/06/17 13:46:50: Created. // //------------------------------------------------------------- CLdapCfg::CLdapCfg( ISMTPServerEx *pISMTPServerEx) { CatFunctEnterEx((LPARAM)this, "CLdapCfg::CLdapCfg"); // // signature and number of servers should be set by the new operator // _ASSERT(m_dwSignature == SIGNATURE_CLDAPCFG); // // Zero out the array of pointers to CLdapServerCfg objects // ZeroMemory(m_prgpCLdapServerCfg, m_dwcServers * sizeof(CLdapServerCfg *)); m_dwInc = 0; m_dwcConnectionFailures = 0; m_pISMTPServerEx = pISMTPServerEx; if(m_pISMTPServerEx) m_pISMTPServerEx->AddRef(); CatFunctLeaveEx((LPARAM)this); } // CLdapCfg::CLdapCfg //+------------------------------------------------------------ // // Function: CLdapCfg::~CLdapCfg // // Synopsis: Clean up // // Arguments: NONE // // Returns: NOTHING // // History: // jstamerj 1999/06/17 14:47:25: Created. // //------------------------------------------------------------- CLdapCfg::~CLdapCfg() { DWORD dwCount; CatFunctEnterEx((LPARAM)this, "CLdapCfg::~CLdapCfg"); // // Release all connections configurations // for(dwCount = 0; dwCount < m_dwcServers; dwCount++) { CLdapServerCfg *pCLdapServerCfg; pCLdapServerCfg = m_prgpCLdapServerCfg[dwCount]; m_prgpCLdapServerCfg[dwCount] = NULL; if(pCLdapServerCfg) pCLdapServerCfg->Release(); } if(m_pISMTPServerEx) m_pISMTPServerEx->Release(); _ASSERT(m_dwSignature == SIGNATURE_CLDAPCFG); m_dwSignature = SIGNATURE_CLDAPCFG_INVALID; CatFunctLeaveEx((LPARAM)this); } // CLdapCfg::~CLdapCfg //+------------------------------------------------------------ // // Function: CLdapCfg::HrInit // // Synopsis: Initialize the configuration // // Arguments: // dwcServers: Size of config array // prgSeverConfig: LDAPSERVERCONFIG array // pCLdapCfgOld: The previous configuration // // Returns: // S_OK: Success // E_OUTOFMEMORY // // History: // jstamerj 1999/06/17 13:52:20: Created. // //------------------------------------------------------------- HRESULT CLdapCfg::HrInit( DWORD dwcServers, PLDAPSERVERCONFIG prgServerConfig, CLdapCfg *pCLdapCfgOld) { HRESULT hr = S_OK; DWORD dwCount; CatFunctEnterEx((LPARAM)this, "CLdapCfg::HrInit"); // // m_dwcServers should be initialized by the new operator // _ASSERT(dwcServers == m_dwcServers); m_sharelock.ExclusiveLock(); // // Zero out the array of pointers to CLdapServerCfg objects // ZeroMemory(m_prgpCLdapServerCfg, m_dwcServers * sizeof(CLdapServerCfg *)); for(dwCount = 0; dwCount < m_dwcServers; dwCount++) { DebugTrace((LPARAM)this, "GC list entry: %s (%u)", prgServerConfig[dwCount].szHost, prgServerConfig[dwCount].dwPort); CLdapServerCfg *pServerCfg = NULL; hr = CLdapServerCfg::GetServerCfg( GetISMTPServerEx(), &(prgServerConfig[dwCount]), &pServerCfg); ERROR_CLEANUP_LOG("CLdapServerCfg::GetServerCfg"); m_prgpCLdapServerCfg[dwCount] = pServerCfg; } ShuffleArray(); CLEANUP: m_sharelock.ExclusiveUnlock(); DebugTrace((LPARAM)this, "returning %08lx", hr); CatFunctLeaveEx((LPARAM)this); return hr; } // CLdapCfg::HrInit //+------------------------------------------------------------ // // Function: CLdapCfg::HrGetConnection // // Synopsis: Select a connection and return it // // Arguments: // ppConn: Set to a pointer to the selected connection // pLdapConnectionCache: Cache to get connection from // // Returns: // S_OK: Success // E_FAIL: We are shutting down // error from ldapconn // // History: // jstamerj 1999/06/17 14:49:37: Created. // //------------------------------------------------------------- HRESULT CLdapCfg::HrGetConnection( CCfgConnection **ppConn, CCfgConnectionCache *pLdapConnectionCache) { HRESULT hr = S_OK; LDAPSERVERCOST Cost, BestCost; DWORD dwCount; CLdapServerCfg *pCLdapServerCfg = NULL; BOOL fFirstServer = TRUE; DWORD dwStart, dwCurrent; CatFunctEnterEx((LPARAM)this, "CLdapCfg::HrGetConnection"); // // Get the cost of the first connection // m_sharelock.ShareLock(); // // Round robin where we start searching the array // Do this so we will use connections with the same cost // approximately the same amount of time. // dwStart = InterlockedIncrement((PLONG) &m_dwInc) % m_dwcServers; for(dwCount = 0; dwCount < m_dwcServers; dwCount++) { dwCurrent = (dwStart + dwCount) % m_dwcServers; if(m_prgpCLdapServerCfg[dwCurrent]) { m_prgpCLdapServerCfg[dwCurrent]->Cost(GetISMTPServerEx(), &Cost); if(fFirstServer) { pCLdapServerCfg = m_prgpCLdapServerCfg[dwCurrent]; fFirstServer = FALSE; BestCost = Cost; } else if(Cost < BestCost) { pCLdapServerCfg = m_prgpCLdapServerCfg[dwCurrent]; BestCost = Cost; } } } if(pCLdapServerCfg == NULL) { ErrorTrace((LPARAM)this, "HrGetConnection can not find any connections"); hr = E_FAIL; _ASSERT(0 && "HrInit not called or did not succeed"); ERROR_LOG("--pCLdapServerCfg == NULL--"); goto CLEANUP; } if(BestCost >= COST_TOO_HIGH_TO_CONNECT) { DebugTrace((LPARAM)this, "BestCost is too high to attempt connection"); hr = CAT_E_DBCONNECTION; ERROR_LOG("-- BestCost >= COST_TOO_HIGH_TO_CONNECT --"); goto CLEANUP; } hr = pCLdapServerCfg->HrGetConnection(GetISMTPServerEx(), ppConn, pLdapConnectionCache); // If we fail to connect to a GC --- there may be other GCs which // are still up. Therefore we should try to connect to them (till // we run out of GCs (BestCost >= COST_TOO_HIGH_TO_CONNECT) if(FAILED(hr)) { DebugTrace((LPARAM)this, "Failed to connect. hr = 0x%08x", hr); ERROR_LOG("pCLdapServerCfg->HrGetConnection"); hr = HRESULT_FROM_WIN32(ERROR_RETRY); } CLEANUP: m_sharelock.ShareUnlock(); if(FAILED(hr)) InterlockedIncrement((PLONG)&m_dwcConnectionFailures); DebugTrace((LPARAM)this, "returning %08lx", hr); CatFunctLeaveEx((LPARAM)this); return hr; } // CLdapCfg::HrGetConnection //+------------------------------------------------------------ // // Function: CLdapCfg::ShuffleArray // // Synopsis: Randomize the order of the CLdapServerCfg array // // Arguments: NONE // // Returns: NOTHING // // History: // jstamerj 1999/06/17 19:10:06: Created. // //------------------------------------------------------------- VOID CLdapCfg::ShuffleArray() { DWORD dwCount; DWORD dwSwap; CLdapServerCfg *pTmp; CatFunctEnterEx((LPARAM)this, "CLdapCfg::ShuffleArray"); srand((int)(GetCurrentThreadId() * time(NULL))); for(dwCount = 0; dwCount < (m_dwcServers - 1); dwCount++) { // // Choose an integer between dwCount and m_dwcServers - 1 // dwSwap = dwCount + (rand() % (m_dwcServers - dwCount)); // // Swap pointers // pTmp = m_prgpCLdapServerCfg[dwCount]; m_prgpCLdapServerCfg[dwCount] = m_prgpCLdapServerCfg[dwSwap]; m_prgpCLdapServerCfg[dwSwap] = pTmp; } CatFunctLeaveEx((LPARAM)this); } // CLdapCfg::ShuffleArray //+------------------------------------------------------------ // // Function: CLdapServerCfg::CLdapServerCfg // // Synopsis: Initialize member variables // // Arguments: NONE // // Returns: NOTHING // // History: // jstamerj 1999/06/17 15:30:32: Created. // //------------------------------------------------------------- CLdapServerCfg::CLdapServerCfg() { CatFunctEnterEx((LPARAM)this, "CLdapServerCfg::CLdapServerCfg"); m_dwSignature = SIGNATURE_CLDAPSERVERCFG; m_ServerConfig.dwPort = 0; m_ServerConfig.pri = 0; m_ServerConfig.bt = BIND_TYPE_NONE; m_ServerConfig.szHost[0] = '\0'; m_ServerConfig.szNamingContext[0] = '\0'; m_ServerConfig.szAccount[0] = '\0'; m_ServerConfig.szPassword[0] = '\0'; m_connstate = CONN_STATE_INITIAL; ZeroMemory(&m_ftLastStateUpdate, sizeof(m_ftLastStateUpdate)); m_dwcPendingSearches = 0; m_lRefCount = 1; m_fLocalServer = FALSE; m_dwcCurrentConnectAttempts = 0; m_dwcFailedConnectAttempts = 0; CatFunctLeaveEx((LPARAM)this); } // CLdapServerCfg::CLdapServerCfg //+---------------------------------------------------------------------------- // // Function: CLdapServerCfg::InitializeFromRegistry // // Synopsis: Helper function that looks up parameters from the registry. // Configurable parameters are: // GC_COST_CONNECTED_LOCAL // GC_COST_CONNECTED_REMOTE // GC_COST_INITIAL_LOCAL // GC_COST_INITIAL_REMOTE // GC_COST_RETRY_LOCAL // GC_COST_RETRY_REMOTE // // Arguments: None // // Returns: Nothing. // //----------------------------------------------------------------------------- VOID CLdapServerCfg::InitializeFromRegistry() { HKEY hkey; DWORD dwErr, dwType, dwValue, cbValue; dwErr = RegOpenKey(HKEY_LOCAL_MACHINE, GC_COST_PARAMETERS_KEY, &hkey); if (dwErr == ERROR_SUCCESS) { cbValue = sizeof(dwValue); dwErr = RegQueryValueEx( hkey, GC_COST_CONNECTED_LOCAL_VALUE, NULL, &dwType, (LPBYTE) &dwValue, &cbValue); if (dwErr == ERROR_SUCCESS && dwType == REG_DWORD) { m_dwCostConnectedLocal = dwValue; } cbValue = sizeof(dwValue); dwErr = RegQueryValueEx( hkey, GC_COST_CONNECTED_REMOTE_VALUE, NULL, &dwType, (LPBYTE) &dwValue, &cbValue); if (dwErr == ERROR_SUCCESS && dwType == REG_DWORD) { m_dwCostConnectedRemote = dwValue; } cbValue = sizeof(dwValue); dwErr = RegQueryValueEx( hkey, GC_COST_INITIAL_LOCAL_VALUE, NULL, &dwType, (LPBYTE) &dwValue, &cbValue); if (dwErr == ERROR_SUCCESS && dwType == REG_DWORD) { m_dwCostInitialLocal = dwValue; } cbValue = sizeof(dwValue); dwErr = RegQueryValueEx( hkey, GC_COST_INITIAL_REMOTE_VALUE, NULL, &dwType, (LPBYTE) &dwValue, &cbValue); if (dwErr == ERROR_SUCCESS && dwType == REG_DWORD) { m_dwCostInitialRemote = dwValue; } cbValue = sizeof(dwValue); dwErr = RegQueryValueEx( hkey, GC_COST_RETRY_LOCAL_VALUE, NULL, &dwType, (LPBYTE) &dwValue, &cbValue); if (dwErr == ERROR_SUCCESS && dwType == REG_DWORD) { m_dwCostRetryLocal = dwValue; } cbValue = sizeof(dwValue); dwErr = RegQueryValueEx( hkey, GC_COST_RETRY_REMOTE_VALUE, NULL, &dwType, (LPBYTE) &dwValue, &cbValue); if (dwErr == ERROR_SUCCESS && dwType == REG_DWORD) { m_dwCostRetryRemote = dwValue; } RegCloseKey( hkey ); } } //+------------------------------------------------------------ // // Function: CLdapServerCfg::~CLdapServerCfg // // Synopsis: object destructor. Check and invalidate signature // // Arguments: NONE // // Returns: NOTHING // // History: // jstamerj 1999/06/22 11:09:03: Created. // //------------------------------------------------------------- CLdapServerCfg::~CLdapServerCfg() { CatFunctEnterEx((LPARAM)this, "CLdapServerCfg::~CLdapServerCfg"); _ASSERT(m_dwSignature == SIGNATURE_CLDAPSERVERCFG); m_dwSignature = SIGNATURE_CLDAPSERVERCFG_INVALID; CatFunctLeaveEx((LPARAM)this); } // CLdapServerCfg::~CLdapServerCfg //+------------------------------------------------------------ // // Function: CLdapServerCfg::HrInit // // Synopsis: Initialize with the passed in config // // Arguments: // pCLdapCfg: the cfg object to notify when servers go down // pServerConfig: The server config struct to use // // Returns: // S_OK: Success // // History: // jstamerj 1999/06/17 15:43:25: Created. // //------------------------------------------------------------- HRESULT CLdapServerCfg::HrInit( PLDAPSERVERCONFIG pServerConfig) { HRESULT hr = S_OK; CatFunctEnterEx((LPARAM)this, "CLdapServerCfg::HrInit"); CopyMemory(&m_ServerConfig, pServerConfig, sizeof(m_ServerConfig)); // // Check if this is the local computer // if(fIsLocalComputer(pServerConfig)) m_fLocalServer = TRUE; DebugTrace((LPARAM)this, "returning %08lx", hr); CatFunctLeaveEx((LPARAM)this); return hr; } // CLdapServerCfg::HrInit //+------------------------------------------------------------ // // Function: CLdapServerCfg::fIsLocalComputer // // Synopsis: Determine if pServerConfig is the local computer or not // // Arguments: // pServerConfig: the server config info structure // // Returns: // TRUE: Server is the local computer // FALSE: Sevrver is a remote computer // // History: // jstamerj 1999/06/22 15:26:53: Created. // //------------------------------------------------------------- BOOL CLdapServerCfg::fIsLocalComputer( PLDAPSERVERCONFIG pServerConfig) { BOOL fLocal = FALSE; DWORD dwSize; CHAR szHost[CAT_MAX_DOMAIN]; CatFunctEnterEx((LPARAM)NULL, "CLdapServerCfg::fIsLocalComputer"); // // Check the FQ name // dwSize = sizeof(szHost); if(GetComputerNameEx( ComputerNameDnsFullyQualified, szHost, &dwSize) && (lstrcmpi(szHost, pServerConfig->szHost) == 0)) { fLocal = TRUE; goto CLEANUP; } // // Check the DNS name // dwSize = sizeof(szHost); if(GetComputerNameEx( ComputerNameDnsHostname, szHost, &dwSize) && (lstrcmpi(szHost, pServerConfig->szHost) == 0)) { fLocal = TRUE; goto CLEANUP; } // // Check the netbios name // dwSize = sizeof(szHost); if(GetComputerNameEx( ComputerNameNetBIOS, szHost, &dwSize) && (lstrcmpi(szHost, pServerConfig->szHost) == 0)) { fLocal = TRUE; goto CLEANUP; } CLEANUP: DebugTrace((LPARAM)NULL, "returning %08lx", fLocal); CatFunctLeaveEx((LPARAM)NULL); return fLocal; } // CLdapServerCfg::fIsLocalComputer //+------------------------------------------------------------ // // Function: CLdapServerCfg::Cost // // Synopsis: Return the cost of choosing this connection // // Arguments: // pCost: Cost sturcture to fill in // // Returns: NOTHING // // History: // jstamerj 1999/06/17 16:08:23: Created. // //------------------------------------------------------------- VOID CLdapServerCfg::Cost( IN ISMTPServerEx *pISMTPServerEx, OUT PLDAPSERVERCOST pCost) { BOOL fShareLock = FALSE; CatFunctEnterEx((LPARAM)this, "CLdapServerCfg::Cost"); // // The smallest unit of cost is the number of pending searches. // The next factor of cost is the connection state. // States: // Connected = + COST_CONNECTED // Initially state (unconnected) = + COST_INITIAL // Connection down = + COST_RETRY // Connection recently went down = + COST_DOWN // // A configurable priority is always added to the cost. // Also, COST_REMOTE is added to the cost of all non-local servers. // *pCost = m_ServerConfig.pri + m_dwcPendingSearches; // // Protect the connection state variables with a spinlock // m_sharelock.ShareLock(); fShareLock = TRUE; switch(m_connstate) { case CONN_STATE_INITIAL: (*pCost) += (m_fLocalServer) ? m_dwCostInitialLocal : m_dwCostInitialRemote; break; case CONN_STATE_RETRY: if(m_dwcCurrentConnectAttempts >= MAX_CONNECT_THREADS) (*pCost) += COST_TOO_HIGH_TO_CONNECT; else (*pCost) += (m_fLocalServer) ? m_dwCostRetryLocal : m_dwCostRetryRemote; break; case CONN_STATE_DOWN: // // Check if the state should be changed to CONN_STATE_RETRY // if(fReadyForRetry()) { (*pCost) += (m_fLocalServer) ? m_dwCostRetryLocal : m_dwCostRetryRemote; // // Change state // fShareLock = FALSE; m_sharelock.ShareUnlock(); m_sharelock.ExclusiveLock(); // // Double check in the exclusive lock // if((m_connstate == CONN_STATE_DOWN) && fReadyForRetry()) { LogStateChangeEvent( pISMTPServerEx, CONN_STATE_RETRY, m_ServerConfig.szHost, m_ServerConfig.dwPort); m_connstate = CONN_STATE_RETRY; } m_sharelock.ExclusiveUnlock(); } else { // // Server is probably still down (don't retry yet) // (*pCost) += (m_fLocalServer) ? COST_DOWN_LOCAL : COST_DOWN_REMOTE; } break; case CONN_STATE_CONNECTED: (*pCost) += (m_fLocalServer) ? m_dwCostConnectedLocal : m_dwCostConnectedRemote; break; default: // Nothing to add break; } if(fShareLock) m_sharelock.ShareUnlock(); CatFunctLeaveEx((LPARAM)this); } // CLdapServerCfg::Cost //+------------------------------------------------------------ // // Function: CLdapServerCfg::HrGetConnection // // Synopsis: // // Arguments: // // Returns: // S_OK: Success // // History: // jstamerj 1999/06/18 10:49:04: Created. // //------------------------------------------------------------- HRESULT CLdapServerCfg::HrGetConnection( ISMTPServerEx *pISMTPServerEx, CCfgConnection **ppConn, CCfgConnectionCache *pLdapConnectionCache) { HRESULT hr = S_OK; DWORD dwcConnectAttempts = 0; CatFunctEnterEx((LPARAM)this, "CLdapServerCfg::HrGetConnection"); dwcConnectAttempts = (DWORD) InterlockedIncrement((PLONG) &m_dwcCurrentConnectAttempts); m_sharelock.ShareLock(); if((m_connstate == CONN_STATE_RETRY) && (dwcConnectAttempts > MAX_CONNECT_THREADS)) { m_sharelock.ShareUnlock(); ErrorTrace((LPARAM)this, "Over max connect thread limit"); hr = HRESULT_FROM_WIN32(ERROR_RETRY); ERROR_LOG_STATIC( "--over max connect thread limit--", this, pISMTPServerEx); goto CLEANUP; } m_sharelock.ShareUnlock(); DebugTrace((LPARAM)this, "Attempting to connect to %s:%d", m_ServerConfig.szHost, m_ServerConfig.dwPort); hr = pLdapConnectionCache->GetConnection( ppConn, &m_ServerConfig, this); ERROR_CLEANUP_LOG_STATIC( "pLdapConnectionCache->GetConnection", this, pISMTPServerEx); // // CCfgConnection::Connect will update the connection state // CLEANUP: InterlockedDecrement((PLONG) &m_dwcCurrentConnectAttempts); DebugTrace((LPARAM)this, "returning %08lx", hr); CatFunctLeaveEx((LPARAM)this); return hr; } // CLdapServerCfg::HrGetConnection //+------------------------------------------------------------ // // Function: CLdapServerCfg::UpdateConnectionState // // Synopsis: Update the connection state. // // Arguments: // pft: Time of update -- if this time is before the last update done, // then this update will be ignored. // If NULL, the function will assume the current time. // connstate: The new connection state. // // Returns: NOTHING // // History: // jstamerj 1999/06/18 13:22:25: Created. // //------------------------------------------------------------- VOID CLdapServerCfg::UpdateConnectionState( ISMTPServerEx *pISMTPServerEx, ULARGE_INTEGER *pft_IN, CONN_STATE connstate) { ULARGE_INTEGER ft, *pft; CatFunctEnterEx((LPARAM)this, "CLdapServerCfg::UpdateConnectionState"); if(pft_IN != NULL) { pft = pft_IN; } else { ft = GetCurrentTime(); pft = &ft; } // // Protect connection state variables with a sharelock // m_sharelock.ShareLock(); // // If we have the latest information about the connection state, // then update the state if the connection state changed. // Also update m_ftLastStateUpdate to the latest ft when the // connection state is down -- m_ftLastStateUpdate is assumed to // be the last connection attempt time when connstate is down. // if( (pft->QuadPart > m_ftLastStateUpdate.QuadPart) && ((m_connstate != connstate) || (connstate == CONN_STATE_DOWN))) { // // We'd like to update the connection state // m_sharelock.ShareUnlock(); m_sharelock.ExclusiveLock(); // // Double check // if( (pft->QuadPart > m_ftLastStateUpdate.QuadPart) && ((m_connstate != connstate) || (connstate == CONN_STATE_DOWN))) { // // Update // if(m_connstate != connstate) { LogStateChangeEvent( pISMTPServerEx, connstate, m_ServerConfig.szHost, m_ServerConfig.dwPort); } m_ftLastStateUpdate = *pft; m_connstate = connstate; DebugTrace((LPARAM)this, "Updating state %d, conn %s:%d", connstate, m_ServerConfig.szHost, m_ServerConfig.dwPort); } else { DebugTrace((LPARAM)this, "Ignoring state update %d, conn %s:%d", connstate, m_ServerConfig.szHost, m_ServerConfig.dwPort); } m_sharelock.ExclusiveUnlock(); } else { DebugTrace((LPARAM)this, "Ignoring state update %d, conn %s:%d", connstate, m_ServerConfig.szHost, m_ServerConfig.dwPort); m_sharelock.ShareUnlock(); } CatFunctLeaveEx((LPARAM)this); } // CLdapServerCfg::UpdateConnectionState //+------------------------------------------------------------ // // Function: CLdapServerCfg::GetServerCfg // // Synopsis: Find or Create a CLdapServerCfg object with the specified // configuration. // // Arguments: // pServerConfig: desired configuration // pCLdapServerCfg: return pointer for the CLdapServerCfg object // // Returns: // S_OK: Success // E_OUTOFMEMORY // // History: // jstamerj 1999/06/21 11:26:49: Created. // //------------------------------------------------------------- HRESULT CLdapServerCfg::GetServerCfg( IN ISMTPServerEx *pISMTPServerEx, IN PLDAPSERVERCONFIG pServerConfig, OUT CLdapServerCfg **ppCLdapServerCfg) { HRESULT hr = S_OK; CLdapServerCfg *pCCfg; CatFunctEnterEx((LPARAM)NULL, "CLdapServerCfg::GetServerCfg"); m_listlock.ShareLock(); pCCfg = FindServerCfg(pServerConfig); if(pCCfg) pCCfg->AddRef(); m_listlock.ShareUnlock(); if(pCCfg == NULL) { // // Check again for a server cfg object inside an exclusive // lock // m_listlock.ExclusiveLock(); pCCfg = FindServerCfg(pServerConfig); if(pCCfg) { pCCfg->AddRef(); } else { // // Create a new object // pCCfg = new CLdapServerCfg(); if(pCCfg == NULL) { hr = E_OUTOFMEMORY; ERROR_LOG_STATIC( "new CLdapServerCfg", 0, pISMTPServerEx); } else { hr = pCCfg->HrInit(pServerConfig); if(FAILED(hr)) { ERROR_LOG_STATIC( "pCCfg->HrInit", pCCfg, pISMTPServerEx); delete pCCfg; pCCfg = NULL; } else { // // Add to global list // InsertTailList(&m_listhead, &(pCCfg->m_le)); } } } m_listlock.ExclusiveUnlock(); } // // Set out parameter // *ppCLdapServerCfg = pCCfg; DebugTrace((LPARAM)NULL, "returning hr %08lx", hr); CatFunctLeaveEx((LPARAM)NULL); return hr; } // CLdapServerCfg::GetServerCfg //+------------------------------------------------------------ // // Function: CLdapServerCfg::FindServerCfg // // Synopsis: Find a server cfg object that matches the // LDAPSERVERCONFIG structure. Note, m_listlock must be // locked when calling this function. // // Arguments: // pServerConfig: pointer to the LDAPSERVERCONFIG struct // // Returns: // NULL: there is no such server cfg object // else, ptr to the found CLdapServerCfg object // // History: // jstamerj 1999/06/21 10:43:23: Created. // //------------------------------------------------------------- CLdapServerCfg * CLdapServerCfg::FindServerCfg( PLDAPSERVERCONFIG pServerConfig) { CLdapServerCfg *pMatch = NULL; PLIST_ENTRY ple; CatFunctEnterEx((LPARAM)NULL, "CLdapServerCfg::FindServerCfg"); for(ple = m_listhead.Flink; (ple != &m_listhead) && (pMatch == NULL); ple = ple->Flink) { CLdapServerCfg *pCandidate = NULL; pCandidate = CONTAINING_RECORD(ple, CLdapServerCfg, m_le); if(pCandidate->fMatch( pServerConfig)) { pMatch = pCandidate; } } CatFunctLeaveEx((LPARAM)NULL); return pMatch; } // CLdapServerCfg::FindServerCfg //+------------------------------------------------------------ // // Function: CLdapServerCfg::fMatch // // Synopsis: Determine if this object matches the passed in config // // Arguments: // pServerConfig: config to check against // // Returns: // TRUE: match // FALSE: no match // // History: // jstamerj 1999/06/21 12:45:10: Created. // //------------------------------------------------------------- BOOL CLdapServerCfg::fMatch( PLDAPSERVERCONFIG pServerConfig) { BOOL fRet; CatFunctEnterEx((LPARAM)this, "CLdapServerCfg::fMatch"); if((pServerConfig->dwPort != m_ServerConfig.dwPort) || (pServerConfig->bt != m_ServerConfig.bt) || (lstrcmpi(pServerConfig->szHost, m_ServerConfig.szHost) != 0) || (lstrcmpi(pServerConfig->szNamingContext, m_ServerConfig.szNamingContext) != 0) || (lstrcmpi(pServerConfig->szAccount, m_ServerConfig.szAccount) != 0) || (lstrcmpi(pServerConfig->szPassword, m_ServerConfig.szPassword) != 0)) { fRet = FALSE; } else { fRet = TRUE; } DebugTrace((LPARAM)this, "returning %08lx", fRet); CatFunctLeaveEx((LPARAM)this); return fRet; } // CLdapServerCfg::fMatch //+------------------------------------------------------------ // // Function: CLdapServerCfg::LogStateChangeEvent // // Synopsis: Log an eventlog for a state change event // // Arguments: // pISMTPServerEx: interface for logging // connstate: new connstate // pszHost: host for connection // dwPort: port of connection // // Returns: Nothing // // History: // jstamerj 2001/12/13 01:43:13: Created. // //------------------------------------------------------------- VOID CLdapServerCfg::LogStateChangeEvent( IN ISMTPServerEx *pISMTPServerEx, IN CONN_STATE connstate, IN LPSTR pszHost, IN DWORD dwPort) { DWORD idEvent = 0; LPCSTR rgSubStrings[2]; CHAR szPort[16]; _snprintf(szPort, sizeof(szPort), "%d", dwPort); rgSubStrings[0] = pszHost; rgSubStrings[1] = szPort; switch(connstate) { case CONN_STATE_CONNECTED: idEvent = CAT_EVENT_CNFGMGR_CONNECTED; break; case CONN_STATE_DOWN: idEvent = CAT_EVENT_CNFGMGR_DOWN; break; case CONN_STATE_RETRY: idEvent = CAT_EVENT_CNFGMGR_RETRY; break; default: break; } if(idEvent) { CatLogEvent( pISMTPServerEx, idEvent, 2, rgSubStrings, S_OK, pszHost, LOGEVENT_FLAG_ALWAYS, LOGEVENT_LEVEL_MEDIUM); } } //+------------------------------------------------------------ // // Function: CCfgConnection::Connect // // Synopsis: Cfg wrapper for the Connect call. // // Arguments: None // // Returns: // S_OK: Success // CAT_E_DBCONNECTION (or whatever CBatchLdapConnection::Connect returns) // // History: // jstamerj 2000/04/13 17:44:43: Created. // //------------------------------------------------------------- HRESULT CCfgConnection::Connect() { HRESULT hr = S_OK; ULARGE_INTEGER ft; CONN_STATE connstate; CatFunctEnterEx((LPARAM)this, "CCfgConnection::Connect"); connstate = m_pCLdapServerCfg->CurrentState(); if(connstate == CONN_STATE_DOWN) { DebugTrace((LPARAM)this, "Not connecting because %s:%d is down", m_szHost, m_dwPort); hr = CAT_E_DBCONNECTION; ERROR_LOG("m_pCLdapServerCfg->CurrentState"); goto CLEANUP; } ft = m_pCLdapServerCfg->GetCurrentTime(); hr = CBatchLdapConnection::Connect(); if(FAILED(hr)) { connstate = CONN_STATE_DOWN; m_pCLdapServerCfg->IncrementFailedCount(); ERROR_LOG("CBatchLdapConnection::Connect"); } else { connstate = CONN_STATE_CONNECTED; m_pCLdapServerCfg->ResetFailedCount(); } // // Update the connection state while inside CLdapConnectionCache's // lock. This will prevent a succeeding thread from attempting // another connection to the GC right after CLdapConnectionCache // releases its lock. Contact msanna for more details. // m_pCLdapServerCfg->UpdateConnectionState( GetISMTPServerEx(), &ft, connstate); CLEANUP: DebugTrace((LPARAM)this, "returning %08lx", hr); CatFunctLeaveEx((LPARAM)this); return hr; } // CCfgConnection::Connect //+------------------------------------------------------------ // // Function: CCfgConnection::AsyncSearch // // Synopsis: Wrapper around AsyncSearch -- keep track of the # of // pending searches and connection state. // // Arguments: See CLdapConnection::AsyncSearch // // Returns: // Value returned from CLdapConnection::AsyncSearch // // History: // jstamerj 1999/06/18 13:49:45: Created. // //------------------------------------------------------------- HRESULT CCfgConnection::AsyncSearch( LPCWSTR szBaseDN, int nScope, LPCWSTR szFilter, LPCWSTR szAttributes[], DWORD dwPageSize, LPLDAPCOMPLETION fnCompletion, LPVOID ctxCompletion) { HRESULT hr = S_OK; CatFunctEnterEx((LPARAM)this, "CCfgConnection::AsyncSearch"); m_pCLdapServerCfg->IncrementPendingSearches(); hr = CBatchLdapConnection::AsyncSearch( szBaseDN, nScope, szFilter, szAttributes, dwPageSize, fnCompletion, ctxCompletion); if(FAILED(hr)) { ERROR_LOG("CBatchLdapConnection::AsyncSearch"); m_pCLdapServerCfg->DecrementPendingSearches(); } DebugTrace((LPARAM)this, "returning %08lx", hr); CatFunctLeaveEx((LPARAM)this); return hr; } // CCfgConnection::AsyncSearch //+------------------------------------------------------------ // // Function: CCfgConnection::CallCompletion // // Synopsis: Wrapper around CLdapConnection::CallCompletion. Checks // for server down errors and keeps track of pending searches. // // Arguments: See CLdapConnection::CallCompletion // // Returns: See CLdapConnection::CallCompletion // // History: // jstamerj 1999/06/18 13:58:28: Created. // //------------------------------------------------------------- VOID CCfgConnection::CallCompletion( PPENDING_REQUEST preq, PLDAPMessage pres, HRESULT hrStatus, BOOL fFinalCompletion) { CatFunctEnterEx((LPARAM)this, "CCfgConnection::CallCompletion"); // // The user(s) of CLdapConnection normally try to get a new // connection and reissue their search when AsyncSearch // fails. When opening a new connection fails, CLdapServerCfg // will be notified that the LDAP server is down. We do not // want to call NotifyServerDown() here because the LDAP // server may have just closed this connection due to idle // time (the server may not actually be down). // if(fFinalCompletion) { m_pCLdapServerCfg->DecrementPendingSearches(); } CBatchLdapConnection::CallCompletion( preq, pres, hrStatus, fFinalCompletion); CatFunctLeaveEx((LPARAM)this); } // CCfgConnection::CallCompletion //+------------------------------------------------------------ // // Function: CCfgConnection::NotifyServerDown // // Synopsis: Notify the server config that this connection is down. // If we already notified it, don't do so again. // // Arguments: NONE // // Returns: NOTHING // // History: // jstamerj 1999/06/18 14:07:48: Created. // //------------------------------------------------------------- VOID CCfgConnection::NotifyServerDown() { BOOL fNotify; CatFunctEnterEx((LPARAM)this, "CCfgConnection::NotifyServerDown"); m_sharelock.ShareLock(); if(m_connstate == CONN_STATE_DOWN) { // // We already notified m_pCLdapServerCfg the server went // down. Don't repeteadly call it // fNotify = FALSE; m_sharelock.ShareUnlock(); } else { m_sharelock.ShareUnlock(); m_sharelock.ExclusiveLock(); // // Double check // if(m_connstate == CONN_STATE_DOWN) { fNotify = FALSE; } else { m_connstate = CONN_STATE_DOWN; fNotify = TRUE; } m_sharelock.ExclusiveUnlock(); } if(fNotify) m_pCLdapServerCfg->UpdateConnectionState( GetISMTPServerEx(), NULL, // Current time CONN_STATE_DOWN); CatFunctLeaveEx((LPARAM)this); } // CCfgConnection::NotifyServerDown //+------------------------------------------------------------ // // Function: CatStoreInitGlobals // // Synopsis: This is called to initialize global variables in the // store layer. // // Arguments: NONE // // Returns: // S_OK: Success // // History: // jstamerj 1999/06/22 11:03:53: Created. // //------------------------------------------------------------- HRESULT CatStoreInitGlobals() { CatFunctEnterEx((LPARAM)NULL, "CatStoreInitGlobals"); CLdapServerCfg::GlobalInit(); CLdapConnection::GlobalInit(); CatFunctLeaveEx((LPARAM)NULL); return S_OK; } // CatStoreInitGlobals //+------------------------------------------------------------ // // Function: CatStoreDeinitGlobals // // Synopsis: Called to deinitialize store layer globals -- called once // only when CatStoreInitGlobals succeeds // // Arguments: NONE // // Returns: NOTHING // // History: // jstamerj 1999/06/22 11:05:44: Created. // //------------------------------------------------------------- VOID CatStoreDeinitGlobals() { CatFunctEnterEx((LPARAM)NULL, "CatStoreDeinitGlobals"); // // Nothing to do // CatFunctLeaveEx((LPARAM)NULL); } // CatStoreDeinitGlobals //+------------------------------------------------------------ // // Function: CCfgConnectionCache::GetConnection // // Synopsis: Same as CLdapConnectionCache::GetConnection, except // retrieves a CCfgConnection instead of a CLdapConnection. // // Arguments: // ppConn: out parameter for new connection // pServerConfig: desired configuration // pCLdapServerConfig: Pointer to config object // // Returns: // S_OK: Success // // History: // jstamerj 1999/12/20 16:49:12: Created. // //------------------------------------------------------------- HRESULT CCfgConnectionCache::GetConnection( CCfgConnection **ppConn, PLDAPSERVERCONFIG pServerConfig, CLdapServerCfg *pCLdapServerConfig) { HRESULT hr = S_OK; CatFunctEnterEx((LPARAM)this, "CCfgConnectionCache::GetConnection"); hr = CBatchLdapConnectionCache::GetConnection( (CBatchLdapConnection **)ppConn, pServerConfig->szHost, pServerConfig->dwPort, pServerConfig->szNamingContext, pServerConfig->szAccount, pServerConfig->szPassword, pServerConfig->bt, (PVOID) pCLdapServerConfig); // pCreateContext if(FAILED(hr)) { ERROR_LOG("CBatchldapConnection::GetConnection"); } CatFunctLeaveEx((LPARAM)this); return hr; } // CCfgConnectionCache::GetConnection //+------------------------------------------------------------ // // Function: CCfgConnectionCache::CreateCachedLdapConnection // // Synopsis: Create a CCfgConnection (Called by GetConnection only) // // Arguments: See CLdapConnectionCache::CreateCachedLdapConnection // // Returns: // Connection ptr if successfull. // NULL if unsuccessfull. // // History: // jstamerj 1999/12/20 16:57:49: Created. // //------------------------------------------------------------- CCfgConnectionCache::CCachedLdapConnection * CCfgConnectionCache::CreateCachedLdapConnection( LPSTR szHost, DWORD dwPort, LPSTR szNamingContext, LPSTR szAccount, LPSTR szPassword, LDAP_BIND_TYPE bt, PVOID pCreateContext) { HRESULT hr = S_OK; CCfgConnection *pret; CatFunctEnterEx((LPARAM)this, "CCfgConnectionCache::CreateCachedLdapConnection"); pret = new CCfgConnection( szHost, dwPort, szNamingContext, szAccount, szPassword, bt, this, (CLdapServerCfg *)pCreateContext); if(pret) { hr = pret->HrInitialize(); if(FAILED(hr)) { ERROR_LOG("pret->HrInitialize"); pret->Release(); pret = NULL; } } else { hr = E_OUTOFMEMORY; ERROR_LOG("new CCfgConnection"); } CatFunctLeaveEx((LPARAM)this); return pret; } // CCfgConnectionCache::CreateCachedLdapConnection