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