|
|
//
// 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_
|