mirror of https://github.com/tongzx/nt5src
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.
447 lines
8.6 KiB
447 lines
8.6 KiB
/* ----------------------------------------------------------------------
|
|
|
|
Module: ULS.DLL (Service Provider)
|
|
File: spconn.cpp
|
|
Content: This file contains the ldap connection object.
|
|
History:
|
|
10/15/96 Chu, Lon-Chan [lonchanc]
|
|
Created.
|
|
|
|
Copyright (c) Microsoft Corporation 1996-1997
|
|
|
|
---------------------------------------------------------------------- */
|
|
|
|
#include "ulsp.h"
|
|
#include "spinc.h"
|
|
#include "rpcdce.h"
|
|
|
|
const TCHAR c_szRTPerson[] = TEXT ("RTPerson");
|
|
const TCHAR c_szRTConf[] = TEXT ("Conference");
|
|
|
|
const TCHAR c_szDefClientBaseDN[] = TEXT ("objectClass=RTPerson");
|
|
const TCHAR c_szDefMtgBaseDN[] = TEXT ("objectClass=Conference");
|
|
|
|
const TCHAR c_szDefO[] = TEXT ("Microsoft");
|
|
const TCHAR c_szEmptyString[] = TEXT ("");
|
|
|
|
|
|
SP_CSessionContainer *g_pSessionContainer = NULL;
|
|
|
|
|
|
/* ---------- public methods ----------- */
|
|
|
|
|
|
SP_CSession::
|
|
SP_CSession ( VOID ) :
|
|
m_cRefs (0),
|
|
m_dwSignature (0),
|
|
m_ld (NULL),
|
|
m_fUsed (FALSE)
|
|
{
|
|
::ZeroMemory (&m_ServerInfo, sizeof (m_ServerInfo));
|
|
}
|
|
|
|
|
|
SP_CSession::
|
|
~SP_CSession ( VOID )
|
|
{
|
|
InternalCleanup ();
|
|
}
|
|
|
|
|
|
/* ---------- public methods ----------- */
|
|
|
|
|
|
HRESULT SP_CSession::
|
|
Disconnect ( VOID )
|
|
{
|
|
// if a connection is available, then simply the existing one
|
|
if (m_dwSignature != LDAP_CONN_SIGNATURE)
|
|
{
|
|
return ILS_E_HANDLE;
|
|
}
|
|
|
|
MyAssert (m_cRefs > 0);
|
|
|
|
HRESULT hr = S_OK;
|
|
if (::InterlockedDecrement (&m_cRefs) == 0)
|
|
{
|
|
// m_cRefs == 0 now
|
|
MyAssert (m_ld != NULL);
|
|
|
|
InternalCleanup ();
|
|
hr = S_OK;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/* ---------- protected methods ----------- */
|
|
|
|
|
|
VOID SP_CSession::
|
|
FillAuthIdentity ( SEC_WINNT_AUTH_IDENTITY *pai )
|
|
{
|
|
// Clean it up
|
|
//
|
|
::ZeroMemory (pai, sizeof (*pai));
|
|
|
|
// Fill in NT auth identity
|
|
//
|
|
if ((pai->User = (BYTE *) m_ServerInfo.pszLogonName) != NULL)
|
|
pai->UserLength = lstrlen (m_ServerInfo.pszLogonName);
|
|
|
|
if ((pai->Domain = (BYTE *) m_ServerInfo.pszDomain) != NULL)
|
|
pai->DomainLength = lstrlen (m_ServerInfo.pszDomain);
|
|
|
|
if ((pai->Password = (BYTE *) m_ServerInfo.pszLogonPassword) != NULL)
|
|
pai->PasswordLength = lstrlen (m_ServerInfo.pszLogonPassword);
|
|
|
|
#ifdef _UNICODE
|
|
pai->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
|
|
#else
|
|
pai->Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
|
|
#endif
|
|
}
|
|
|
|
|
|
HRESULT SP_CSession::
|
|
Bind ( BOOL fAbortable )
|
|
{
|
|
ULONG uLdapAuthMethod = LDAP_AUTH_SIMPLE;
|
|
TCHAR *pszLogonName = m_ServerInfo.pszLogonName;
|
|
TCHAR *pszLogonPassword = m_ServerInfo.pszLogonPassword;
|
|
SEC_WINNT_AUTH_IDENTITY ai;
|
|
BOOL fSyncBind = TRUE;
|
|
HRESULT hr = S_OK;
|
|
|
|
switch (m_ServerInfo.AuthMethod)
|
|
{
|
|
default:
|
|
MyAssert (FALSE);
|
|
// Fall through...
|
|
|
|
case ILS_AUTH_ANONYMOUS:
|
|
fSyncBind = FALSE;
|
|
uLdapAuthMethod = LDAP_AUTH_SIMPLE;
|
|
pszLogonName = STR_EMPTY;
|
|
pszLogonPassword = STR_EMPTY;
|
|
break;
|
|
|
|
case ILS_AUTH_CLEAR_TEXT:
|
|
fSyncBind = FALSE;
|
|
uLdapAuthMethod = LDAP_AUTH_SIMPLE;
|
|
break;
|
|
|
|
case ILS_AUTH_NTLM:
|
|
uLdapAuthMethod = LDAP_AUTH_NTLM;
|
|
FillAuthIdentity (&ai);
|
|
pszLogonName = NULL;
|
|
pszLogonPassword = (TCHAR *) &ai;
|
|
break;
|
|
|
|
case ILS_AUTH_DPA:
|
|
uLdapAuthMethod = LDAP_AUTH_DPA;
|
|
break;
|
|
|
|
case ILS_AUTH_MSN:
|
|
uLdapAuthMethod = LDAP_AUTH_MSN;
|
|
break;
|
|
|
|
case ILS_AUTH_SICILY:
|
|
uLdapAuthMethod = LDAP_AUTH_SICILY;
|
|
break;
|
|
|
|
case ILS_AUTH_SSPI:
|
|
uLdapAuthMethod = LDAP_AUTH_SSPI;
|
|
break;
|
|
}
|
|
|
|
if (fSyncBind)
|
|
{
|
|
INT nRetCode = ::ldap_bind_s (m_ld, pszLogonName,
|
|
pszLogonPassword,
|
|
uLdapAuthMethod);
|
|
hr = (nRetCode == LDAP_SUCCESS) ? S_OK : ILS_E_BIND;
|
|
}
|
|
else
|
|
{
|
|
INT uMsgID = ::ldap_bind (m_ld, pszLogonName,
|
|
pszLogonPassword,
|
|
uLdapAuthMethod);
|
|
|
|
INT ResultType;
|
|
LDAP_TIMEVAL TimeVal;
|
|
LDAPMessage *pMsg;
|
|
|
|
LONG i, nTimeoutInSecond;
|
|
nTimeoutInSecond = GetServerTimeoutInSecond ();
|
|
for (i = 0; i < nTimeoutInSecond; i++)
|
|
{
|
|
TimeVal.tv_usec = 0;
|
|
TimeVal.tv_sec = 1;
|
|
pMsg = NULL;
|
|
|
|
ResultType = ::ldap_result (m_ld, uMsgID, LDAP_MSG_ALL, &TimeVal, &pMsg);
|
|
if (ResultType == LDAP_RES_BIND)
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// deal with timeout or error
|
|
if (ResultType == 0)
|
|
{
|
|
MyAssert (g_pReqQueue != NULL);
|
|
if (fAbortable && g_pReqQueue != NULL &&
|
|
g_pReqQueue->IsCurrentRequestCancelled ())
|
|
{
|
|
hr = ILS_E_ABORT;
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
if (ResultType == -1)
|
|
{
|
|
hr = ILS_E_BIND;
|
|
}
|
|
else
|
|
{
|
|
// lonchanc: AndyHe said the return value
|
|
// can be anything. Thus, removed the assertion.
|
|
hr = ILS_E_FAIL;
|
|
}
|
|
|
|
::ldap_abandon (m_ld, uMsgID);
|
|
::ldap_unbind (m_ld);
|
|
m_ld = NULL;
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
// Check if it times out
|
|
//
|
|
if (i >= nTimeoutInSecond)
|
|
{
|
|
hr = ILS_E_TIMEOUT;
|
|
::ldap_abandon (m_ld, uMsgID);
|
|
::ldap_unbind (m_ld);
|
|
m_ld = NULL;
|
|
return hr;
|
|
}
|
|
|
|
MyAssert (pMsg != NULL);
|
|
|
|
::ldap_msgfree (pMsg);
|
|
hr = S_OK;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT SP_CSession::
|
|
Connect (
|
|
SERVER_INFO *pInfo,
|
|
ULONG cConns,
|
|
BOOL fAbortable )
|
|
{
|
|
// If a connection is available,
|
|
// then simply the existing one
|
|
//
|
|
if (m_dwSignature == LDAP_CONN_SIGNATURE)
|
|
{
|
|
m_cRefs += cConns;
|
|
return S_OK;
|
|
}
|
|
|
|
// We need to create a new connection
|
|
// let's cache the server info
|
|
//
|
|
HRESULT hr = ::IlsCopyServerInfo (&m_ServerInfo, pInfo);
|
|
if (hr != S_OK)
|
|
return hr;
|
|
|
|
// Connect to ldap server
|
|
//
|
|
ULONG ulPort = LDAP_PORT;
|
|
LPTSTR pszServerName = My_strdup(m_ServerInfo.pszServerName);
|
|
if (NULL == pszServerName)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
LPTSTR pszSeparator = My_strchr(pszServerName, _T(':'));
|
|
if (NULL != pszSeparator)
|
|
{
|
|
*pszSeparator = _T('\0');
|
|
ulPort = GetStringLong(pszSeparator + 1);
|
|
}
|
|
|
|
m_ld = ::ldap_open (pszServerName, ulPort);
|
|
MemFree(pszServerName);
|
|
if (m_ld == NULL)
|
|
{
|
|
// We need to know why ldap_open() failed.
|
|
// Is it because server name is not valid?
|
|
// Or is it because server does not support LDAP?
|
|
//
|
|
// hr = (gethostbyname (m_ServerInfo.pszServerName) != NULL) ?
|
|
// Winsock will set ERROR_OPEN_FAILED, but wldap32.dll sets ERROR_HOST_UNREACHABLE
|
|
// The down side is that when the server was down, the client will try ULP.
|
|
//
|
|
DWORD dwErr = ::GetLastError ();
|
|
MyDebugMsg ((ZONE_REQ, "ULS: ldap_open failed, err=%lu)\r\n", dwErr));
|
|
hr = (dwErr == ERROR_OPEN_FAILED || dwErr == ERROR_HOST_UNREACHABLE) ?
|
|
ILS_E_SERVER_SERVICE : ILS_E_SERVER_NAME;
|
|
goto MyExit;
|
|
}
|
|
|
|
// Do the bind
|
|
//
|
|
hr = Bind (fAbortable);
|
|
if (hr == S_OK)
|
|
{
|
|
// remember the handle and increment the reference count
|
|
m_cRefs = cConns;
|
|
m_dwSignature = LDAP_CONN_SIGNATURE;
|
|
}
|
|
|
|
MyExit:
|
|
|
|
MyDebugMsg ((ZONE_CONN, "ILS: Connect: hr=0x%p, m_ld=0x%p, server=%s\r\n", (DWORD) hr, m_ld, m_ServerInfo.pszServerName));
|
|
|
|
if (hr != S_OK)
|
|
{
|
|
InternalCleanup ();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/* ---------- private methods ----------- */
|
|
|
|
|
|
VOID SP_CSession::
|
|
InternalCleanup ( VOID )
|
|
{
|
|
if (IsUsed ())
|
|
{
|
|
MyDebugMsg ((ZONE_CONN, "ILS: InternalCleanup: m_ld=0x%p, server=%s\r\n", m_ld, m_ServerInfo.pszServerName));
|
|
|
|
// Clean up these two ASAP because ldap_unbind may delay
|
|
//
|
|
m_dwSignature = 0;
|
|
::IlsFreeServerInfo (&m_ServerInfo);
|
|
|
|
// Free the ldap infor
|
|
//
|
|
if (m_ld != NULL)
|
|
{
|
|
ldap_unbind (m_ld);
|
|
m_ld = NULL;
|
|
}
|
|
|
|
// Clear it out
|
|
//
|
|
ClearUsed ();
|
|
}
|
|
}
|
|
|
|
|
|
/* ==================== container ========================= */
|
|
|
|
|
|
/* ---------- public methods ----------- */
|
|
|
|
|
|
SP_CSessionContainer::
|
|
SP_CSessionContainer ( VOID ) :
|
|
m_cEntries (0),
|
|
m_aConns (NULL)
|
|
{
|
|
::MyInitializeCriticalSection (&m_csSessContainer);
|
|
}
|
|
|
|
|
|
SP_CSessionContainer::
|
|
~SP_CSessionContainer ( VOID )
|
|
{
|
|
::MyDeleteCriticalSection (&m_csSessContainer);
|
|
m_cEntries = 0;
|
|
delete [] m_aConns;
|
|
}
|
|
|
|
|
|
HRESULT SP_CSessionContainer::
|
|
Initialize (
|
|
ULONG cEntries,
|
|
SP_CSession *ConnArr )
|
|
{
|
|
m_cEntries = cEntries;
|
|
m_aConns = new SP_CSession[cEntries];
|
|
return ((m_aConns != NULL) ? S_OK : ILS_E_MEMORY);
|
|
}
|
|
|
|
|
|
HRESULT SP_CSessionContainer::
|
|
GetSession (
|
|
SP_CSession **ppConn,
|
|
SERVER_INFO *pInfo,
|
|
ULONG cConns,
|
|
BOOL fAbortable )
|
|
{
|
|
MyAssert (ppConn != NULL);
|
|
MyAssert (pInfo != NULL);
|
|
|
|
*ppConn = NULL;
|
|
|
|
HRESULT hr;
|
|
|
|
WriteLock ();
|
|
|
|
// The first pass is to see any existing connection
|
|
//
|
|
for (ULONG i = 0; i < m_cEntries; i++)
|
|
{
|
|
if (m_aConns[i].IsUsed ())
|
|
{
|
|
if (m_aConns[i].SameServerInfo (pInfo))
|
|
{
|
|
*ppConn = &m_aConns[i];
|
|
hr = m_aConns[i].Connect (pInfo, cConns, fAbortable);
|
|
goto MyExit;
|
|
}
|
|
}
|
|
}
|
|
|
|
// The second pass is to see any empty slot
|
|
//
|
|
for (i = 0; i < m_cEntries; i++)
|
|
{
|
|
if (! m_aConns[i].IsUsed ())
|
|
{
|
|
m_aConns[i].SetUsed ();
|
|
*ppConn = &m_aConns[i];
|
|
hr = m_aConns[i].Connect (pInfo, cConns, fAbortable);
|
|
goto MyExit;
|
|
}
|
|
}
|
|
|
|
hr = ILS_E_MEMORY;
|
|
|
|
MyExit:
|
|
|
|
WriteUnlock ();
|
|
return hr;
|
|
}
|
|
|
|
|
|
/* ---------- protected methods ----------- */
|
|
|
|
/* ---------- private methods ----------- */
|
|
|