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