Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

716 lines
20 KiB

//+------------------------------------------------------------
//
// 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__