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.
 
 
 
 
 
 

647 lines
21 KiB

//
// ldapconn.h -- This file contains the class definitions for:
// CLdapConnection
// CLdapConnectionCache
//
// Created:
// Dec 31, 1996 -- Milan Shah (milans)
//
// Changes:
//
#ifndef _LDAPCONN_H_
#define _LDAPCONN_H_
#include <transmem.h>
#include "winldap.h"
#include "rwex.h"
#include "spinlock.h"
#include "catperf.h"
#include "catdefs.h"
//
// Timeout value (in seconds) to pass into ldap_result
//
#define LDAPCONN_DEFAULT_RESULT_TIMEOUT (2*60) // 2 Minutes
#define DEFAULT_LDAP_REQUEST_TIME_LIMIT (10*60) // 10 minutes
#define LDAPCONN_RESULT_TIMEOUT_KEY "System\\CurrentControlSet\\Services\\SMTPSVC\\Parameters"
#define LDAPCONN_RESULT_TIMEOUT_VALUE "LdapResultTimeout"
#define LDAP_REQUEST_TIME_LIMIT_VALUE "LdapRequestTimeLimit"
typedef VOID LDAPRESULT;
typedef PVOID PLDAPRESULT;
typedef VOID LDAPENTRY;
typedef PVOID PLDAPENTRY;
enum LDAP_BIND_TYPE {
BIND_TYPE_NONE,
BIND_TYPE_SIMPLE,
BIND_TYPE_GENERIC,
BIND_TYPE_CURRENTUSER
};
class CLdapConnection;
typedef VOID (*LPLDAPCOMPLETION)(
LPVOID ctx,
DWORD dwNumResults,
ICategorizerItemAttributes **rgpICatItemAttrs,
HRESULT hr,
BOOL fFinalCompletion);
DWORD WINAPI LdapCompletionThread(LPVOID ctx);
VOID LogLdapError(
IN ISMTPServerEx *pISMTPServerEx,
IN ULONG ulLdapErr,
IN LPSTR pszHost,
IN LPSTR pszCall);
CatDebugClass(CLdapConnection)
{
public:
virtual HRESULT HrInitialize();
virtual DWORD AddRef()
{
return InterlockedIncrement((PLONG)&m_dwRefCount);
}
virtual DWORD Release();
virtual VOID ReleaseAndWaitForDestruction();
virtual VOID FinalRelease();
virtual DWORD GetRefCount()
{
return m_dwRefCount;
}
virtual LPSTR GetNamingContext() { // Return the naming context
return( m_szNamingContext ); // of the connection
}
virtual LPWSTR GetNamingContextW() {
return( m_wszNamingContext );
}
virtual LPSTR GetHostName() {
return( m_szHost );
}
virtual DWORD GetPort() {
return( m_dwPort );
}
virtual LPSTR GetAccount() {
return( m_szAccount );
}
virtual LPSTR GetPassword() {
return( m_szPassword );
}
virtual LDAP_BIND_TYPE GetBindType() {
return( m_bt );
}
virtual HRESULT Search( // Look up objects matching
LPCSTR szBaseDN, // specified criteria in the
int nScope, // DS
LPCSTR szFilter,
LPCSTR *rgszAttributes,
PLDAPRESULT *ppResult);
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);
//
// Same as above with UTF8 search filter
//
virtual HRESULT AsyncSearch(
LPCWSTR szBaseDN, // objects matching specified
int nScope, // criteria in the DS. The
LPCSTR szFilterUTF8, // results are passed to
LPCWSTR szAttributes[], // fnCompletion when they
DWORD dwPageSize, // Optinal page size
LPLDAPCOMPLETION fnCompletion, // become available.
LPVOID ctxCompletion);
//
// Same as above with UTF8 search filter and base DN
//
virtual HRESULT AsyncSearch(
LPCSTR szBaseDN, // objects matching specified
int nScope, // criteria in the DS. The
LPCSTR szFilterUTF8, // results are passed to
LPCWSTR szAttributes[], // fnCompletion when they
DWORD dwPageSize, // Optinal page size
LPLDAPCOMPLETION fnCompletion, // become available.
LPVOID ctxCompletion);
virtual VOID CancelAllSearches( // Cancels all pending searches
HRESULT hr = HRESULT_FROM_WIN32(ERROR_CANCELLED),
ISMTPServer *pISMTPServer = NULL);
VOID ProcessAsyncResult( // Method to process results
PLDAPMessage pres, // of AsyncSearch requests
DWORD dwLdapError,
BOOL *pfTerminateIndicator);
friend DWORD WINAPI LdapCompletionThread(// Friend function to
LPVOID ctx); // handle AsyncSearch
// completions.
virtual HRESULT GetFirstEntry( // Get first entry from the
PLDAPRESULT pResult, // search result returned
PLDAPENTRY *ppEntry); // by ::Search
virtual HRESULT GetNextEntry( // Get the next entry from
PLDAPRESULT pResult, // the search result
PLDAPENTRY *ppEntry);
virtual HRESULT GetAttributeValues( // Get an entry's attribute
PLDAPENTRY pEntry, // values
LPCSTR szAttribute,
LPSTR *prgszValues[]);
static VOID FreeResult( // Free a search result
PLDAPRESULT pResult);
virtual VOID FreeValues( // Free values returned by
LPSTR rgszValues[]); // ::GetAttributeValues
virtual HRESULT Add( // Add a set of new
LPCSTR szDN, // attributes to an existing
LPCSTR *rgszAttributes, // object in the DS
LPCSTR *rgrgszValues[]) {
return ( ModifyAttributes(
LDAP_MOD_ADD,
szDN,
rgszAttributes,
rgrgszValues) );
}
virtual HRESULT Delete( // Delete attributes from
LPCSTR szDN, // an existing object in the
LPCSTR *rgszAttributes) { // DS
return ( ModifyAttributes(
LDAP_MOD_DELETE,
szDN,
rgszAttributes,
NULL) );
}
virtual HRESULT Update( // Update attributes on an
LPCSTR szDN, // existing object in the DS
LPCSTR rgszAttributes[],
LPCSTR *rgrgszValues[]) {
return ( ModifyAttributes(
LDAP_MOD_REPLACE,
szDN,
rgszAttributes,
rgrgszValues) );
}
//
// Returns an ISMTPServerEx interface for logging events or
// NULL if none are available
//
virtual ISMTPServerEx * GetISMTPServerEx() = 0;
LPSTR SzHost()
{
return m_szHost;
}
static VOID GlobalInit()
{
//
// initialize LDAP perf block
//
ZeroMemory(&g_LDAPPerfBlock, sizeof(g_LDAPPerfBlock));
m_ldaptimeout.tv_sec = LDAPCONN_DEFAULT_RESULT_TIMEOUT;
m_ldaptimeout.tv_usec = 0;
m_dwLdapRequestTimeLimit = DEFAULT_LDAP_REQUEST_TIME_LIMIT;
//
// read configurable static members from the registry
//
InitializeFromRegistry();
}
static VOID InitializeFromRegistry();
protected:
CLdapConnection( // The constructor and
LPSTR szHost, // destructor are protected
DWORD dwPort,
LPSTR szNamingContext, // since only derived classes
LPSTR szAccount, // can create/delete these
LPSTR szPassword,
LDAP_BIND_TYPE BindType);
virtual ~CLdapConnection();
virtual HRESULT Connect(); // Create/Delete connection
// to LDAP host
virtual VOID Disconnect();
virtual VOID Invalidate();
virtual BOOL IsValid();
virtual DWORD BindToHost(
PLDAP pldap,
LPSTR szAccount,
LPSTR szPassword);
virtual BOOL IsEqual( // Return true if the
LPSTR szHost, // object member variables
DWORD dwPort,
LPSTR szNamingContext, // match the passed in
LPSTR szAccount, // values
LPSTR szPassword,
LDAP_BIND_TYPE BindType);
virtual HRESULT ModifyAttributes( // Helper function for
int nOperation, // ::Add, ::Delete, and
LPCSTR szDN, // ::Update public functions
LPCSTR rgszAttributes[],
LPCSTR *rgrgszValues[]);
virtual HRESULT LdapErrorToHr(
DWORD dwLdapError);
VOID SetTerminateIndicatorTrue()
{
BOOL *pfTerminate;
m_fTerminating = TRUE;
pfTerminate = (BOOL *) InterlockedExchangePointer(
(PVOID *) &m_pfTerminateCompletionThreadIndicator,
NULL);
if(pfTerminate)
*pfTerminate = TRUE;
}
VOID CancelExpiredSearches(HRESULT hr);
ULONG GetDefaultNamingContext(); // Helper function to
// get the default
// naming context from
// the server we are
// connected to.
static LDAP_TIMEVAL m_ldaptimeout;
static DWORD m_dwLdapRequestTimeLimit;
DWORD m_dwPort;
char m_szHost[CAT_MAX_DOMAIN];
char m_szNamingContext[CAT_MAX_DOMAIN];
WCHAR m_wszNamingContext[CAT_MAX_DOMAIN];
char m_szAccount[CAT_MAX_LOGIN];
char m_szPassword[CAT_MAX_PASSWORD];
#define SIGNATURE_LDAPCONN ((DWORD) 'CadL')
#define SIGNATURE_LDAPCONN_INVALID ((DWORD) 'XadL')
DWORD m_dwSignature;
DWORD m_dwRefCount;
DWORD m_dwDestructionWaiters;
HANDLE m_hShutdownEvent;
LDAP_BIND_TYPE m_bt;
PLDAP GetPLDAP()
{
if(m_pCPLDAPWrap)
return m_pCPLDAPWrap->GetPLDAP();
else
return NULL;
}
CPLDAPWrap *m_pCPLDAPWrap;
BOOL m_fDefaultNamingContext;
//
// Unfortunately, our RFC1823 LDAP API provides no access to the
// socket handle which we can register with a completion port. So,
// if one or more async search request is issued, we have to burn a
// thread to await its completion.
//
//
// This spin lock protects access to the pending request list as
// well as m_dwStatusFlags
//
SPIN_LOCK m_spinlockCompletion;
// CRITICAL_SECTION m_cs;
//
// jstamerj 980501 15:56:27:
// Reader/writer lock so that we wait for all calls in
// ldap_search_ext before cancelling all pending searches
//
CExShareLock m_ShareLock;
DWORD m_idCompletionThread;
HANDLE m_hCompletionThread;
HANDLE m_hOutstandingRequests;
BOOL *m_pfTerminateCompletionThreadIndicator;
BOOL m_fTerminating;
BOOL m_fValid;
typedef struct _PendingRequest {
int msgid;
LPLDAPCOMPLETION fnCompletion;
LPVOID ctxCompletion;
LIST_ENTRY li;
//
// Parameters for paged searches
//
DWORD dwPageSize;
PLDAPSearch pldap_search;
DWORD dwTickCount;
} PENDING_REQUEST, *PPENDING_REQUEST;
LIST_ENTRY m_listPendingRequests;
BOOL m_fCancel;
//
// The following three functions must be called inside an external
// lock (m_spinlockcompletion)
//
VOID NotifyCancel()
{
m_fCancel = TRUE;
}
VOID ClearCancel()
{
m_fCancel = FALSE;
}
BOOL CancelOccured()
{
return m_fCancel;
}
VOID SetPort(DWORD dwPort)
{
m_dwPort = (dwPort != 0) ? dwPort : LDAP_PORT;
}
BOOL fIsPortEqual(DWORD dwPort)
{
return (m_dwPort == ((dwPort != 0) ? dwPort : LDAP_PORT));
}
virtual HRESULT CreateCompletionThreadIfNeeded();
virtual VOID SetTerminateCompletionThreadIndicator(
BOOL *pfTerminateCompletionThreadIndicator);
virtual VOID InsertPendingRequest(
PPENDING_REQUEST preq);
virtual VOID RemovePendingRequest(
PPENDING_REQUEST preq);
virtual VOID CallCompletion(
PPENDING_REQUEST preq,
PLDAPMessage pres,
HRESULT hrStatus,
BOOL fFinalCompletion);
VOID AbandonRequest(
PPENDING_REQUEST preq)
{
ldap_abandon(
GetPLDAP(),
preq->msgid);
if(preq->pldap_search)
ldap_search_abandon_page(
GetPLDAP(),
preq->pldap_search);
INCREMENT_LDAP_COUNTER(AbandonedSearches);
DECREMENT_LDAP_COUNTER(PendingSearches);
}
VOID LogLdapError(
IN ULONG ulLdapErr,
IN LPSTR pszFormatString,
...);
static VOID LogLdapError(
IN ISMTPServerEx *pISMTPServerEx,
IN ULONG ulLdapErr,
IN LPSTR pszFormatString,
...);
};
//
// For the hash function to work correctly, the table size must be a power of
// two. This is just an efficiency trick; there is nothing fundamentally
// wrong with using some other size, except that the hash function would have
// to use an expensive MODULO operator instead of a cheap AND.
//
#define LDAP_CONNECTION_CACHE_TABLE_SIZE 256
#define MAX_LDAP_CONNECTIONS_PER_HOST_KEY "System\\CurrentControlSet\\Services\\SMTPSVC\\Parameters"
#define MAX_LDAP_CONNECTIONS_PER_HOST_VALUE "MaxLdapConnections"
class CLdapConnectionCache
{
public:
CLdapConnectionCache(
ISMTPServerEx *pISMTPServerEx); // Constructor
~CLdapConnectionCache(); // Destructor
HRESULT GetConnection( // Given LDAP config info,
CLdapConnection **ppConn,
LPSTR szHost, // retrieve a connection to
DWORD dwPort,
LPSTR szNamingContext, // the LDAP host.
LPSTR szAccount,
LPSTR szPassword,
LDAP_BIND_TYPE bt,
PVOID pCreateContext = NULL);
VOID CancelAllConnectionSearches(
ISMTPServer *pISMTPServer = NULL);
//
// It is intended for there to be a single, global, instance of
// a CLdapConnectionCache object, serving multiple instances of
// CEmailIDLdapStore. Each instance of CEmailIDLdapStore needs to
// call AddRef() and Release() in its constructor/destructor, so that
// the connection cache knows to clean up connections in the cache
// when the ref count goes to 0.
//
VOID AddRef();
VOID Release();
private:
//
// An internally utility function to release a connection
//
VOID ReleaseConnectionInternal(
CLdapConnection *pConnection,
BOOL fLockRequired);
LONG m_cRef;
protected:
class CCachedLdapConnection : public CLdapConnection {
public:
CCachedLdapConnection(
LPSTR szHost,
DWORD dwPort,
LPSTR szNamingContext,
LPSTR szAccount,
LPSTR szPassword,
LDAP_BIND_TYPE bt,
CLdapConnectionCache *pCache) :
CLdapConnection(
szHost,
dwPort,
szNamingContext,
szAccount,
szPassword,
bt)
{
m_pCache = pCache;
}
HRESULT Connect() {
return( CLdapConnection::Connect() );
}
VOID Disconnect() {
CLdapConnection::Disconnect();
}
VOID Invalidate() {
CLdapConnection::Invalidate();
}
BOOL IsValid() {
return( CLdapConnection::IsValid() );
}
BOOL IsEqual(
LPSTR szHost,
DWORD dwPort,
LPSTR szNamingContext,
LPSTR szAccount,
LPSTR szPassword,
LDAP_BIND_TYPE BindType) {
return( CLdapConnection::IsEqual(
szHost, dwPort, szNamingContext, szAccount,
szPassword, BindType) );
}
ISMTPServerEx *GetISMTPServerEx()
{
return (m_pCache) ? m_pCache->GetISMTPServerEx() : NULL;
}
DWORD Release();
LIST_ENTRY li;
CLdapConnectionCache *m_pCache;
};
virtual VOID RemoveFromCache(
CCachedLdapConnection *pConn);
virtual CCachedLdapConnection *CreateCachedLdapConnection(
LPSTR szHost,
DWORD dwPort,
LPSTR szNamingContext,
LPSTR szAccount,
LPSTR szPassword,
LDAP_BIND_TYPE bt,
PVOID pCreateContext)
{
CCachedLdapConnection *pret;
pret = new CCachedLdapConnection(
szHost,
dwPort,
szNamingContext,
szAccount,
szPassword,
bt,
this);
if(pret)
if(FAILED(pret->HrInitialize())) {
pret->Release();
pret = NULL;
}
return pret;
}
ISMTPServerEx *GetISMTPServerEx()
{
return m_pISMTPServerEx;
}
private:
//
// We want to support multiple connections per host, up to a maximum
// of m_cMaxHostConnections. We achieve this in a simple way by
// keeping a per-cache m_nConnectionSkipCount. Every time we are
// searching for a cached connection to a host in, we skip
// m_nNextConnectionSkipCount cached connections. Every time we
// find a cached connection, we bump up
// m_nNextCachedConnectionSkipCount by 1 modulo m_cMaxHostConnections.
// This means we'll round robin through m_cMaxHostConnections
// connections per host.
//
ISMTPServerEx *m_pISMTPServerEx;
LONG m_nNextConnectionSkipCount;
LONG m_cMaxHostConnections;
LONG m_cCachedConnections;
LIST_ENTRY m_rgCache[ LDAP_CONNECTION_CACHE_TABLE_SIZE ];
CExShareLock m_rgListLocks[ LDAP_CONNECTION_CACHE_TABLE_SIZE ];
LONG m_rgcCachedConnections[ LDAP_CONNECTION_CACHE_TABLE_SIZE ];
VOID InitializeFromRegistry();
unsigned short Hash(
LPSTR szConnectionName);
friend class CLdapConnectionCache::CCachedLdapConnection;
friend class CBatchLdapConnection;
};
#endif // _LDAPCONN_H_