|
|
//+------------------------------------------------------------
//
// Copyright (C) 1999, Microsoft Corporation
//
// File: cnfgmgr.h
//
// Contents: Declaration of classes related to the handling on
// different LDAP host configurations.
// This includes LDAP failover and load balancing.
//
// Classes:
// CLdapCfgMgr
// CLdapCfg
// CLdapHost
// CCfgConnectionCache
// CCfgConnection
//
// Functions:
//
// History:
// jstamerj 1999/06/15 14:49:52: Created.
//
//-------------------------------------------------------------
#ifndef __CNFGMGR_H__
#define __CNFGMGR_H__
#include <windows.h>
#include "asyncctx.h"
#include <baseobj.h>
#include <ntdsapi.h>
#include <dsgetdc.h>
#include <lmcons.h>
#include <lmapibuf.h>
#include <baseobj.h>
#include "asyncctx.h"
class CLdapCfg; class CLdapServerCfg; class CCfgConectionCache; class CCfgConnection;
#define CONN_RETRY_TIME (5*60) // 5 Minutes
typedef DWORD CONN_PRIORITY;
enum CONN_STATE { CONN_STATE_INITIAL, CONN_STATE_CONNECTED, CONN_STATE_DOWN, CONN_STATE_RETRY, };
typedef struct _tagLdapServerConfig { DWORD dwPort; CONN_PRIORITY pri; LDAP_BIND_TYPE bt; CHAR szHost[CAT_MAX_DOMAIN]; CHAR szNamingContext[CAT_MAX_DOMAIN]; CHAR szAccount[CAT_MAX_LOGIN]; CHAR szPassword[CAT_MAX_PASSWORD]; } LDAPSERVERCONFIG, *PLDAPSERVERCONFIG;
typedef DWORD LDAPSERVERCOST, *PLDAPSERVERCOST;
//
// Connection costs:
//
// 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.
//
#define DEFAULT_COST_CONNECTED_LOCAL 0
#define DEFAULT_COST_CONNECTED_REMOTE 2
#define DEFAULT_COST_INITIAL_LOCAL 4
#define DEFAULT_COST_INITIAL_REMOTE 8
#define DEFAULT_COST_RETRY_LOCAL 6
#define DEFAULT_COST_RETRY_REMOTE 10
#define COST_DOWN_LOCAL 0x80000000
#define COST_DOWN_REMOTE 0x80000000
#define COST_TOO_HIGH_TO_CONNECT 0x80000000
//
// Registry key and value names for initializing GC cost values from the registry
//
#define GC_COST_PARAMETERS_KEY "System\\CurrentControlSet\\Services\\SMTPSVC\\Parameters"
#define GC_COST_CONNECTED_LOCAL_VALUE "GCCostConnectedLocal"
#define GC_COST_CONNECTED_REMOTE_VALUE "GCCostConnectedRemote"
#define GC_COST_INITIAL_LOCAL_VALUE "GCCostInitialLocal"
#define GC_COST_INITIAL_REMOTE_VALUE "GCCostInitialRemote"
#define GC_COST_RETRY_LOCAL_VALUE "GCCostRetryLocal"
#define GC_COST_RETRY_REMOTE_VALUE "GCCostRetryRemote"
//
// The maximum number of threads that will try to connect to a
// connection in CONN_STATE_RETRY:
//
#define MAX_CONNECT_THREADS 1
//
// Requerying of available GC control:
// The code will rebuild the list of available GCs at a hard coded
// time interval. The code will also requery for available GCs after
// a hard coded number of connection failures and a minimum time interval.
//
#define DEFAULT_REBUILD_GC_LIST_MAX_INTERVAL (60*60) // 1 hour
#define DEFAULT_REBUILD_GC_LIST_MAX_FAILURES (100) // 100 connection failures
#define DEFAULT_REBUILD_GC_LIST_MIN_INTERVAL (60*5) // 5 minutes
//
// Registry key and value names for initializing values from the registry
//
#define REBUILD_GC_LIST_PARAMETERS_KEY "System\\CurrentControlSet\\Services\\SMTPSVC\\Parameters"
#define REBUILD_GC_LIST_MAX_INTERVAL_VALUE "RebuildGCListMaxInterval"
#define REBUILD_GC_LIST_MAX_FAILURES_VALUE "RebuildGCListMaxFailures"
#define REBUILD_GC_LIST_MIN_INTERVAL_VALUE "RebuildGCListMinInterval"
//
// An LDAP connection cache object that creates CCfgConnection objects
//
class CCfgConnectionCache : public CBatchLdapConnectionCache { public: CCfgConnectionCache( ISMTPServerEx *pISMTPServerEx) : CBatchLdapConnectionCache(pISMTPServerEx) { } HRESULT GetConnection( CCfgConnection **ppConn, PLDAPSERVERCONFIG pServerConfig, CLdapServerCfg *pCLdapServerConfig);
CCachedLdapConnection *CreateCachedLdapConnection( LPSTR szHost, DWORD dwPort, LPSTR szNamingContext, LPSTR szAccount, LPSTR szPassword, LDAP_BIND_TYPE bt, PVOID pCreateContext); private: #define SIGNATURE_CCFGCONNECTIONCACHE (DWORD)'CCCC'
#define SIGNATURE_CCFGCONNECTIONCACHE_INVALID (DWORD)'CCCX'
DWORD m_dwSignature; };
//
// CLdapCfgMgr is a wrapper around CLdapCfg. It contains thread save
// code to build a new CLdapCfg object with a new list of available
// LDAP servers
//
CatDebugClass(CLdapCfgMgr), public CBaseObject { public: CLdapCfgMgr( ISMTPServerEx *pISMTPServerEx, BOOL fAutomaticConfigUpdate, ICategorizerParameters *pICatParams, LDAP_BIND_TYPE bt = BIND_TYPE_NONE, LPSTR pszAccount = NULL, LPSTR pszPassword = NULL, LPSTR pszNamingContext = NULL);
//
// Reads parameters from the registry, setting member variables
// when configuration data is available
//
VOID InitializeFromRegistry();
//
// Build a list of all available GCs and initialize
// This function may be called multiple times (necessary if the
// available GCs change)
//
HRESULT HrInit( BOOL fRediscoverGCs = FALSE);
//
// Initialize using a specified list of avialable LDAP servers
// THis function may be called more than once
//
HRESULT HrInit( DWORD dwcServers, PLDAPSERVERCONFIG prgServerConfig);
//
// Get a connection
//
HRESULT HrGetConnection( CCfgConnection **ppConn);
//
// Called very often to update the GC configuration if warranted.
//
HRESULT HrUpdateConfigurationIfNecessary();
//
// Wrapper to cancel all searches on all connections
//
VOID CancelAllConnectionSearches( ISMTPServer *pIServer) { m_LdapConnectionCache.CancelAllConnectionSearches( pIServer); }
ISMTPServerEx * GetISMTPServerEx() { return m_pISMTPServerEx; }
private: ~CLdapCfgMgr();
HRESULT 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 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 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);
BOOL fReadyForUpdate();
LPSTR SzConnectNameFromDomainControllerInfo( PDS_DOMAIN_CONTROLLER_INFO_2 pDCInfo) { if(pDCInfo->DnsHostName) return pDCInfo->DnsHostName; else if(pDCInfo->NetbiosName) return pDCInfo->NetbiosName; else return NULL; }
VOID LogCnfgInit(); VOID LogCnfgEntry(PLDAPSERVERCONFIG pConfig); private: #define SIGNATURE_CLDAPCFGMGR (DWORD)'MCLC'
#define SIGNATURE_CLDAPCFGMGR_INVALID (DWORD)'MCLX'
DWORD m_dwSignature; BOOL m_fAutomaticConfigUpdate; DWORD m_dwUpdateInProgress; ULARGE_INTEGER m_ulLastUpdateTime; CExShareLock m_sharelock; CLdapCfg *m_pCLdapCfg; DWORD m_dwRebuildGCListMaxInterval; DWORD m_dwRebuildGCListMaxFailures; DWORD m_dwRebuildGCListMinInterval;
//
// Default configuration to use with automatic host selection
//
LDAP_BIND_TYPE m_bt; CHAR m_szNamingContext[CAT_MAX_DOMAIN]; CHAR m_szAccount[CAT_MAX_LOGIN]; CHAR m_szPassword[CAT_MAX_PASSWORD];
ICategorizerParameters *m_pICatParams; ISMTPServerEx *m_pISMTPServerEx;
CCfgConnectionCache m_LdapConnectionCache; };
//
// CLdapCfg contains the configuration of a group of LDAP servers at
// one point in time. The group of LDAP servers may not be changed
// (without creating a new CLdapCfg object)
//
CatDebugClass(CLdapCfg), public CBaseObject { public: CLdapCfg( ISMTPServerEx *pISMTPServerEx);
void * operator new(size_t size, DWORD dwcServers); //
// HrInit should only be called once per object
//
HRESULT HrInit( DWORD dwcServers, PLDAPSERVERCONFIG prgServerConfig, CLdapCfg *pCLdapCfgOld);
//
// Get a connection
//
HRESULT HrGetConnection( CCfgConnection **ppConn, CCfgConnectionCache *pLdapConnectionCache);
DWORD DwNumConnectionFailures() { return m_dwcConnectionFailures; } DWORD DwNumServers() { return m_dwcServers; } ISMTPServerEx * GetISMTPServerEx() { return m_pISMTPServerEx; }
private: ~CLdapCfg();
VOID ShuffleArray();
private: #define SIGNATURE_CLDAPCFG (DWORD)'fCLC'
#define SIGNATURE_CLDAPCFG_INVALID (DWORD)'fCLX'
DWORD m_dwSignature; DWORD m_dwInc; CExShareLock m_sharelock; // Protects m_prgpCLdapServerCfg
DWORD m_dwcServers; DWORD m_dwcConnectionFailures; CLdapServerCfg **m_prgpCLdapServerCfg; ISMTPServerEx *m_pISMTPServerEx; };
//
// CLdapServerCfg maintains information on the state of one LDAP
// server/port
//
CatDebugClass(CLdapServerCfg) { public: static VOID GlobalInit() { InitializeListHead(&m_listhead); InitializeFromRegistry(); } static VOID InitializeFromRegistry();
static HRESULT GetServerCfg( IN ISMTPServerEx *pISMTPServerEx, IN PLDAPSERVERCONFIG pServerConfig, OUT CLdapServerCfg **ppCLdapServerCfg);
LONG AddRef() { return InterlockedIncrement(&m_lRefCount); } LONG Release() { LONG lRet; lRet = InterlockedDecrement(&m_lRefCount); if(lRet == 0) { //
// Remove object from global list and destroy
//
m_listlock.ExclusiveLock();
if(m_lRefCount > 0) { //
// Somebody grabbed this object out of the global list
// and AddRef'd it. Abort deletion.
//
} else { RemoveEntryList(&m_le); delete this; } m_listlock.ExclusiveUnlock(); } return lRet; }
//
// Get a connection
//
HRESULT HrGetConnection( ISMTPServerEx *pISMTPServerEx, CCfgConnection **ppConn, CCfgConnectionCache *pLdapConnectionCache);
VOID Cost( IN ISMTPServerEx *pISMTPServerEx, OUT PLDAPSERVERCOST pCost); VOID IncrementPendingSearches() { DWORD dwcSearches; CatFunctEnterEx((LPARAM)this, "CLdapServerCfg::IncrementPendingSearches"); dwcSearches = (LONG) InterlockedIncrement((PLONG)&m_dwcPendingSearches); DebugTrace((LPARAM)this, "%ld pending searches on connection [%s:%d]", dwcSearches, m_ServerConfig.szHost, m_ServerConfig.dwPort); CatFunctLeaveEx((LPARAM)this); } VOID DecrementPendingSearches() { DWORD dwcSearches; CatFunctEnterEx((LPARAM)this, "CLdapServerCfg::IncrementPendingSearches"); dwcSearches = (DWORD) InterlockedDecrement((PLONG)&m_dwcPendingSearches); DebugTrace((LPARAM)this, "%ld pending searches on connection [%s:%d]", dwcSearches, m_ServerConfig.szHost, m_ServerConfig.dwPort); CatFunctLeaveEx((LPARAM)this); }
VOID UpdateConnectionState( ISMTPServerEx *pISMTPServerEx, ULARGE_INTEGER *pft, CONN_STATE connstate);
VOID IncrementFailedCount() { InterlockedIncrement((PLONG) &m_dwcFailedConnectAttempts); } VOID ResetFailedCount() { InterlockedExchange((PLONG) &m_dwcFailedConnectAttempts, 0); } CONN_STATE CurrentState() { return m_connstate; } ULARGE_INTEGER GetCurrentTime() { ULARGE_INTEGER FileTime;
_ASSERT(sizeof(ULARGE_INTEGER) == sizeof(FILETIME)); GetSystemTimeAsFileTime((LPFILETIME)&FileTime); return FileTime; } private: CLdapServerCfg(); ~CLdapServerCfg();
HRESULT HrInit( PLDAPSERVERCONFIG pServerConfig);
BOOL fReadyForRetry() { // 100 nanoseconds * 10^7 == 1 second
return ((GetCurrentTime().QuadPart - m_ftLastStateUpdate.QuadPart) >= ((LONGLONG)CONN_RETRY_TIME * 10000000)); }
BOOL fMatch( PLDAPSERVERCONFIG pServerConfig);
VOID LogStateChangeEvent( IN ISMTPServerEx *pISMTPServerEx, IN CONN_STATE connstate, IN LPSTR pszHost, IN DWORD dwPort);
static CLdapServerCfg *FindServerCfg( PLDAPSERVERCONFIG pServerConfig);
static BOOL fIsLocalComputer( PLDAPSERVERCONFIG pServerConfig);
private: #define SIGNATURE_CLDAPSERVERCFG (DWORD)'CSLC'
#define SIGNATURE_CLDAPSERVERCFG_INVALID (DWORD)'CSLX'
static DWORD m_dwCostConnectedLocal; static DWORD m_dwCostConnectedRemote; static DWORD m_dwCostInitialLocal; static DWORD m_dwCostInitialRemote; static DWORD m_dwCostRetryLocal; static DWORD m_dwCostRetryRemote;
DWORD m_dwSignature; LONG m_lRefCount; LDAPSERVERCONFIG m_ServerConfig; CExShareLock m_sharelock; CONN_STATE m_connstate; ULARGE_INTEGER m_ftLastStateUpdate; DWORD m_dwcPendingSearches; DWORD m_dwcCurrentConnectAttempts; DWORD m_dwcFailedConnectAttempts;
//
// Member variables to keep/protect a list of CLdapServer objects
//
static CExShareLock m_listlock; static LIST_ENTRY m_listhead; LIST_ENTRY m_le;
BOOL m_fLocalServer; };
//
// An LDAP connection that notifies CLdapServerCfg about state changes
//
class CCfgConnection : public CBatchLdapConnection { #define SIGNATURE_CCFGCONNECTION (DWORD)'oCCC'
#define SIGNATURE_CCFGCONNECTION_INVALID (DWORD)'oCCX'
public: CCfgConnection( LPSTR szHost, DWORD dwPort, LPSTR szNamingContext, LPSTR szAccount, LPSTR szPassword, LDAP_BIND_TYPE bt, CLdapConnectionCache *pCache, CLdapServerCfg *pCLdapServerCfg) : CBatchLdapConnection( szHost, dwPort, szNamingContext, szAccount, szPassword, bt, pCache) { m_dwSignature = SIGNATURE_CCFGCONNECTION; m_pCLdapServerCfg = pCLdapServerCfg; pCLdapServerCfg->AddRef();
m_connstate = CONN_STATE_INITIAL; }
~CCfgConnection() { _ASSERT(m_pCLdapServerCfg); m_pCLdapServerCfg->Release();
_ASSERT(m_dwSignature == SIGNATURE_CCFGCONNECTION); m_dwSignature = SIGNATURE_CCFGCONNECTION_INVALID; }
virtual HRESULT Connect();
virtual HRESULT AsyncSearch( // Asynchronously look up
LPCWSTR szBaseDN, // objects matching specified
int nScope, // criteria in the DS. The
LPCWSTR szFilter, // results are passed to
LPCWSTR szAttributes[], // fnCompletion when they
DWORD dwPageSize, // Optinal page size
LPLDAPCOMPLETION fnCompletion, // become available.
LPVOID ctxCompletion);
private: virtual VOID CallCompletion( PPENDING_REQUEST preq, PLDAPMessage pres, HRESULT hrStatus, BOOL fFinalCompletion);
VOID NotifyServerDown();
private: DWORD m_dwSignature; CLdapServerCfg *m_pCLdapServerCfg; CExShareLock m_sharelock; CONN_STATE m_connstate; };
//+------------------------------------------------------------
//
// Function: CLdapCfgMgr::HrUpdateConfigurationIfNecessary
//
// Synopsis: Check to see if the CLdapCfg should be updated.
// If it should be, do the update.
//
// Arguments: NONE
//
// Returns:
// S_OK: Success
// error from HrInit
//
// History:
// jstamerj 1999/06/29 20:51:23: Created.
//
//-------------------------------------------------------------
inline HRESULT CLdapCfgMgr::HrUpdateConfigurationIfNecessary() { HRESULT hr = S_OK; DWORD dw; BOOL fUpdate;
if(m_fAutomaticConfigUpdate == FALSE) //
// Update is disabled
return S_OK;
//
// See if some other thread is already updating the configuration
// (try to enter the lock)
//
dw = InterlockedExchange((PLONG)&m_dwUpdateInProgress, TRUE);
if(dw == FALSE) { //
// No other thread is updating
//
fUpdate = fReadyForUpdate();
if(fUpdate) { //
// Call HrInit to generate a new CLdapCfg
//
hr = HrInit(TRUE); if(SUCCEEDED(hr)) { //
// Set the last update time
//
GetSystemTimeAsFileTime((LPFILETIME)&m_ulLastUpdateTime); } } //
// Release the lock
//
InterlockedExchange((PLONG)&m_dwUpdateInProgress, FALSE); } return hr; } // CLdapCfgMgr::HrUpdateConfigurationIfNecessary
//+------------------------------------------------------------
//
// Function: CLdapCfgMgr::fReadyForUpdate
//
// Synopsis: Calculates wether or not this object is due for an
// automatic cfg update
//
// Arguments: NONE
//
// Returns:
// TRUE: Yes, it is time for an update
// FALSE: No, an update is not required at this time
//
// History:
// jstamerj 1999/06/30 12:08:35: Created.
//
//-------------------------------------------------------------
inline BOOL CLdapCfgMgr::fReadyForUpdate() { DWORD dwNumConnectionFailures; ULARGE_INTEGER ulCurrentTime;
//
// We need an update when:
// 1) A periodic time interval has ellapsed
// 100 ns * 10^7 == 1 second
//
GetSystemTimeAsFileTime((LPFILETIME)&ulCurrentTime);
if((ulCurrentTime.QuadPart - m_ulLastUpdateTime.QuadPart) >= (ULONGLONG) Int32x32To64((LONG)m_dwRebuildGCListMaxInterval, 10000000)) return TRUE;
//
// We also need an update when:
// 2) We have received more than a set number of connection
// failures on the current configuration and at least a minimum
// time interval has passed
//
// Check for the mimimum time interval
//
if( (ulCurrentTime.QuadPart - m_ulLastUpdateTime.QuadPart) >= (ULONGLONG) Int32x32To64((LONG)m_dwRebuildGCListMinInterval, 10000000)) { //
// Get the number of connection failures
//
m_sharelock.ShareLock(); if(m_pCLdapCfg) { dwNumConnectionFailures = m_pCLdapCfg->DwNumConnectionFailures(); } else { dwNumConnectionFailures = 0; _ASSERT(0 && "HrInit was not called or failed"); }
m_sharelock.ShareUnlock();
if(dwNumConnectionFailures >= m_dwRebuildGCListMaxFailures) return TRUE; }
return FALSE; } // CLdapCfgMgr::fReadyForUpdate
#endif //__CNFGMGR_H__
|