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.

3714 lines
108 KiB

  1. //
  2. // ldapconn.cpp -- This file contains the class implementation for:
  3. // CLdapConnection
  4. // CLdapConnectionCache
  5. //
  6. // Created:
  7. // Dec 31, 1996 -- Milan Shah (milans)
  8. //
  9. // Changes:
  10. //
  11. #include "precomp.h"
  12. #include "ldapconn.h"
  13. #include "icatitemattr.h"
  14. #define SECURITY_WIN32
  15. #include "security.h"
  16. LDAP_TIMEVAL CLdapConnection::m_ldaptimeout = { LDAPCONN_DEFAULT_RESULT_TIMEOUT, 0 };
  17. DWORD CLdapConnection::m_dwLdapRequestTimeLimit = DEFAULT_LDAP_REQUEST_TIME_LIMIT;
  18. //
  19. // LDAP counter block
  20. //
  21. CATLDAPPERFBLOCK g_LDAPPerfBlock;
  22. //+----------------------------------------------------------------------------
  23. //
  24. // Function: CLdapConnection::CLdapConnection
  25. //
  26. // Synopsis: Constructor for a CLdapConnection object.
  27. //
  28. // Arguments: [szHost] -- The actual name of the LDAP host to connect to.
  29. // If it is NULL, and we are running on an NT5 machine, we'll
  30. // use the default DC
  31. //
  32. // [dwPort] -- The remote tcp port to connect to. If
  33. // zero, LDAP_PORT is assumed
  34. //
  35. // [szNamingContext] -- The naming context to use within the
  36. // LDAP DS. If NULL, the naming context will be determined
  37. // by using the default naming context of the LDAP DS.
  38. //
  39. // By allowing a naming context to be associated with an
  40. // ldap connection, we can have multiple "logical" ldap
  41. // connections served by the same LDAP DS. This is useful
  42. // if folks want to setup mutliple virtual SMTP/POP3 servers
  43. // all served by the same LDAP DS. The naming context in
  44. // that case would be the name of the OU to restrict the
  45. // DS operations to.
  46. //
  47. // [szAccount] -- The DN of the account to log in as.
  48. //
  49. // [szPassword] -- The password to use to log in.
  50. //
  51. // [bt] -- The bind method to use. (none, simple, or generic)
  52. //
  53. // Returns: Nothing
  54. //
  55. //-----------------------------------------------------------------------------
  56. CLdapConnection::CLdapConnection(
  57. IN LPSTR szHost,
  58. IN DWORD dwPort,
  59. IN LPSTR szNamingContext,
  60. IN LPSTR szAccount,
  61. IN LPSTR szPassword,
  62. IN LDAP_BIND_TYPE bt)
  63. {
  64. int i;
  65. CatFunctEnter( "CLdapConnection::CLdapConnection" );
  66. m_dwSignature = SIGNATURE_LDAPCONN;
  67. m_pCPLDAPWrap = NULL;
  68. m_fValid = TRUE;
  69. m_fTerminating = FALSE;
  70. if (szNamingContext != NULL && szNamingContext[0] != 0) {
  71. _ASSERT(strlen(szNamingContext) < sizeof(m_szNamingContext) );
  72. strcpy(m_szNamingContext, szNamingContext);
  73. m_fDefaultNamingContext = FALSE;
  74. i = MultiByteToWideChar(
  75. CP_UTF8,
  76. 0,
  77. m_szNamingContext,
  78. -1,
  79. m_wszNamingContext,
  80. sizeof(m_wszNamingContext) / sizeof(m_wszNamingContext[0]));
  81. _ASSERT(i > 0);
  82. } else {
  83. m_szNamingContext[0] = 0;
  84. m_wszNamingContext[0] = 0;
  85. m_fDefaultNamingContext = TRUE;
  86. }
  87. _ASSERT( (szHost != NULL) &&
  88. (strlen(szHost) < sizeof(m_szHost)) );
  89. _ASSERT( (bt == BIND_TYPE_NONE) ||
  90. (bt == BIND_TYPE_CURRENTUSER) ||
  91. ((szAccount != NULL) &&
  92. (szAccount[0] != 0) &&
  93. (strlen(szAccount) < sizeof(m_szAccount)))
  94. );
  95. _ASSERT( (bt == BIND_TYPE_NONE) ||
  96. (bt == BIND_TYPE_CURRENTUSER) ||
  97. ((szPassword != NULL) &&
  98. (strlen(szPassword) < sizeof(m_szPassword))) );
  99. strcpy(m_szHost, szHost);
  100. SetPort(dwPort);
  101. if ((bt != BIND_TYPE_NONE) &&
  102. (bt != BIND_TYPE_CURRENTUSER)) {
  103. strcpy(m_szAccount, szAccount);
  104. strcpy(m_szPassword, szPassword);
  105. } else {
  106. m_szAccount[0] = 0;
  107. m_szPassword[0] = 0;
  108. }
  109. m_bt = bt;
  110. //
  111. // Initialize the async search completion structures
  112. //
  113. InitializeSpinLock( &m_spinlockCompletion );
  114. // InitializeCriticalSection( &m_cs );
  115. m_hCompletionThread = INVALID_HANDLE_VALUE;
  116. m_hOutstandingRequests = INVALID_HANDLE_VALUE;
  117. m_pfTerminateCompletionThreadIndicator = NULL;
  118. InitializeListHead( &m_listPendingRequests );
  119. m_fCancel = FALSE;
  120. m_dwRefCount = 1;
  121. m_dwDestructionWaiters = 0;
  122. m_hShutdownEvent = INVALID_HANDLE_VALUE;
  123. CatFunctLeave();
  124. }
  125. //+----------------------------------------------------------------------------
  126. //
  127. // Function: CLdapConnection::~CLdapConnection
  128. //
  129. // Synopsis: Destructor for a CLdapConnection object
  130. //
  131. // Arguments: None
  132. //
  133. // Returns: Nothing.
  134. //
  135. //-----------------------------------------------------------------------------
  136. CLdapConnection::~CLdapConnection()
  137. {
  138. CatFunctEnter( "CLdapConnection::~CLdapConnection" );
  139. _ASSERT(m_dwSignature == SIGNATURE_LDAPCONN);
  140. //
  141. // Disconnect
  142. //
  143. if (m_pCPLDAPWrap != NULL) {
  144. Disconnect();
  145. }
  146. if (m_hOutstandingRequests != INVALID_HANDLE_VALUE)
  147. CloseHandle( m_hOutstandingRequests );
  148. if (m_hShutdownEvent != INVALID_HANDLE_VALUE)
  149. CloseHandle( m_hShutdownEvent );
  150. // DeleteCriticalSection( &m_cs );
  151. m_dwSignature = SIGNATURE_LDAPCONN_INVALID;
  152. CatFunctLeave();
  153. }
  154. //+----------------------------------------------------------------------------
  155. //
  156. // Function: CLdapConnection::~CLdapConnection
  157. //
  158. // Synopsis: Called on the last release
  159. //
  160. // Arguments: None
  161. //
  162. // Returns: Nothing.
  163. //
  164. //-----------------------------------------------------------------------------
  165. VOID CLdapConnection::FinalRelease()
  166. {
  167. CancelAllSearches();
  168. //
  169. // If there was an async completion thread, we need to indicate to it that
  170. // it should exit. That thread could be stuck at one of two points -
  171. // either it is waiting on the m_hOutstandingRequests semaphore to be
  172. // fired, or it is blocked on ldap_result(). So, we set the event and
  173. // close out m_pldap, then we wait for the async completion thread to
  174. // quit.
  175. //
  176. SetTerminateIndicatorTrue();
  177. if (m_hOutstandingRequests != INVALID_HANDLE_VALUE) {
  178. LONG nUnused;
  179. ReleaseSemaphore(m_hOutstandingRequests, 1, &nUnused);
  180. }
  181. //
  182. // We do not wait for the LdapCompletionThread to die if it is the
  183. // LdapCompletionThread itself that is deleting us. If we did, it would
  184. // cause a deadlock.
  185. //
  186. if (m_hCompletionThread != INVALID_HANDLE_VALUE) {
  187. if (m_idCompletionThread != GetCurrentThreadId()) {
  188. WaitForSingleObject( m_hCompletionThread, INFINITE );
  189. }
  190. CloseHandle( m_hCompletionThread );
  191. }
  192. delete this;
  193. }
  194. //+----------------------------------------------------------------------------
  195. //
  196. // Function: CLdapConnection::InitializeFromRegistry
  197. //
  198. // Synopsis: Reads registry-configurable parameters to initialize
  199. // LDAP connection parameters.
  200. //
  201. // LDAPCONN_RESULT_TIMEOUT_VALUE is used as the timeout value
  202. // passed into ldap_result.
  203. //
  204. // LDAP_REQUEST_TIME_LIMIT_VALUE is used to set the search time
  205. // limit option on the connection and also for the expiration time
  206. // on pending search requests.
  207. //
  208. // Arguments: None
  209. //
  210. // Returns: TRUE if successfully connected, FALSE otherwise.
  211. //
  212. //-----------------------------------------------------------------------------
  213. VOID CLdapConnection::InitializeFromRegistry()
  214. {
  215. HKEY hkey;
  216. DWORD dwErr, dwType, dwValue, cbValue;
  217. dwErr = RegOpenKey(HKEY_LOCAL_MACHINE, LDAPCONN_RESULT_TIMEOUT_KEY, &hkey);
  218. if (dwErr == ERROR_SUCCESS) {
  219. cbValue = sizeof(dwValue);
  220. dwErr = RegQueryValueEx(
  221. hkey,
  222. LDAPCONN_RESULT_TIMEOUT_VALUE,
  223. NULL,
  224. &dwType,
  225. (LPBYTE) &dwValue,
  226. &cbValue);
  227. if ((dwErr == ERROR_SUCCESS) && (dwType == REG_DWORD) && (dwValue > 0))
  228. m_ldaptimeout.tv_sec = dwValue;
  229. cbValue = sizeof(dwValue);
  230. dwErr = RegQueryValueEx(
  231. hkey,
  232. LDAP_REQUEST_TIME_LIMIT_VALUE,
  233. NULL,
  234. &dwType,
  235. (LPBYTE) &dwValue,
  236. &cbValue);
  237. if ((dwErr == ERROR_SUCCESS) && (dwType == REG_DWORD) && (dwValue > 0))
  238. m_dwLdapRequestTimeLimit = dwValue;
  239. RegCloseKey( hkey );
  240. }
  241. }
  242. //+----------------------------------------------------------------------------
  243. //
  244. // Function: CLdapConnection::Connect
  245. //
  246. // Synopsis: Establishes a connection to the LDAP host and, if a naming
  247. // context has not been established, asks the host for the
  248. // default naming context.
  249. //
  250. // Arguments: None
  251. //
  252. // Returns: TRUE if successfully connected, FALSE otherwise.
  253. //
  254. //-----------------------------------------------------------------------------
  255. HRESULT CLdapConnection::Connect()
  256. {
  257. CatFunctEnter( "CLdapConnection::Connect" );
  258. DWORD ldapErr = LDAP_SUCCESS; // innocent until proven...
  259. LPSTR pszHost = (m_szHost[0] == '\0' ? NULL : m_szHost);
  260. if (m_pCPLDAPWrap == NULL) {
  261. DebugTrace(LDAP_CONN_DBG, "Connecting to [%s:%d]",
  262. pszHost ? pszHost : "NULL",
  263. m_dwPort);
  264. m_pCPLDAPWrap = new CPLDAPWrap( GetISMTPServerEx(), pszHost, m_dwPort);
  265. if(m_pCPLDAPWrap == NULL) {
  266. HRESULT hr = E_OUTOFMEMORY;
  267. ERROR_LOG("new CPLDAPWrap");
  268. }
  269. if((m_pCPLDAPWrap != NULL) &&
  270. (m_pCPLDAPWrap->GetPLDAP() == NULL)) {
  271. //
  272. // Failure to connect; release
  273. //
  274. m_pCPLDAPWrap->Release();
  275. m_pCPLDAPWrap = NULL;
  276. }
  277. DebugTrace(LDAP_CONN_DBG, "ldap_open returned 0x%x", m_pCPLDAPWrap);
  278. if (m_pCPLDAPWrap != NULL) {
  279. INCREMENT_LDAP_COUNTER(Connections);
  280. INCREMENT_LDAP_COUNTER(OpenConnections);
  281. //
  282. // First, set some options - no autoreconnect, and no chasing of
  283. // referrals
  284. //
  285. ULONG ulLdapOff = (ULONG)((ULONG_PTR)LDAP_OPT_OFF);
  286. ULONG ulLdapRequestTimeLimit = m_dwLdapRequestTimeLimit;
  287. ULONG ulLdapVersion = LDAP_VERSION3;
  288. ldap_set_option(
  289. GetPLDAP(), LDAP_OPT_REFERRALS, (LPVOID) &ulLdapOff);
  290. ldap_set_option(
  291. GetPLDAP(), LDAP_OPT_AUTO_RECONNECT, (LPVOID) &ulLdapOff);
  292. ldap_set_option(
  293. GetPLDAP(), LDAP_OPT_TIMELIMIT, (LPVOID) &ulLdapRequestTimeLimit);
  294. ldap_set_option(
  295. GetPLDAP(), LDAP_OPT_PROTOCOL_VERSION, (LPVOID) &ulLdapVersion);
  296. ldapErr = BindToHost( GetPLDAP(), m_szAccount, m_szPassword);
  297. DebugTrace(LDAP_CONN_DBG, "BindToHost returned 0x%x", ldapErr);
  298. } else {
  299. INCREMENT_LDAP_COUNTER(ConnectFailures);
  300. ldapErr = LDAP_SERVER_DOWN;
  301. }
  302. //
  303. // Figure out the naming context for this connection if none was
  304. // initially specified and we are using the default LDAP_PORT
  305. // (a baseDN of "" is acceptable on other LDAP ports such as
  306. // a GC)
  307. //
  308. if ((m_dwPort == LDAP_PORT) &&
  309. (ldapErr == LDAP_SUCCESS) &&
  310. (m_szNamingContext[0] == 0)) {
  311. ldapErr = GetDefaultNamingContext();
  312. if (ldapErr != LDAP_SUCCESS)
  313. Disconnect();
  314. } // end if port 389, successful bind and no naming context
  315. } else { // end if we didn't have a connection already
  316. DebugTrace(
  317. LDAP_CONN_DBG,
  318. "Already connected to %s:%d, pldap = 0x%x",
  319. m_szHost, m_dwPort, GetPLDAP());
  320. }
  321. DebugTrace(LDAP_CONN_DBG, "Connect status = 0x%x", ldapErr);
  322. if (ldapErr != LDAP_SUCCESS) {
  323. m_fValid = FALSE;
  324. CatFunctLeave();
  325. return( LdapErrorToHr( ldapErr) );
  326. } else {
  327. CatFunctLeave();
  328. return( S_OK );
  329. }
  330. }
  331. //+------------------------------------------------------------
  332. //
  333. // Function: CLdapConnection::GetDefaultNamingContext
  334. //
  335. // Synopsis: Gets the default naming context from the LDAP server that
  336. // we are connected to. Note: this should only be called from
  337. // Connect. It is not gaurenteed to be multi-thread safe, and it may
  338. // not work when there is an LdapCompletionThread for this connection.
  339. //
  340. // Arguments: None
  341. //
  342. // Returns:
  343. // LDAP_SUCCESS: fetched m_szNamingContext successfully
  344. // else, an LDAP error describing why we couldn't get a naming context
  345. //
  346. // History:
  347. // jstamerj 2002/04/16 14:36:28: Created.
  348. //
  349. //-------------------------------------------------------------
  350. ULONG CLdapConnection::GetDefaultNamingContext()
  351. {
  352. ULONG ldapErr = LDAP_SUCCESS;
  353. PLDAPMessage pmsg = NULL;
  354. PLDAPMessage pentry = NULL;
  355. LPWSTR rgszAttributes[2] = { L"defaultNamingContext", NULL };
  356. LPWSTR *rgszValues = NULL;
  357. int i = 0;
  358. CatFunctEnterEx((LPARAM)this, "CLdapConnection::GetDefaultNamingContext");
  359. ldapErr = ldap_search_sW(
  360. GetPLDAP(), // ldap binding
  361. L"", // base DN
  362. LDAP_SCOPE_BASE, // scope of search
  363. L"(objectClass=*)", // filter,
  364. rgszAttributes, // attributes required
  365. FALSE, // attributes-only is false
  366. &pmsg);
  367. DebugTrace(
  368. LDAP_CONN_DBG,
  369. "Search for namingContexts returned 0x%x",
  370. ldapErr);
  371. // If the search succeeded
  372. if ((ldapErr == LDAP_SUCCESS) &&
  373. // and there is at least one entry
  374. ((pentry = ldap_first_entry(GetPLDAP(), pmsg)) != NULL) &&
  375. // and there are values
  376. ((rgszValues = ldap_get_valuesW(GetPLDAP(), pentry,
  377. rgszAttributes[0])) != NULL) &&
  378. // and there is at least one value
  379. (ldap_count_valuesW(rgszValues) != 0) &&
  380. // and the length of that value is within limits
  381. (wcslen(rgszValues[0]) <
  382. sizeof(m_wszNamingContext)/sizeof(WCHAR)) &&
  383. // and the UTF8 conversion succeeds
  384. (WideCharToMultiByte(
  385. CP_UTF8,
  386. 0,
  387. rgszValues[0],
  388. -1,
  389. m_szNamingContext,
  390. sizeof(m_szNamingContext),
  391. NULL,
  392. NULL) > 0))
  393. {
  394. //
  395. // Use the first value for our naming context.
  396. //
  397. wcscpy(m_wszNamingContext, rgszValues[0]);
  398. DebugTrace(
  399. LDAP_CONN_DBG,
  400. "NamingContext is [%s]",
  401. m_szNamingContext);
  402. } else {
  403. HRESULT hr = ldapErr; // Used by ERROR_LOG
  404. ERROR_LOG("ldap_search_sW");
  405. ldapErr = LDAP_OPERATIONS_ERROR;
  406. }
  407. if (rgszValues != NULL)
  408. ldap_value_freeW( rgszValues );
  409. if (pmsg != NULL)
  410. ldap_msgfree( pmsg );
  411. CatFunctLeaveEx((LPARAM)this);
  412. return ldapErr;
  413. }
  414. //+----------------------------------------------------------------------------
  415. //
  416. // Function: CLdapConnection::Disconnect
  417. //
  418. // Synopsis: Disconnects from the ldap host
  419. //
  420. // Arguments: None
  421. //
  422. // Returns: Nothing
  423. //
  424. //-----------------------------------------------------------------------------
  425. VOID CLdapConnection::Disconnect()
  426. {
  427. BOOL fValid;
  428. CatFunctEnter("CLdapConnection::Disconnect");
  429. if (m_pCPLDAPWrap != NULL) {
  430. SetTerminateIndicatorTrue();
  431. fValid = InterlockedExchange((PLONG) &m_fValid, FALSE);
  432. m_pCPLDAPWrap->Release();
  433. m_pCPLDAPWrap = NULL;
  434. if( fValid ) {
  435. DECREMENT_LDAP_COUNTER(OpenConnections);
  436. }
  437. }
  438. CatFunctLeave();
  439. }
  440. //+----------------------------------------------------------------------------
  441. //
  442. // Function: CLdapConnection::Invalidate
  443. //
  444. // Synopsis: Marks this connection invalid. Once this is done, it will
  445. // return FALSE from all calls to IsEqual, thus effectively
  446. // removing itself from all searches for cached connections.
  447. //
  448. // Arguments: None
  449. //
  450. // Returns: Nothing
  451. //
  452. //-----------------------------------------------------------------------------
  453. VOID CLdapConnection::Invalidate()
  454. {
  455. BOOL fValid;
  456. fValid = InterlockedExchange((PLONG) &m_fValid, FALSE);
  457. if( fValid ) {
  458. DECREMENT_LDAP_COUNTER(OpenConnections);
  459. }
  460. }
  461. //+----------------------------------------------------------------------------
  462. //
  463. // Function: CLdapConnection::IsValid
  464. //
  465. // Synopsis: Returns whether the connection is valid or not.
  466. //
  467. // Arguments: None
  468. //
  469. // Returns: TRUE if valid, FALSE if a call to Invalidate has been made.
  470. //
  471. //-----------------------------------------------------------------------------
  472. BOOL CLdapConnection::IsValid()
  473. {
  474. return( m_fValid );
  475. }
  476. //+----------------------------------------------------------------------------
  477. //
  478. // Function: CLdapConnection::BindToHost
  479. //
  480. // Synopsis: Creates a binding to the LDAP host using the given account
  481. // and password.
  482. //
  483. // Arguments: [pldap] -- The ldap connection to bind.
  484. // [szAccount] -- The account to use. Of the form "account-name"
  485. // or "domain\account-name".
  486. // [szPassword] -- The password to use.
  487. //
  488. // Returns: LDAP result of bind.
  489. //
  490. //-----------------------------------------------------------------------------
  491. DWORD CLdapConnection::BindToHost(
  492. PLDAP pldap,
  493. LPSTR szAccount,
  494. LPSTR szPassword)
  495. {
  496. CatFunctEnter( "CLdapConnection::BindToHost" );
  497. DWORD ldapErr;
  498. char szDomain[ DNLEN + 1];
  499. LPSTR pszDomain, pszUser;
  500. HANDLE hToken; // LogonUser modifies hToken
  501. BOOL fLogon = FALSE; // even if it fails! So, we
  502. // have to look at the result
  503. // of LogonUser!
  504. //
  505. // If this connection was created with anonymous access rights, there is
  506. // no bind action to do.
  507. //
  508. if (m_bt == BIND_TYPE_NONE) {
  509. ldapErr = ERROR_SUCCESS;
  510. goto Cleanup;
  511. }
  512. //
  513. // If we are supposed to use simple bind, do it now
  514. //
  515. if (m_bt == BIND_TYPE_SIMPLE) {
  516. ldapErr = ldap_simple_bind_s(pldap,szAccount, szPassword);
  517. DebugTrace(0, "ldap_simple_bind returned 0x%x", ldapErr);
  518. if(ldapErr != LDAP_SUCCESS)
  519. LogLdapError(ldapErr, "ldap_simple_bind_s(pldap,\"%s\",szPassword), PLDAP = 0x%08lx", szAccount, pldap);
  520. goto Cleanup;
  521. }
  522. //
  523. // If we are supposed to logon with current credetials, do it now.
  524. //
  525. if (m_bt == BIND_TYPE_CURRENTUSER) {
  526. //-------------------------------------------------------------------
  527. // X5: TBD
  528. // This is the normal case for Exchange services. We are connecting
  529. // as LocalSystem, so we must use Kerberos (this is true for the LDAP
  530. // server as of Win2000 SP1).
  531. // If we cannot bind as Kerberos, LDAP_AUTH_NEGOTIATE may negotiate
  532. // down to NTLM, at which point we become anonymous, and the bind
  533. // succeeds. Anonymous binding is useless to Exchange, so we would
  534. // rather force Kerberos and fail if Kerberos has a problem. Use a
  535. // SEC_WINNT_AUTH_IDENTITY_EX to specify that only Kerberos auth
  536. // should be tried.
  537. //-------------------------------------------------------------------
  538. SEC_WINNT_AUTH_IDENTITY_EX authstructex;
  539. ZeroMemory (&authstructex, sizeof(authstructex));
  540. authstructex.Version = SEC_WINNT_AUTH_IDENTITY_VERSION;
  541. authstructex.Length = sizeof (authstructex);
  542. authstructex.PackageList = (PUCHAR) MICROSOFT_KERBEROS_NAME_A;
  543. authstructex.PackageListLength = strlen ((PCHAR) authstructex.PackageList);
  544. authstructex.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
  545. ldapErr = ldap_bind_s(pldap,
  546. NULL,
  547. (PCHAR) &authstructex,
  548. LDAP_AUTH_NEGOTIATE);
  549. DebugTrace(0, "ldap_bind returned 0x%x", ldapErr);
  550. if(ldapErr != LDAP_SUCCESS)
  551. LogLdapError(ldapErr, "ldap_bind_s(pldap, NULL, &authstructex, LDAP_AUTH_NEGOTIATE), PLDAP = 0x%08lx", pldap);
  552. goto Cleanup;
  553. }
  554. //
  555. // Parse out the domain and user names from the szAccount parameter.
  556. //
  557. if ((pszUser = strchr(szAccount, '\\')) == NULL) {
  558. pszUser = szAccount;
  559. pszDomain = NULL;
  560. } else {
  561. ULONG cbDomain = (ULONG)(((ULONG_PTR) pszUser) - ((ULONG_PTR) szAccount));
  562. if(cbDomain < sizeof(szDomain)) {
  563. strncpy( szDomain, szAccount, cbDomain);
  564. szDomain[cbDomain] = '\0';
  565. } else {
  566. ldapErr = LDAP_INVALID_CREDENTIALS;
  567. goto Cleanup;
  568. }
  569. pszDomain = cbDomain > 0 ? szDomain : NULL;
  570. pszUser++; // Go past the backslash
  571. }
  572. //
  573. // Logon as the given user, impersonate, and attempt the LDAP bind.
  574. //
  575. fLogon = LogonUser(pszUser, pszDomain, szPassword, LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, &hToken);
  576. if(!fLogon) {
  577. HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
  578. ERROR_LOG("LogonUser");
  579. } else {
  580. fLogon = ImpersonateLoggedOnUser(hToken);
  581. if(!fLogon) {
  582. HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
  583. ERROR_LOG("ImpersonateLoggedOnUser");
  584. }
  585. }
  586. if (fLogon) {
  587. ldapErr = ldap_bind_s(pldap, NULL, NULL, LDAP_AUTH_SSPI);
  588. DebugTrace(0, "ldap_bind returned 0x%x", ldapErr);
  589. if(ldapErr != LDAP_SUCCESS)
  590. LogLdapError(ldapErr, "ldap_bind_s(pldap, NULL, NULL, LDAP_AUTH_SSPI), PLDAP = 0x%08lx", pldap);
  591. RevertToSelf();
  592. } else {
  593. if (GetLastError() == ERROR_PRIVILEGE_NOT_HELD)
  594. ldapErr = LDAP_INSUFFICIENT_RIGHTS;
  595. else
  596. ldapErr = LDAP_INVALID_CREDENTIALS;
  597. }
  598. Cleanup:
  599. if (fLogon)
  600. CloseHandle( hToken );
  601. //
  602. // Increment counters
  603. //
  604. if(m_bt != BIND_TYPE_NONE) {
  605. if(ldapErr == ERROR_SUCCESS) {
  606. INCREMENT_LDAP_COUNTER(Binds);
  607. } else {
  608. INCREMENT_LDAP_COUNTER(BindFailures);
  609. }
  610. }
  611. CatFunctLeave();
  612. return( ldapErr);
  613. }
  614. //+----------------------------------------------------------------------------
  615. //
  616. // Function: CLdapConnection::IsEqual
  617. //
  618. // Synopsis: Figures out if this connection represents a connection to the
  619. // given Host,NamingContext,Account, and Password parameters.
  620. //
  621. // Arguments: [szHost] -- The name of the LDAP host
  622. // [dwPort] -- The remote tcp port # of the LDAP connection
  623. // [szNamingContext] -- The naming context within the DS
  624. // [szAccount] -- The account used to bind to the LDAP DS.
  625. // [szPassword] -- The password used with szAccount.
  626. // [BindType] -- The bind type used to connect to host.
  627. //
  628. // Returns: TRUE if this connection represents the connection to the
  629. // given LDAP context, FALSE otherwise.
  630. //
  631. //-----------------------------------------------------------------------------
  632. BOOL CLdapConnection::IsEqual(
  633. LPSTR szHost,
  634. DWORD dwPort,
  635. LPSTR szNamingContext,
  636. LPSTR szAccount,
  637. LPSTR szPassword,
  638. LDAP_BIND_TYPE BindType)
  639. {
  640. CatFunctEnter("CLdapConnection::IsEqual");
  641. BOOL fResult = FALSE;
  642. _ASSERT( szHost != NULL );
  643. _ASSERT( szAccount != NULL );
  644. _ASSERT( szPassword != NULL );
  645. if (!m_fValid)
  646. return( FALSE );
  647. DebugTrace(
  648. LDAP_CONN_DBG,
  649. "Comparing %s:%d;%s;%s",
  650. szHost, dwPort, szNamingContext, szAccount);
  651. DebugTrace(
  652. LDAP_CONN_DBG,
  653. "With %s:%d;%s;%s; Def NC = %s",
  654. m_szHost, m_dwPort, m_szNamingContext, m_szAccount,
  655. m_fDefaultNamingContext ? "TRUE" : "FALSE");
  656. //
  657. // See if the host/port match.
  658. //
  659. fResult = (BOOL) ((lstrcmpi( szHost, m_szHost) == 0) &&
  660. fIsPortEqual(dwPort));
  661. //
  662. // If the host matches, see if the bind info matches.
  663. //
  664. if (fResult) {
  665. switch (BindType) {
  666. case BIND_TYPE_NONE:
  667. case BIND_TYPE_CURRENTUSER:
  668. fResult = (BindType == m_bt);
  669. break;
  670. case BIND_TYPE_SIMPLE:
  671. case BIND_TYPE_GENERIC:
  672. fResult = (BindType == m_bt) &&
  673. (lstrcmpi(szAccount, m_szAccount) == 0) &&
  674. (lstrcmpi(szPassword, m_szPassword) == 0);
  675. break;
  676. default:
  677. _ASSERT( FALSE && "Invalid Bind Type in CLdapConnection::IsEqual");
  678. break;
  679. }
  680. }
  681. if (fResult) {
  682. //
  683. // If caller specified a naming context, see if it matches. Otherwise,
  684. // see if we are using the Default Naming Context.
  685. //
  686. if (szNamingContext && szNamingContext[0] != 0)
  687. fResult = (lstrcmpi(szNamingContext, m_szNamingContext) == 0);
  688. else
  689. fResult = m_fDefaultNamingContext;
  690. }
  691. CatFunctLeave();
  692. return( fResult );
  693. }
  694. //+----------------------------------------------------------------------------
  695. //
  696. // Function: CLdapConnection::Search
  697. //
  698. // Synopsis: Issues a synchronous search request. Returns the result as an
  699. // opaque pointer that can be passed to GetFirstEntry /
  700. // GetNextEntry
  701. //
  702. // Arguments: [szBaseDN] -- The DN of the container object within which to
  703. // search.
  704. // [nScope] -- One of LDAP_SCOPE_BASE, LDAP_SCOPE_ONELEVEL, or
  705. // LDAP_SCOPE_SUBTREE.
  706. // [szFilter] -- The search filter to use. If NULL, a default
  707. // filter is used.
  708. // [rgszAttributes] -- The list of attributes to retrieve.
  709. // [ppResult] -- The result is passed back in here.
  710. //
  711. // Returns: TRUE if success, FALSE otherwise
  712. //
  713. //-----------------------------------------------------------------------------
  714. HRESULT CLdapConnection::Search(
  715. LPCSTR szBaseDN,
  716. int nScope,
  717. LPCSTR szFilter,
  718. LPCSTR *rgszAttributes,
  719. PLDAPRESULT *ppResult)
  720. {
  721. //$$BUGBUG: Obsolete code
  722. CatFunctEnter("CLdapConnection::Search");
  723. DWORD ldapErr = LDAP_SUCCESS;
  724. LPCSTR szFilterToUse = szFilter != NULL ? szFilter : "(objectClass=*)";
  725. if (m_pCPLDAPWrap != NULL) {
  726. ldapErr = ldap_search_s(
  727. GetPLDAP(), // ldap binding
  728. (LPSTR) szBaseDN, // container DN to search
  729. nScope, // Base, 1 or multi level
  730. (LPSTR) szFilterToUse, // search filter
  731. (LPSTR *)rgszAttributes, // attributes to retrieve
  732. FALSE, // attributes-only is false
  733. (PLDAPMessage *) ppResult); // return result here
  734. } else {
  735. ldapErr = LDAP_UNAVAILABLE;
  736. }
  737. if (ldapErr != LDAP_SUCCESS) {
  738. CatFunctLeave();
  739. return( LdapErrorToHr( ldapErr) );
  740. } else {
  741. CatFunctLeave();
  742. return( S_OK );
  743. }
  744. }
  745. //+----------------------------------------------------------------------------
  746. //
  747. // Function: CLdapConnection::AsyncSearch
  748. //
  749. // Synopsis: Issues an asynchronous search request. Inserts a pending
  750. // request item into the m_pPendingHead queue, so that the
  751. // given completion routine may be called when the results are
  752. // available.
  753. //
  754. // As a side effect, if this is the first time an async request
  755. // is being issued on this connection, a thread to handle search
  756. // completions is created.
  757. //
  758. // Arguments: [szBaseDN] -- The DN of the container object within which to
  759. // search.
  760. // [nScope] -- One of LDAP_SCOPE_BASE, LDAP_SCOPE_ONELEVEL, or
  761. // LDAP_SCOPE_SUBTREE.
  762. // [szFilter] -- The search filter to use. If NULL, a default
  763. // filter is used.
  764. // [rgszAttributes] -- The list of attributes to retrieve.
  765. // [dwPageSize] -- The desired page size for results. If
  766. // zero, a non-paged ldap search is performed.
  767. // [fnCompletion] -- The LPLDAPCOMPLETION routine to call when
  768. // results are available.
  769. // [ctxCompletion] -- The context to pass to fnCompletion.
  770. //
  771. // Returns: [ERROR_SUCCESS] -- Successfully issued the search request.
  772. //
  773. // [ERROR_OUTOFMEMORY] -- Unable to allocate working data strucs
  774. //
  775. // Win32 Error from ldap_search() call if something went wrong.
  776. //
  777. //-----------------------------------------------------------------------------
  778. HRESULT CLdapConnection::AsyncSearch(
  779. LPCWSTR szBaseDN,
  780. int nScope,
  781. LPCWSTR szFilter,
  782. LPCWSTR *rgszAttributes,
  783. DWORD dwPageSize,
  784. LPLDAPCOMPLETION fnCompletion,
  785. LPVOID ctxCompletion)
  786. {
  787. CatFunctEnter("CLdapConnectio::AsyncSearch");
  788. HRESULT hr;
  789. DWORD dwLdapErr;
  790. PPENDING_REQUEST preq;
  791. ULONG msgid;
  792. //
  793. // First, see if we need to create the completion thread.
  794. //
  795. hr = CreateCompletionThreadIfNeeded();
  796. if(FAILED(hr)) {
  797. ERROR_LOG("CreateCompletionThreadIfNeeded");
  798. return hr;
  799. }
  800. //
  801. // Next, allocate a new PENDING_REQUEST record to represent this async
  802. // request.
  803. //
  804. preq = new PENDING_REQUEST;
  805. if (preq == NULL)
  806. {
  807. hr = E_OUTOFMEMORY;
  808. ERROR_LOG("new PENIDNG_REQUEST");
  809. return( HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY) );
  810. }
  811. preq->fnCompletion = fnCompletion;
  812. preq->ctxCompletion = ctxCompletion;
  813. preq->dwPageSize = dwPageSize;
  814. //
  815. // Initialize msgid to -1 so it can't possibly match any valid msgid that
  816. // the completion thread might be looking for in the pending request list.
  817. //
  818. preq->msgid = -1;
  819. //
  820. //
  821. if(dwPageSize) {
  822. //
  823. // Init the paged search if that is what we will be doing
  824. //
  825. preq->pldap_search = ldap_search_init_pageW(
  826. GetPLDAP(), // LDAP connection to use
  827. (LPWSTR) szBaseDN, // Starting container DN
  828. nScope, // depth of search
  829. (LPWSTR) szFilter, // Search filter
  830. (LPWSTR *) rgszAttributes, // Attributes array
  831. FALSE, // Attributes only?
  832. NULL, // Server controls
  833. NULL, // Client controls
  834. 0, // PageTimeLimit
  835. 0, // TotalSizeLimit
  836. NULL); // Sorting keys
  837. if(preq->pldap_search == NULL) {
  838. ULONG ulLdapErr = LdapGetLastError();
  839. LogLdapError(ulLdapErr, "ldap_search_init_pageW(GetPLDAP(), \"%S\", %d, \"%S\", rgszAttributes, ...), PLDAP = 0x%08lx",
  840. szBaseDN, nScope, szFilter, GetPLDAP());
  841. dwLdapErr = LdapErrorToHr(ulLdapErr);
  842. ErrorTrace((LPARAM)this, "ldap_search_init_page failed with err %d (0x%x)", dwLdapErr, dwLdapErr);
  843. delete preq;
  844. //$$BUGBUG: We call LdapErrorToHr twice?
  845. return ( LdapErrorToHr(dwLdapErr));
  846. }
  847. } else {
  848. preq->pldap_search = NULL; // Not doing a paged search
  849. }
  850. //
  851. // We might want to abandon all of our outstanding requests at
  852. // some point. Because of this, we use this sharelock to prevent
  853. // abandoning requests with msgid still set to -1
  854. //
  855. m_ShareLock.ShareLock();
  856. //
  857. // Link the request into the queue of pending requests so that the
  858. // completion thread can pick it up when a result is available.
  859. //
  860. InsertPendingRequest( preq );
  861. if(dwPageSize) {
  862. //
  863. // Issue an async request for the next page of matches
  864. //
  865. dwLdapErr = ldap_get_next_page(
  866. GetPLDAP(), // LDAP connection to use
  867. preq->pldap_search, // LDAP page search context
  868. dwPageSize, // page size desired
  869. &msgid);
  870. if(dwLdapErr != LDAP_SUCCESS) {
  871. LogLdapError(dwLdapErr, "ldap_get_next_page(GetPLDAP(),preq->pldap_search,%d,&msgid), PDLAP = 0x%08lx",
  872. dwPageSize, GetPLDAP());
  873. }
  874. } else {
  875. //
  876. // Now, attempt to issue the async search request.
  877. //
  878. dwLdapErr = ldap_search_extW(
  879. GetPLDAP(), // LDAP connection to use
  880. (LPWSTR) szBaseDN, // Starting container DN
  881. nScope, // depth of search
  882. (LPWSTR) szFilter, // Search filter
  883. (LPWSTR *)rgszAttributes, // List of attributes to get
  884. FALSE, // Attributes only?
  885. NULL, // Server controls
  886. NULL, // Client controls
  887. 0, // Time limit
  888. 0, // Size limit
  889. &msgid);
  890. if(dwLdapErr != LDAP_SUCCESS) {
  891. LogLdapError(dwLdapErr, "ldap_search_extW(GetPLDAP(), \"%S\", %d, \"%S\", rgszAttributes, ...), PLDAP = 0x%08lx",
  892. szBaseDN, nScope, szFilter, GetPLDAP());
  893. }
  894. }
  895. //
  896. // One last thing - ldap_search could fail, in which case we need to
  897. // remove the PENDING_REQUEST item we just inserted.
  898. //
  899. if (dwLdapErr != LDAP_SUCCESS) { // ldap_search failed!
  900. DebugTrace((LPARAM)this, "DispError %d 0x%08lx conn %08lx", dwLdapErr, dwLdapErr, (PLDAP)(GetPLDAP()));
  901. RemovePendingRequest( preq );
  902. m_ShareLock.ShareUnlock();
  903. INCREMENT_LDAP_COUNTER(SearchFailures);
  904. if(preq->pldap_search) {
  905. INCREMENT_LDAP_COUNTER(PagedSearchFailures);
  906. //
  907. // Free the ldap page search context
  908. //
  909. ldap_search_abandon_page(
  910. GetPLDAP(),
  911. preq->pldap_search);
  912. }
  913. delete preq;
  914. return( LdapErrorToHr(dwLdapErr) );
  915. } else {
  916. preq->msgid = (int) msgid;
  917. INCREMENT_LDAP_COUNTER(Searches);
  918. INCREMENT_LDAP_COUNTER(PendingSearches);
  919. if(dwPageSize)
  920. INCREMENT_LDAP_COUNTER(PagedSearches);
  921. //
  922. // WARNING: preq could have been processed and free'd in the
  923. // completion routine at this point so it is not advisable to view
  924. // it!
  925. //
  926. DebugTrace((LPARAM)msgid, "Dispatched ldap search request %ld 0x%08lx conn %08lx", msgid, msgid, (PLDAP)(GetPLDAP()));
  927. m_ShareLock.ShareUnlock();
  928. ReleaseSemaphore( m_hOutstandingRequests, 1, NULL );
  929. }
  930. CatFunctLeave();
  931. return( S_OK );
  932. }
  933. //+----------------------------------------------------------------------------
  934. //
  935. // Function: CLdapConnection::CancelAllSearches
  936. //
  937. // Synopsis: Cancels all pending requests to the LDAP server.
  938. //
  939. // Arguments: [hr] -- The error code to complete pending requests with.
  940. // Defaults to HRESULT_FROM_WIN32(ERROR_CANCELLED)
  941. // [pISMTPServer] -- Interface on which to call StopHint after
  942. // every cancelled search. Defaults to NULL, in which case no
  943. // StopHint is called.
  944. //
  945. // Returns: Nothing
  946. //
  947. //-----------------------------------------------------------------------------
  948. VOID CLdapConnection::CancelAllSearches(
  949. HRESULT hr,
  950. ISMTPServer *pISMTPServer)
  951. {
  952. CatFunctEnter("CLdapConnection::CancelAllSearches");
  953. PLIST_ENTRY pli;
  954. PPENDING_REQUEST preq = NULL;
  955. LIST_ENTRY listCancel;
  956. //
  957. // We need to visit every node of m_listPendingRequests and call the
  958. // completion routine with the error. But, we want to call the
  959. // completion routine outside the critical section, so that calls to
  960. // AsyncSearch (from other threads or this thread!) won't block. So,
  961. // we simply transfer m_listPendingRequests to a temporary list under
  962. // the critical section, and then complete the temporary list outside
  963. // the critical section.
  964. //
  965. //
  966. // Transfer m_listPendingRequests to listCancel under the critical
  967. // section
  968. //
  969. InitializeListHead( &listCancel );
  970. //
  971. // We need exclusive access to the list (no half completed
  972. // searches are welcome), so get the exclusive lock
  973. //
  974. m_ShareLock.ExclusiveLock();
  975. AcquireSpinLock( &m_spinlockCompletion );
  976. //
  977. // swipe the contents of the pending request list
  978. //
  979. InsertTailList( &m_listPendingRequests, &listCancel);
  980. RemoveEntryList( &m_listPendingRequests );
  981. InitializeListHead( &m_listPendingRequests );
  982. //
  983. // Inform ProcessAyncResult that we've cancelled everything
  984. //
  985. NotifyCancel();
  986. ReleaseSpinLock( &m_spinlockCompletion );
  987. m_ShareLock.ExclusiveUnlock();
  988. //
  989. // Cancel all pending requests outside the critical section
  990. //
  991. for (pli = listCancel.Flink;
  992. pli != & listCancel;
  993. pli = listCancel.Flink) {
  994. preq = CONTAINING_RECORD(pli, PENDING_REQUEST, li);
  995. RemoveEntryList( &preq->li );
  996. ErrorTrace(0, "Calling ldap_abandon for msgid %ld",
  997. preq->msgid);
  998. AbandonRequest(preq);
  999. CallCompletion(
  1000. preq,
  1001. NULL,
  1002. hr,
  1003. TRUE);
  1004. if (pISMTPServer) {
  1005. pISMTPServer->ServerStopHintFunction();
  1006. }
  1007. delete preq;
  1008. }
  1009. CatFunctLeave();
  1010. return;
  1011. }
  1012. //+----------------------------------------------------------------------------
  1013. //
  1014. // Function: CLdapConnection::ProcessAsyncResult
  1015. //
  1016. // Synopsis: Routine that LdapCompletionThread calls to process any
  1017. // results for async searches it receives.
  1018. //
  1019. // Arguments: [pres] -- The PLDAPMessage to process. This routine will free
  1020. // this result when its done with it.
  1021. //
  1022. // [dwLdapError] -- The status of the received message.
  1023. //
  1024. // [pfTerminateIndicator] -- Ptr to boolean that is set
  1025. // to true when we want to
  1026. // shutdown
  1027. //
  1028. // Returns: Nothing.
  1029. //
  1030. //-----------------------------------------------------------------------------
  1031. VOID CLdapConnection::ProcessAsyncResult(
  1032. PLDAPMessage presIN,
  1033. DWORD dwLdapError,
  1034. BOOL *pfTerminateIndicator)
  1035. {
  1036. CatFunctEnterEx((LPARAM)this, "CLdapConnection::ProcessAsyncResult");
  1037. //
  1038. // balanced by a release at the end of ProcessAsyncResult
  1039. //
  1040. int msgid;
  1041. PLIST_ENTRY pli;
  1042. PPENDING_REQUEST preq = NULL;
  1043. LONG lOops = 0; // It's possible we've recieved a result for a
  1044. // query that's was sent by ldap_search_ext
  1045. // currently in another thread and the msgid
  1046. // hasn't been stamped yet. If this happens,
  1047. // we've consumed someone other request's
  1048. // semaphore count..keep track of these here
  1049. // and release them when we're done.
  1050. BOOL fNoMsgID = FALSE; // Set this to true if we see one or more
  1051. // messages with ID = -1
  1052. BOOL fFinalCompletion = TRUE; // TRUE unless this is a partial
  1053. // completion of a paged search
  1054. BOOL fPagedSearch = FALSE;
  1055. PLDAPMessage pres = presIN;
  1056. CPLDAPWrap *pCLDAPWrap = NULL;
  1057. ISMTPServerEx *pISMTPServerEx = GetISMTPServerEx();
  1058. _ASSERT(m_pCPLDAPWrap);
  1059. _ASSERT(pfTerminateIndicator);
  1060. if( pISMTPServerEx )
  1061. pISMTPServerEx->AddRef();
  1062. pCLDAPWrap = m_pCPLDAPWrap;
  1063. pCLDAPWrap->AddRef();
  1064. //
  1065. // If dwLdapError is LDAP_SERVER_DOWN, pres will be NULL and we simply
  1066. // have to complete all outstanding requests with that error
  1067. //
  1068. if ((pres == NULL) || (dwLdapError == LDAP_SERVER_DOWN)) {
  1069. _ASSERT(dwLdapError != 0);
  1070. INCREMENT_LDAP_COUNTER(GeneralCompletionFailures);
  1071. ErrorTrace(0, "Generic LDAP error %d 0x%08lx", dwLdapError, dwLdapError);
  1072. CancelAllSearches( LdapErrorToHr( dwLdapError ) );
  1073. goto CLEANUP;
  1074. }
  1075. //
  1076. // We have a search specific result, find the search request and complete
  1077. // it.
  1078. //
  1079. _ASSERT( pres != NULL );
  1080. msgid = pres->lm_msgid;
  1081. DebugTrace(msgid, "Processing message %d 0x%08lx conn %08lx", pres->lm_msgid, pres->lm_msgid, (PLDAP)(pCLDAPWrap->GetPLDAP()));
  1082. while(preq == NULL) {
  1083. //
  1084. // Lookup the msgid in the list of pending requests.
  1085. //
  1086. AcquireSpinLock( &m_spinlockCompletion );
  1087. // EnterCriticalSection( &m_cs );
  1088. for (pli = m_listPendingRequests.Flink;
  1089. pli != &m_listPendingRequests && preq == NULL;
  1090. pli = pli->Flink) {
  1091. PPENDING_REQUEST preqCandidate;
  1092. preqCandidate = CONTAINING_RECORD(pli, PENDING_REQUEST, li);
  1093. if (preqCandidate->msgid == msgid) {
  1094. preq = preqCandidate;
  1095. RemoveEntryList( &preq->li );
  1096. //
  1097. // Clear the cancel bit here so we'll know if Cancel
  1098. // was recently requested later in this function
  1099. //
  1100. ClearCancel();
  1101. } else if (preqCandidate->msgid == -1) {
  1102. fNoMsgID = TRUE;
  1103. }
  1104. }
  1105. ReleaseSpinLock( &m_spinlockCompletion );
  1106. // LeaveCriticalSection( &m_cs );
  1107. if (preq == NULL) {
  1108. LPCSTR rgSubStrings[2];
  1109. CHAR szMsgId[11];
  1110. _snprintf(szMsgId, sizeof(szMsgId), "0x%08lx", msgid);
  1111. rgSubStrings[0] = szMsgId;
  1112. rgSubStrings[1] = m_szHost;
  1113. if(!fNoMsgID) {
  1114. ErrorTrace((LPARAM)this, "Couldn't find message ID %d in list of pending requests. Ignoring it", msgid);
  1115. //
  1116. // If we don't find the message in our list of pending requests,
  1117. // and we see no messages with ID == -1, it means
  1118. // some other thread came in and cancelled the search before we could
  1119. // process it. This is ok - just return.
  1120. //
  1121. CatLogEvent(
  1122. GetISMTPServerEx(),
  1123. CAT_EVENT_LDAP_UNEXPECTED_MSG,
  1124. 2,
  1125. rgSubStrings,
  1126. S_OK,
  1127. szMsgId,
  1128. LOGEVENT_FLAG_ALWAYS,
  1129. LOGEVENT_LEVEL_FIELD_ENGINEERING);
  1130. //
  1131. // It is also possible wldap32 is giving us a msgid we
  1132. // never dispatched. We need to re-release the
  1133. // semaphore count we consumed if this is the case
  1134. //
  1135. lOops++; // For the msgid we did not find
  1136. goto CLEANUP;
  1137. } else {
  1138. //
  1139. // So this(these) messages with id==-1 could possibly be
  1140. // the one we're looking for. If this is so, we just
  1141. // consumed a semaphore count of a different request.
  1142. // Block for our semaphore and keep track of the extra
  1143. // semaphore counts we are consuming (lOops)
  1144. //
  1145. CatLogEvent(
  1146. GetISMTPServerEx(),
  1147. CAT_EVENT_LDAP_PREMATURE_MSG,
  1148. 2,
  1149. rgSubStrings,
  1150. S_OK,
  1151. szMsgId,
  1152. LOGEVENT_FLAG_ALWAYS,
  1153. LOGEVENT_LEVEL_FIELD_ENGINEERING);
  1154. lOops++;
  1155. DebugTrace((LPARAM)this, "Couldn't find message ID %d in list of pending requests. Waiting retry #%d", msgid, lOops);
  1156. // Oops, we consumed a semaphore count not meant for us
  1157. _VERIFY(WaitForSingleObject(m_hOutstandingRequests, INFINITE) ==
  1158. WAIT_OBJECT_0);
  1159. if(*pfTerminateIndicator)
  1160. goto CLEANUP;
  1161. // Try again to find our request
  1162. fNoMsgID = FALSE;
  1163. }
  1164. }
  1165. }
  1166. _ASSERT(preq);
  1167. INCREMENT_LDAP_COUNTER(SearchesCompleted);
  1168. DECREMENT_LDAP_COUNTER(PendingSearches);
  1169. //
  1170. // Determine wether or not this is the final completion call (by
  1171. // default fFinalCompletion is TRUE)
  1172. //
  1173. fPagedSearch = (preq->pldap_search != NULL);
  1174. if(fPagedSearch) {
  1175. INCREMENT_LDAP_COUNTER(PagedSearchesCompleted);
  1176. if (dwLdapError == ERROR_SUCCESS) {
  1177. ULONG ulTotalCount;
  1178. //
  1179. // The result is one page of the search. Dispatch a request
  1180. // for the next page
  1181. //
  1182. // First, call ldap_get_paged_count (required so wldap32 can
  1183. // "save off the cookie that the server sent to resumt the
  1184. // search")
  1185. //
  1186. dwLdapError = ldap_get_paged_count(
  1187. pCLDAPWrap->GetPLDAP(),
  1188. preq->pldap_search,
  1189. &ulTotalCount,
  1190. pres);
  1191. if(dwLdapError == ERROR_SUCCESS) {
  1192. //
  1193. // Dispatch a search for the next page
  1194. //
  1195. dwLdapError = ldap_get_next_page(
  1196. pCLDAPWrap->GetPLDAP(),
  1197. preq->pldap_search,
  1198. preq->dwPageSize,
  1199. (PULONG) &(preq->msgid));
  1200. if(dwLdapError == ERROR_SUCCESS) {
  1201. //
  1202. // Another request has been dispatched, so this was
  1203. // not the final search
  1204. //
  1205. INCREMENT_LDAP_COUNTER(Searches);
  1206. INCREMENT_LDAP_COUNTER(PagedSearches);
  1207. INCREMENT_LDAP_COUNTER(PendingSearches);
  1208. fFinalCompletion = FALSE;
  1209. ReleaseSemaphore( m_hOutstandingRequests, 1, NULL );
  1210. } else if(dwLdapError == LDAP_NO_RESULTS_RETURNED) {
  1211. //
  1212. // We have the last page now. The paged search will be
  1213. // freed in cleanup code below.
  1214. //
  1215. DebugTrace(
  1216. (LPARAM)this,
  1217. "ldap_get_next_page returned LDAP_NO_RESULTS_RETURNED. Paged search completed.");
  1218. } else {
  1219. LogLdapError(dwLdapError, "ldap_get_next_page(GetPLDAP(),preq->pldap_search,%d,&(preq->msgid), PLDAP = 0x%08lx",
  1220. preq->dwPageSize,
  1221. pCLDAPWrap->GetPLDAP());
  1222. INCREMENT_LDAP_COUNTER(SearchFailures);
  1223. INCREMENT_LDAP_COUNTER(PagedSearchFailures);
  1224. }
  1225. } else {
  1226. LogLdapError(dwLdapError, "ldap_get_paged_count, PLDAP = 0x%08lx",
  1227. pCLDAPWrap->GetPLDAP());
  1228. }
  1229. }
  1230. }
  1231. //
  1232. // Call the completion routine of the Request.
  1233. //
  1234. if ( (dwLdapError == ERROR_SUCCESS)
  1235. || ((dwLdapError == LDAP_NO_RESULTS_RETURNED) && fPagedSearch) ) {
  1236. CallCompletion(
  1237. preq,
  1238. pres,
  1239. S_OK,
  1240. fFinalCompletion);
  1241. //
  1242. // CallCompletion will handle the freeing of pres
  1243. //
  1244. pres = NULL;
  1245. } else {
  1246. DebugTrace(0, "Search request %d completed with LDAP error 0x%x",
  1247. msgid, dwLdapError);
  1248. ErrorTrace(msgid, "ProcError %d 0x%08lx msgid %d 0x%08lx conn %08lx", dwLdapError, dwLdapError, pres->lm_msgid, pres->lm_msgid, (PLDAP)(pCLDAPWrap->GetPLDAP()));
  1249. INCREMENT_LDAP_COUNTER(SearchCompletionFailures);
  1250. if(preq->pldap_search != NULL)
  1251. INCREMENT_LDAP_COUNTER(PagedSearchCompletionFailures);
  1252. CallCompletion(
  1253. preq,
  1254. NULL,
  1255. LdapErrorToHr( dwLdapError ),
  1256. fFinalCompletion);
  1257. //
  1258. // CallCompletion will handle the freeing of pres
  1259. // BUGBUG ??? No it won't; we passed in NULL, not pres. It has nothing to clean up,
  1260. // so leave it at its present value so that cleanup code below can take a crack at it.
  1261. //
  1262. // pres = NULL;
  1263. //
  1264. // It is unsafe to touch CLdapConnection past here -- it may
  1265. // be deleted (or waiting in the destructor)
  1266. //
  1267. }
  1268. if (!fFinalCompletion) {
  1269. //
  1270. // If we were asked to cancel all searches between the time we
  1271. // got the preq pointer out of the list and now, abandon the
  1272. // pending search, and notify our caller we're cancelled
  1273. //
  1274. AcquireSpinLock(&m_spinlockCompletion);
  1275. if(CancelOccured()) {
  1276. ReleaseSpinLock(&m_spinlockCompletion);
  1277. AbandonRequest(preq);
  1278. CallCompletion(
  1279. preq,
  1280. NULL,
  1281. HRESULT_FROM_WIN32(ERROR_CANCELLED),
  1282. TRUE);
  1283. delete preq;
  1284. } else {
  1285. //
  1286. // we're doing another async wldap32 operation for the
  1287. // next page. Put preq back in the pending request list,
  1288. // updating the tick count first
  1289. //
  1290. preq->dwTickCount = GetTickCount();
  1291. InsertTailList(&m_listPendingRequests, &(preq->li));
  1292. ReleaseSpinLock(&m_spinlockCompletion);
  1293. }
  1294. }
  1295. CLEANUP:
  1296. //
  1297. // Release the extra semaphore counts we might have consumed
  1298. //
  1299. if((*pfTerminateIndicator == FALSE) && (lOops > 0)) {
  1300. ReleaseSemaphore(m_hOutstandingRequests, lOops, NULL);
  1301. }
  1302. if(fFinalCompletion)
  1303. {
  1304. if (fPagedSearch) {
  1305. //
  1306. // Free the paged search
  1307. //
  1308. dwLdapError = ldap_search_abandon_page(
  1309. pCLDAPWrap->GetPLDAP(),
  1310. preq->pldap_search);
  1311. if(dwLdapError != LDAP_SUCCESS)
  1312. {
  1313. ErrorTrace((LPARAM)this, "ldap_search_abandon_page failed %08lx", dwLdapError);
  1314. //
  1315. // Nothing we can do if we can't free the search
  1316. //
  1317. LogLdapError(
  1318. pISMTPServerEx,
  1319. dwLdapError,
  1320. "ldap_search_abandon_page, PLDAP = 0x%08lx",
  1321. pCLDAPWrap->GetPLDAP());
  1322. }
  1323. }
  1324. delete preq;
  1325. }
  1326. if(pres) {
  1327. FreeResult(pres);
  1328. }
  1329. if(pCLDAPWrap)
  1330. pCLDAPWrap->Release();
  1331. if(pISMTPServerEx)
  1332. pISMTPServerEx->Release();
  1333. CatFunctLeaveEx((LPARAM)this);
  1334. }
  1335. //+----------------------------------------------------------------------------
  1336. //
  1337. // Function: LdapCompletionThread
  1338. //
  1339. // Synopsis: Friend function of CLdapConnection that handles results
  1340. // received for requests sent via CLdapConnection::AsyncSearch.
  1341. //
  1342. // Arguments: [ctx] -- Opaque pointer to the CLdapConnection instance which
  1343. // we will service.
  1344. //
  1345. // Returns: Always ERROR_SUCCESS.
  1346. //
  1347. //-----------------------------------------------------------------------------
  1348. DWORD WINAPI LdapCompletionThread(
  1349. LPVOID ctx)
  1350. {
  1351. CatFunctEnterEx((LPARAM)ctx, "LdapCompletionThread");
  1352. CLdapConnection *pConn = (CLdapConnection *) ctx;
  1353. int nResultCode = LDAP_RES_SEARCH_RESULT;
  1354. DWORD dwError;
  1355. PLDAPMessage pres;
  1356. BOOL fTerminate = FALSE;
  1357. //
  1358. // Make sure we have a friend CLdapConnection object!
  1359. //
  1360. _ASSERT( pConn != NULL );
  1361. //
  1362. // Tell our friend to set fTerminate to true when it wants us to return.
  1363. //
  1364. pConn->SetTerminateCompletionThreadIndicator( &fTerminate );
  1365. //
  1366. // Sit in a loop waiting on results for AsyncSearch requests issued by
  1367. // our pConn friend. Do so until our pConn friend terminates the
  1368. // LDAP connection we are servicing.
  1369. //
  1370. do {
  1371. pConn->CancelExpiredSearches(
  1372. pConn->LdapErrorToHr( LDAP_TIMELIMIT_EXCEEDED ));
  1373. dwError = WaitForSingleObject(
  1374. pConn->m_hOutstandingRequests, INFINITE );
  1375. if (dwError != WAIT_OBJECT_0 || fTerminate)
  1376. break;
  1377. DebugTrace((LPARAM)pConn, "Calling ldap_result now");
  1378. nResultCode = ldap_result(
  1379. pConn->GetPLDAP(), // LDAP connection to use
  1380. (ULONG) LDAP_RES_ANY, // Search msgid
  1381. LDAP_MSG_ALL, // Get all results
  1382. &(CLdapConnection::m_ldaptimeout), // Timeout
  1383. &pres);
  1384. if (fTerminate)
  1385. break;
  1386. if (nResultCode != 0) {
  1387. //
  1388. // We are supposed to call ldap_result2error to find out what the
  1389. // result specific error code is.
  1390. //
  1391. dwError = ldap_result2error( pConn->GetPLDAP(), pres, FALSE );
  1392. if ((dwError == LDAP_SUCCESS) ||
  1393. (dwError == LDAP_RES_SEARCH_RESULT) ||
  1394. (dwError == LDAP_REFERRAL_V2)) {
  1395. //
  1396. // Good, we have a search result. Tell our friend pConn to handle
  1397. // it.
  1398. //
  1399. pConn->ProcessAsyncResult( pres, ERROR_SUCCESS, &fTerminate);
  1400. } else {
  1401. if (pres != NULL) {
  1402. pConn->LogLdapError(dwError, "ldap_result2error, PLDAP = 0x%08lx, msgid = %d",
  1403. pConn->GetPLDAP(), pres->lm_msgid);
  1404. ErrorTrace(
  1405. (LPARAM)pConn,
  1406. "LdapCompletionThread - error from ldap_result() for non NULL pres - 0x%x (%d)",
  1407. dwError, dwError);
  1408. pConn->ProcessAsyncResult( pres, dwError, &fTerminate);
  1409. } else {
  1410. pConn->LogLdapError(dwError, "ldap_result2error, PLDAP = 0x%08lx, pres = NULL",
  1411. pConn->GetPLDAP());
  1412. ErrorTrace(
  1413. (LPARAM)pConn,
  1414. "LdapCompletionThread - generic error from ldap_result() 0x%x (%d)",
  1415. dwError, dwError);
  1416. ErrorTrace(
  1417. (LPARAM)pConn,
  1418. "nResultCode = %d", nResultCode);
  1419. dwError = LDAP_SERVER_DOWN;
  1420. pConn->ProcessAsyncResult( NULL, dwError, &fTerminate);
  1421. }
  1422. }
  1423. } else {
  1424. pConn->LogLdapError(nResultCode, "ldap_result (timeout), PLDAP = 0x%08lx",
  1425. pConn->GetPLDAP());
  1426. pConn->ProcessAsyncResult( NULL, LDAP_SERVER_DOWN, &fTerminate);
  1427. }
  1428. } while ( !fTerminate );
  1429. CatFunctLeaveEx((LPARAM)pConn);
  1430. return( 0 );
  1431. }
  1432. //+----------------------------------------------------------------------------
  1433. //
  1434. // Function: CLdapConnection::CancelExpiredSearches
  1435. //
  1436. // Synopsis: Cancels searches in the pending request queue that have msgids
  1437. // other than -1 that have been there for more than
  1438. // m_dwLdapRequestTimeLimit seconds. Completions are called
  1439. // on each of these pending requests with hr as the failure code.
  1440. //
  1441. // Arguments: [hr] -- completion status code.
  1442. //
  1443. // Returns: Nothing.
  1444. //
  1445. //-----------------------------------------------------------------------------
  1446. VOID CLdapConnection::CancelExpiredSearches(HRESULT hr)
  1447. {
  1448. PLIST_ENTRY ple;
  1449. PPENDING_REQUEST preq;
  1450. BOOL fDone = FALSE;
  1451. DWORD dwTickCount;
  1452. LPCSTR rgSubStrings[2];
  1453. CHAR szMsgId[11];
  1454. //
  1455. // check for expired pending requests. We will start at the head of the
  1456. // pending request queue because the ones at the front are the oldest.
  1457. // We will ignore all pending requests that have a msgid of -1, because
  1458. // they may be removed from the list in AsyncSearch if the search issue
  1459. // failed, in which case we don't want to remove the pending request here.
  1460. //
  1461. dwTickCount = GetTickCount();
  1462. while (!fDone) {
  1463. AcquireSpinLock(&m_spinlockCompletion);
  1464. ple = m_listPendingRequests.Flink;
  1465. if (ple == &m_listPendingRequests) {
  1466. //
  1467. // no pending requests
  1468. //
  1469. preq = NULL;
  1470. } else {
  1471. preq = CONTAINING_RECORD(ple, PENDING_REQUEST, li);
  1472. if ((preq->msgid != -1) &&
  1473. (dwTickCount - preq->dwTickCount > m_dwLdapRequestTimeLimit * 1000)) {
  1474. //
  1475. // this request has expired
  1476. //
  1477. RemoveEntryList( &preq->li );
  1478. } else {
  1479. //
  1480. // request has not expired or has msgid == -1
  1481. //
  1482. preq = NULL;
  1483. }
  1484. }
  1485. ReleaseSpinLock(&m_spinlockCompletion);
  1486. if (preq) {
  1487. _snprintf(szMsgId, sizeof(szMsgId), "0x%08lx", preq->msgid);
  1488. rgSubStrings[0] = szMsgId;
  1489. rgSubStrings[1] = m_szHost;
  1490. CatLogEvent(
  1491. GetISMTPServerEx(),
  1492. CAT_EVENT_LDAP_CAT_TIME_LIMIT,
  1493. 2,
  1494. rgSubStrings,
  1495. S_OK,
  1496. szMsgId,
  1497. LOGEVENT_FLAG_ALWAYS,
  1498. LOGEVENT_LEVEL_FIELD_ENGINEERING);
  1499. //
  1500. // we have an expired request that has been removed from the queue
  1501. //
  1502. AbandonRequest(preq);
  1503. CallCompletion(
  1504. preq,
  1505. NULL,
  1506. hr,
  1507. TRUE);
  1508. delete preq;
  1509. //
  1510. // We need to down the semaphore count since it was upped in AsyncSearch.
  1511. // It is possible that the semaphore hasn't been upped yet due to timing,
  1512. // but the thread that queued the request is about to up the semaphore,
  1513. // so we have to wait for it. This should be *extremely* rare, as
  1514. // the thread issuing the request would have to go unscheduled for the
  1515. // entire duration of the request time limit (m_dwLdapRequestTimeLimit).
  1516. //
  1517. _VERIFY(WaitForSingleObject(m_hOutstandingRequests, INFINITE) ==
  1518. WAIT_OBJECT_0);
  1519. } else {
  1520. fDone = TRUE;
  1521. }
  1522. }
  1523. }
  1524. //+----------------------------------------------------------------------------
  1525. //
  1526. // Function: CLdapConnection::GetFirstEntry
  1527. //
  1528. // Synopsis: Retrieves the first entry from a search result. The result is
  1529. // returned as a pointer to an opaque type; all one can do is
  1530. // query the attribute-values of the entry using
  1531. // GetAttributeValues
  1532. //
  1533. // Arguments: [pResult] -- The result set returned by Search.
  1534. // [ppEntry] -- On successful return, pointer to first entry in
  1535. // result is returned here.
  1536. //
  1537. // Returns: TRUE if successful, FALSE otherwise.
  1538. //
  1539. //-----------------------------------------------------------------------------
  1540. HRESULT CLdapConnection::GetFirstEntry(
  1541. PLDAPRESULT pResult,
  1542. PLDAPENTRY *ppEntry)
  1543. {
  1544. CatFunctEnter("CLdapConnection::GetFirstEntry");
  1545. PLDAPMessage pres = (PLDAPMessage) pResult;
  1546. _ASSERT( m_pCPLDAPWrap != NULL );
  1547. _ASSERT( pResult != NULL );
  1548. _ASSERT( ppEntry != NULL );
  1549. *ppEntry = (PLDAPENTRY) ldap_first_entry(GetPLDAP(), pres);
  1550. if (*ppEntry == NULL) {
  1551. DebugTrace(0, "GetFirstEntry failed!");
  1552. CatFunctLeave();
  1553. return( HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) );
  1554. } else {
  1555. CatFunctLeave();
  1556. return( S_OK );
  1557. }
  1558. }
  1559. //+----------------------------------------------------------------------------
  1560. //
  1561. // Function: CLdapConnection::GetNextEntry
  1562. //
  1563. // Synopsis: Retrieves the next entry from a result set.
  1564. //
  1565. // Arguments: [pLastEntry] -- The last entry returned.
  1566. // [ppEntry] -- The next entry in the result set.
  1567. //
  1568. // Returns: TRUE if successful, FALSE otherwise.
  1569. //
  1570. //-----------------------------------------------------------------------------
  1571. HRESULT CLdapConnection::GetNextEntry(
  1572. PLDAPENTRY pLastEntry,
  1573. PLDAPENTRY *ppEntry)
  1574. {
  1575. CatFunctEnter("CLdapConnection::GetNextEntry");
  1576. PLDAPMessage plastentry = (PLDAPMessage) pLastEntry;
  1577. _ASSERT( m_pCPLDAPWrap != NULL );
  1578. _ASSERT( pLastEntry != NULL );
  1579. _ASSERT( ppEntry != NULL );
  1580. *ppEntry = (PLDAPENTRY) ldap_next_entry( GetPLDAP(), plastentry );
  1581. if (*ppEntry == NULL) {
  1582. CatFunctLeave();
  1583. return( HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) );
  1584. } else {
  1585. CatFunctLeave();
  1586. return( S_OK );
  1587. }
  1588. }
  1589. //+----------------------------------------------------------------------------
  1590. //
  1591. // Function: CLdapConnection::GetAttributeValues
  1592. //
  1593. // Synopsis: Retrieves the values of a specified attribute of the given
  1594. // entry.
  1595. //
  1596. // Arguments: [pEntry] -- The entry whose attribute value is desired.
  1597. // [szAttribute] -- The attribute whose value is desired.
  1598. // [prgszValues] -- On return, contains pointer to array of
  1599. // string values
  1600. //
  1601. // Returns: TRUE if successful, FALSE otherwise
  1602. //
  1603. //-----------------------------------------------------------------------------
  1604. HRESULT CLdapConnection::GetAttributeValues(
  1605. PLDAPENTRY pEntry,
  1606. LPCSTR szAttribute,
  1607. LPSTR *prgszValues[])
  1608. {
  1609. CatFunctEnter("CLdapConnection::GetAttributeValues");
  1610. _ASSERT(m_pCPLDAPWrap != NULL);
  1611. _ASSERT(pEntry != NULL);
  1612. _ASSERT(szAttribute != NULL);
  1613. _ASSERT(prgszValues != NULL);
  1614. *prgszValues = ldap_get_values(
  1615. GetPLDAP(),
  1616. (PLDAPMessage) pEntry,
  1617. (LPSTR) szAttribute);
  1618. if ((*prgszValues) == NULL) {
  1619. CatFunctLeave();
  1620. return( HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) );
  1621. } else {
  1622. CatFunctLeave();
  1623. return( S_OK );
  1624. }
  1625. }
  1626. //+----------------------------------------------------------------------------
  1627. //
  1628. // Function: CLdapConnection::FreeResult
  1629. //
  1630. // Synopsis: Frees a search result and all its entries.
  1631. //
  1632. // Arguments: [pResult] -- Result retrieved via Search.
  1633. //
  1634. // Returns: Nothing
  1635. //
  1636. //-----------------------------------------------------------------------------
  1637. VOID CLdapConnection::FreeResult(
  1638. PLDAPRESULT pResult)
  1639. {
  1640. CatFunctEnter("CLdapConnection::FreeResult");
  1641. _ASSERT( pResult != NULL );
  1642. ldap_msgfree( (PLDAPMessage) pResult );
  1643. CatFunctLeave();
  1644. }
  1645. //+----------------------------------------------------------------------------
  1646. //
  1647. // Function: CLdapConnection::FreeValues
  1648. //
  1649. // Synopsis: Frees the attribute values retrieved from GetAttributeValues
  1650. //
  1651. // Arguments: [rgszValues] -- The array of values to free.
  1652. //
  1653. // Returns: Nothing
  1654. //
  1655. //-----------------------------------------------------------------------------
  1656. VOID CLdapConnection::FreeValues(
  1657. LPSTR rgszValues[])
  1658. {
  1659. CatFunctEnter("CLdapConnection::FreeValues");
  1660. _ASSERT( rgszValues != NULL );
  1661. ldap_value_free( rgszValues );
  1662. CatFunctLeave();
  1663. }
  1664. //+----------------------------------------------------------------------------
  1665. //
  1666. // Function: CLdapConnection::ModifyAttributes
  1667. //
  1668. // Synopsis: Adds, deletes, or modifies attributes on a DS object.
  1669. //
  1670. // Arguments: [nOperation] -- One of LDAP_MOD_ADD, LDAP_MOD_DELETE, or
  1671. // LDAP_MOD_REPLACE.
  1672. // [szDN] -- DN of the DS object.
  1673. // [rgszAttributes] -- The list of attributes
  1674. // [rgrgszValues] -- The list of values associated with each
  1675. // attribute. rgrgszValues[0] points to an array of values
  1676. // associated with rgszAttribute[0]; rgrgszValues[1] points
  1677. // to an array of values associated with rgszAttribute[1];
  1678. // and so on.
  1679. //
  1680. // Returns: TRUE if success, FALSE otherwise.
  1681. //
  1682. //-----------------------------------------------------------------------------
  1683. HRESULT CLdapConnection::ModifyAttributes(
  1684. int nOperation,
  1685. LPCSTR szDN,
  1686. LPCSTR *rgszAttributes,
  1687. LPCSTR *rgrgszValues[])
  1688. {
  1689. //$$BUGBUG: Legacy code
  1690. CatFunctEnter("CLdapConnection::ModifyAttributes");
  1691. int i, cAttr;
  1692. PLDAPMod *prgMods = NULL, rgMods;
  1693. DWORD ldapErr;
  1694. _ASSERT( m_pCPLDAPWrap != NULL );
  1695. _ASSERT( nOperation == LDAP_MOD_ADD ||
  1696. nOperation == LDAP_MOD_DELETE ||
  1697. nOperation == LDAP_MOD_REPLACE );
  1698. _ASSERT( szDN != NULL );
  1699. _ASSERT( rgszAttributes != NULL );
  1700. _ASSERT( rgrgszValues != NULL || nOperation == LDAP_MOD_DELETE );
  1701. for (cAttr = 0; rgszAttributes[ cAttr ] != NULL; cAttr++) {
  1702. // NOTHING TO DO.
  1703. }
  1704. //
  1705. // Below, we allocate a single chunk of memory that contains an array
  1706. // of pointers to LDAPMod structures. Immediately following that array is
  1707. // the space for the LDAPMod structures themselves.
  1708. //
  1709. prgMods = (PLDAPMod *) new BYTE[ (cAttr+1) *
  1710. (sizeof(PLDAPMod) + sizeof(LDAPMod)) ];
  1711. if (prgMods != NULL) {
  1712. rgMods = (PLDAPMod) &prgMods[cAttr+1];
  1713. for (i = 0; i < cAttr; i++) {
  1714. rgMods[i].mod_op = nOperation;
  1715. rgMods[i].mod_type = (LPSTR) rgszAttributes[i];
  1716. if (rgrgszValues != NULL) {
  1717. rgMods[i].mod_vals.modv_strvals = (LPSTR *)rgrgszValues[i];
  1718. } else {
  1719. rgMods[i].mod_vals.modv_strvals = NULL;
  1720. }
  1721. prgMods[i] = &rgMods[i];
  1722. }
  1723. prgMods[i] = NULL; // Null terminate the array
  1724. ldapErr = ldap_modify_s( GetPLDAP(), (LPSTR) szDN, prgMods );
  1725. delete [] prgMods;
  1726. } else {
  1727. ldapErr = LDAP_NO_MEMORY;
  1728. }
  1729. if (ldapErr != LDAP_SUCCESS) {
  1730. DebugTrace(LDAP_CONN_DBG, "Status = 0x%x", ldapErr);
  1731. CatFunctLeave();
  1732. return( LdapErrorToHr( ldapErr) );
  1733. } else {
  1734. CatFunctLeave();
  1735. return( S_OK );
  1736. }
  1737. }
  1738. //+----------------------------------------------------------------------------
  1739. //
  1740. // Function: CLdapConnection::LdapErrorToWin32
  1741. //
  1742. // Synopsis: Converts LDAP errors to Win32
  1743. //
  1744. // Arguments: [dwLdapError] -- The LDAP error to convert
  1745. //
  1746. // Returns: Equivalent Win32 error
  1747. //
  1748. //-----------------------------------------------------------------------------
  1749. HRESULT CLdapConnection::LdapErrorToHr(
  1750. DWORD dwLdapError)
  1751. {
  1752. DWORD dwErr;
  1753. CatFunctEnter("LdapErrorToWin32");
  1754. switch (dwLdapError) {
  1755. case LDAP_SUCCESS:
  1756. dwErr = NO_ERROR;
  1757. break;
  1758. case LDAP_OPERATIONS_ERROR:
  1759. case LDAP_PROTOCOL_ERROR:
  1760. dwErr = CAT_E_DBFAIL;
  1761. break;
  1762. case LDAP_TIMELIMIT_EXCEEDED:
  1763. dwErr = ERROR_TIMEOUT;
  1764. break;
  1765. case LDAP_SIZELIMIT_EXCEEDED:
  1766. dwErr = ERROR_DISK_FULL;
  1767. break;
  1768. case LDAP_AUTH_METHOD_NOT_SUPPORTED:
  1769. dwErr = ERROR_NOT_SUPPORTED;
  1770. break;
  1771. case LDAP_STRONG_AUTH_REQUIRED:
  1772. dwErr = ERROR_ACCESS_DENIED;
  1773. break;
  1774. case LDAP_ADMIN_LIMIT_EXCEEDED:
  1775. dwErr = CAT_E_DBFAIL;
  1776. break;
  1777. case LDAP_ATTRIBUTE_OR_VALUE_EXISTS:
  1778. dwErr = ERROR_FILE_EXISTS;
  1779. break;
  1780. case LDAP_NO_SUCH_OBJECT:
  1781. dwErr = ERROR_FILE_NOT_FOUND;
  1782. break;
  1783. case LDAP_INAPPROPRIATE_AUTH:
  1784. dwErr = ERROR_ACCESS_DENIED;
  1785. break;
  1786. case LDAP_INVALID_CREDENTIALS:
  1787. dwErr = ERROR_LOGON_FAILURE;
  1788. break;
  1789. case LDAP_INSUFFICIENT_RIGHTS:
  1790. dwErr = ERROR_ACCESS_DENIED;
  1791. break;
  1792. case LDAP_BUSY:
  1793. dwErr = ERROR_BUSY;
  1794. break;
  1795. case LDAP_UNAVAILABLE:
  1796. dwErr = CAT_E_DBCONNECTION;
  1797. break;
  1798. case LDAP_UNWILLING_TO_PERFORM:
  1799. dwErr = CAT_E_TRANX_FAILED;
  1800. break;
  1801. case LDAP_ALREADY_EXISTS:
  1802. dwErr = ERROR_FILE_EXISTS;
  1803. break;
  1804. case LDAP_OTHER:
  1805. dwErr = CAT_E_TRANX_FAILED;
  1806. break;
  1807. case LDAP_SERVER_DOWN:
  1808. dwErr = CAT_E_DBCONNECTION;
  1809. break;
  1810. case LDAP_LOCAL_ERROR:
  1811. dwErr = CAT_E_TRANX_FAILED;
  1812. break;
  1813. case LDAP_NO_MEMORY:
  1814. dwErr = ERROR_OUTOFMEMORY;
  1815. break;
  1816. case LDAP_TIMEOUT:
  1817. dwErr = ERROR_TIMEOUT;
  1818. break;
  1819. case LDAP_CONNECT_ERROR:
  1820. dwErr = CAT_E_DBCONNECTION;
  1821. break;
  1822. case LDAP_NOT_SUPPORTED:
  1823. dwErr = ERROR_NOT_SUPPORTED;
  1824. break;
  1825. default:
  1826. DebugTrace(
  1827. 0,
  1828. "LdapErrorToWin32: No equivalent for ldap error 0x%x",
  1829. dwLdapError);
  1830. dwErr = dwLdapError;
  1831. break;
  1832. }
  1833. DebugTrace(
  1834. LDAP_CONN_DBG,
  1835. "LdapErrorToWin32: Ldap Error 0x%x == Win32 error %d (0x%x) == HResult %d (0x%x)",
  1836. dwLdapError, dwErr, dwErr, HRESULT_FROM_WIN32(dwErr), HRESULT_FROM_WIN32(dwErr));
  1837. CatFunctLeave();
  1838. return( HRESULT_FROM_WIN32(dwErr) );
  1839. }
  1840. //+----------------------------------------------------------------------------
  1841. //
  1842. // Function: CLdapConnection::CreateCompletionThreadIfNeeded
  1843. //
  1844. // Synopsis: Helper function to create a completion thread that will
  1845. // watch for results of async ldap searches.
  1846. //
  1847. // Arguments: None
  1848. //
  1849. // Returns: TRUE if success, FALSE otherwise
  1850. //
  1851. //-----------------------------------------------------------------------------
  1852. HRESULT CLdapConnection::CreateCompletionThreadIfNeeded()
  1853. {
  1854. HRESULT hr = S_OK;
  1855. BOOL fLocked = FALSE;
  1856. CatFunctEnterEx((LPARAM)this, "CLdapConnection::CreateCompletionThreadIfNeeded");
  1857. //
  1858. // Test to see if we already have a completion thread...
  1859. //
  1860. if (m_hCompletionThread != INVALID_HANDLE_VALUE) {
  1861. hr = S_OK;
  1862. goto CLEANUP;
  1863. }
  1864. //
  1865. // Looks like we'll have to create a completion thread. Lets acquire
  1866. // m_spinlockCompletion so only one of us tries to do this...
  1867. //
  1868. AcquireSpinLock( &m_spinlockCompletion );
  1869. // EnterCriticalSection( &m_cs );
  1870. fLocked = TRUE;
  1871. //
  1872. // Check one more time inside the lock - someone might have beaten us to
  1873. // it.
  1874. //
  1875. if (m_hOutstandingRequests == INVALID_HANDLE_VALUE) {
  1876. m_hOutstandingRequests = CreateSemaphore(NULL, 0, LONG_MAX, NULL);
  1877. if (m_hOutstandingRequests == NULL) {
  1878. m_hOutstandingRequests = INVALID_HANDLE_VALUE;
  1879. hr = HRESULT_FROM_WIN32(GetLastError());
  1880. ERROR_LOG("CreateSemaphore");
  1881. goto CLEANUP;
  1882. }
  1883. }
  1884. if (m_hCompletionThread == INVALID_HANDLE_VALUE) {
  1885. //
  1886. // Create the completion thread
  1887. //
  1888. m_hCompletionThread =
  1889. CreateThread(
  1890. NULL, // Security Attributes
  1891. 0, // Initial stack - default
  1892. LdapCompletionThread,// Starting address
  1893. (LPVOID) this, // Param to LdapCompletionRtn
  1894. 0, // Create Flags
  1895. &m_idCompletionThread);// Receives thread id
  1896. if (m_hCompletionThread == NULL) {
  1897. m_hCompletionThread = INVALID_HANDLE_VALUE;
  1898. hr = HRESULT_FROM_WIN32(GetLastError());
  1899. ERROR_LOG("CreateThread");
  1900. goto CLEANUP;
  1901. }
  1902. }
  1903. CLEANUP:
  1904. if(fLocked) {
  1905. ReleaseSpinLock( &m_spinlockCompletion );
  1906. // LeaveCriticalSection( &m_cs );
  1907. }
  1908. DebugTrace((LPARAM)this, "returning %08lx", hr);
  1909. CatFunctLeaveEx((LPARAM)this);
  1910. return hr;
  1911. }
  1912. //+----------------------------------------------------------------------------
  1913. //
  1914. // Function: CLdapConnection::SetTerminateCompletionThreadIndicator
  1915. //
  1916. // Synopsis: Callback for our LdapCompletionThread to set a pointer to a
  1917. // boolean that will be set to TRUE when the LdapCompletionThread
  1918. // needs to terminate.
  1919. //
  1920. // Arguments: [pfTerminateCompletionThreadIndicator] -- Pointer to boolean
  1921. // which will be set to true when the completion thread should
  1922. // terminate.
  1923. //
  1924. // Returns: Nothing
  1925. //
  1926. //-----------------------------------------------------------------------------
  1927. VOID CLdapConnection::SetTerminateCompletionThreadIndicator(
  1928. BOOL *pfTerminateCompletionThreadIndicator)
  1929. {
  1930. _ASSERT(pfTerminateCompletionThreadIndicator);
  1931. InterlockedExchangePointer(
  1932. (PVOID *) &m_pfTerminateCompletionThreadIndicator,
  1933. (PVOID) pfTerminateCompletionThreadIndicator);
  1934. if(m_fTerminating) {
  1935. //
  1936. // We may have decided to terminate before the
  1937. // LdapCompletionThread had the chance to call this function.
  1938. // If this is the case, we still need to set the thread's
  1939. // terminate indicator to true. We call
  1940. // SetTerminateIndicatorTrue() to accomplish this. It uses
  1941. // interlocked functions to ensure that the terminate
  1942. // indicator pointer is not set to true more than once.
  1943. //
  1944. SetTerminateIndicatorTrue();
  1945. }
  1946. }
  1947. //+----------------------------------------------------------------------------
  1948. //
  1949. // Function: CLdapConnection::InsertPendingRequest
  1950. //
  1951. // Synopsis: Inserts a new PENDING_REQUEST record in the m_pPendingHead
  1952. // list so that the completion thread will find it when the
  1953. // search result is available.
  1954. //
  1955. // Arguments: [preq] -- The PENDING_REQUEST record to insert.
  1956. //
  1957. // Returns: Nothing, this always succeeds.
  1958. //
  1959. //-----------------------------------------------------------------------------
  1960. VOID CLdapConnection::InsertPendingRequest(
  1961. PPENDING_REQUEST preq)
  1962. {
  1963. AcquireSpinLock( &m_spinlockCompletion );
  1964. preq->dwTickCount = GetTickCount();
  1965. InsertTailList( &m_listPendingRequests, &preq->li );
  1966. ReleaseSpinLock( &m_spinlockCompletion );
  1967. }
  1968. //+----------------------------------------------------------------------------
  1969. //
  1970. // Function: CLdapConnection::RemovePendingRequest
  1971. //
  1972. // Synopsis: Removes a PENDING_REQUEST record from the
  1973. // m_listPendingRequests list.
  1974. //
  1975. // Arguments: [preq] -- The PENDING_REQUEST record to remove
  1976. //
  1977. // Returns: Nothing
  1978. //
  1979. //-----------------------------------------------------------------------------
  1980. VOID CLdapConnection::RemovePendingRequest(
  1981. PPENDING_REQUEST preq)
  1982. {
  1983. AcquireSpinLock( &m_spinlockCompletion );
  1984. RemoveEntryList( &preq->li );
  1985. ReleaseSpinLock( &m_spinlockCompletion );
  1986. }
  1987. //+----------------------------------------------------------------------------
  1988. //
  1989. // Function: CLdapConnectionCache::CLdapConnectionCache
  1990. //
  1991. // Synopsis: Constructor
  1992. //
  1993. // Arguments: None
  1994. //
  1995. // Returns: Nothing
  1996. //
  1997. //-----------------------------------------------------------------------------
  1998. #define MAX_HOST_CONNECTIONS 100
  1999. #define DEFAULT_HOST_CONNECTIONS 8
  2000. CLdapConnectionCache::CLdapConnectionCache(
  2001. ISMTPServerEx *pISMTPServerEx)
  2002. {
  2003. CatFunctEnter("CLdapConnectionCache::CLdapConnectionCache");
  2004. m_cRef = 0;
  2005. for (DWORD i = 0; i < LDAP_CONNECTION_CACHE_TABLE_SIZE; i++) {
  2006. InitializeListHead( &m_rgCache[i] );
  2007. }
  2008. m_nNextConnectionSkipCount = 0;
  2009. m_cMaxHostConnections = DEFAULT_HOST_CONNECTIONS;
  2010. m_cCachedConnections = 0;
  2011. ZeroMemory(&m_rgcCachedConnections, sizeof(m_rgcCachedConnections));
  2012. m_pISMTPServerEx = pISMTPServerEx;
  2013. if(m_pISMTPServerEx)
  2014. m_pISMTPServerEx->AddRef();
  2015. InitializeFromRegistry();
  2016. CatFunctLeave();
  2017. }
  2018. //+----------------------------------------------------------------------------
  2019. //
  2020. // Function: CLdapConnectionCache::InitializeFromRegistry
  2021. //
  2022. // Synopsis: Helper function that looks up parameters from the registry.
  2023. // The only configurable parameter is
  2024. // MAX_LDAP_CONNECTIONS_PER_HOST_KEY, which is read into
  2025. // m_cMaxHostConnections.
  2026. //
  2027. // Arguments: None
  2028. //
  2029. // Returns: Nothing.
  2030. //
  2031. //-----------------------------------------------------------------------------
  2032. VOID CLdapConnectionCache::InitializeFromRegistry()
  2033. {
  2034. HKEY hkey;
  2035. DWORD dwErr, dwType, dwValue, cbValue;
  2036. cbValue = sizeof(dwValue);
  2037. dwErr = RegOpenKey(HKEY_LOCAL_MACHINE, MAX_LDAP_CONNECTIONS_PER_HOST_KEY, &hkey);
  2038. if (dwErr == ERROR_SUCCESS) {
  2039. dwErr = RegQueryValueEx(
  2040. hkey,
  2041. MAX_LDAP_CONNECTIONS_PER_HOST_VALUE,
  2042. NULL,
  2043. &dwType,
  2044. (LPBYTE) &dwValue,
  2045. &cbValue);
  2046. RegCloseKey( hkey );
  2047. }
  2048. if (dwErr == ERROR_SUCCESS && dwType == REG_DWORD &&
  2049. dwValue > 0 && dwValue < MAX_HOST_CONNECTIONS) {
  2050. InterlockedExchange((PLONG) &m_cMaxHostConnections, (LONG)dwValue);
  2051. } else {
  2052. InterlockedExchange(
  2053. (PLONG) &m_cMaxHostConnections, (LONG) DEFAULT_HOST_CONNECTIONS);
  2054. }
  2055. }
  2056. //+----------------------------------------------------------------------------
  2057. //
  2058. // Function: CLdapConnectionCache::~CLdapConnectionCache
  2059. //
  2060. // Synopsis: Destructor
  2061. //
  2062. // Arguments: None
  2063. //
  2064. // Returns: Nothing
  2065. //
  2066. //-----------------------------------------------------------------------------
  2067. CLdapConnectionCache::~CLdapConnectionCache()
  2068. {
  2069. CatFunctEnter("CLdapConnectionCache::~CLdapConnectionCache");
  2070. unsigned short i;
  2071. for (i = 0; i < LDAP_CONNECTION_CACHE_TABLE_SIZE; i++) {
  2072. _ASSERT( IsListEmpty( &m_rgCache[i] ) );
  2073. }
  2074. if(m_pISMTPServerEx)
  2075. m_pISMTPServerEx->Release();
  2076. CatFunctLeave();
  2077. }
  2078. //+----------------------------------------------------------------------------
  2079. //
  2080. // Function: CLdapConnectionCache::AddRef
  2081. //
  2082. // Synopsis: Increment the refcount on this Connection Cache object.
  2083. // Indicates that there is one more CEmailIDLdapStore object that
  2084. // wants to avail of our services.
  2085. //
  2086. // Arguments: None
  2087. //
  2088. // Returns: Nothing
  2089. //
  2090. //-----------------------------------------------------------------------------
  2091. VOID CLdapConnectionCache::AddRef()
  2092. {
  2093. InterlockedIncrement( &m_cRef );
  2094. }
  2095. //+----------------------------------------------------------------------------
  2096. //
  2097. // Function: CLdapConnectionCache::Release
  2098. //
  2099. // Synopsis: Decrements the refcount on this connection cache object.
  2100. // Indicates that there is one less CEmailIDLdapStore object that
  2101. // wants to use our services.
  2102. //
  2103. // If the refcount drops to 0, all outstanding LDAP connections
  2104. // are destroyed!
  2105. //
  2106. // Arguments: None
  2107. //
  2108. // Returns: Nothing
  2109. //
  2110. //-----------------------------------------------------------------------------
  2111. VOID CLdapConnectionCache::Release()
  2112. {
  2113. unsigned short i;
  2114. CCachedLdapConnection *pcc;
  2115. LIST_ENTRY *pli;
  2116. _ASSERT( m_cRef > 0 );
  2117. if (InterlockedDecrement( &m_cRef ) == 0) {
  2118. for (i = 0; i < LDAP_CONNECTION_CACHE_TABLE_SIZE; i++) {
  2119. m_rgListLocks[i].ExclusiveLock();
  2120. for (pli = m_rgCache[i].Flink;
  2121. pli != &m_rgCache[i];
  2122. pli = m_rgCache[i].Flink) {
  2123. pcc = CONTAINING_RECORD(pli, CCachedLdapConnection, li);
  2124. RemoveEntryList( &pcc->li );
  2125. //
  2126. // Initialize li just in case someone attempts another
  2127. // removal
  2128. //
  2129. InitializeListHead( &pcc->li );
  2130. pcc->Disconnect();
  2131. pcc->ReleaseAndWaitForDestruction();
  2132. }
  2133. m_rgListLocks[i].ExclusiveUnlock();
  2134. }
  2135. }
  2136. }
  2137. //+----------------------------------------------------------------------------
  2138. //
  2139. // Function: CLdapConnectionCache::GetConnection
  2140. //
  2141. // Synopsis: Gets a connection to a given LDAP host
  2142. //
  2143. // Arguments: [szNamingContext] -- The container within the DS. Could be
  2144. // null to indicate root of the DS.
  2145. // [szHost] -- the LDAP Host
  2146. // [dwPort] -- the remote LDAP tcp port (if zero, LDAP_PORT is assumed)
  2147. // [szAccount] -- The account to be used to log in
  2148. // [szPassword] -- The password to be used to log in
  2149. // [bt] -- The bind method to use to log in
  2150. // [pCreateContext] -- a pointer to pass to
  2151. // CreateCachedLdapConnection when
  2152. // we need to create a new connection.
  2153. //
  2154. // Returns: Pointer to Connected LDAP connection or NULL
  2155. //
  2156. //-----------------------------------------------------------------------------
  2157. HRESULT CLdapConnectionCache::GetConnection(
  2158. CLdapConnection **ppConn,
  2159. LPSTR szHost,
  2160. DWORD dwPort,
  2161. LPSTR szNamingContext,
  2162. LPSTR szAccount,
  2163. LPSTR szPassword,
  2164. LDAP_BIND_TYPE bt,
  2165. PVOID pCreateContext)
  2166. {
  2167. CatFunctEnter("CLdapConnectionCache::GetConnection");
  2168. LPSTR szConnectionName = szHost;
  2169. unsigned short n;
  2170. LIST_ENTRY *pli;
  2171. CCachedLdapConnection *pcc;
  2172. LONG nSkipCount, nTargetSkipCount;
  2173. HRESULT hr = S_OK;
  2174. _ASSERT( szHost != NULL );
  2175. _ASSERT( szAccount != NULL );
  2176. _ASSERT( szPassword != NULL );
  2177. //
  2178. // See if we have a cached connection already.
  2179. //
  2180. n = Hash( szConnectionName );
  2181. m_rgListLocks[n].ShareLock();
  2182. nTargetSkipCount = m_nNextConnectionSkipCount % m_cMaxHostConnections;
  2183. for (nSkipCount = 0, pcc= NULL, pli = m_rgCache[n].Flink;
  2184. pli != &m_rgCache[n];
  2185. pli = pli->Flink) {
  2186. pcc = CONTAINING_RECORD(pli, CCachedLdapConnection, li);
  2187. if (pcc->IsEqual(szHost, dwPort, szNamingContext, szAccount, szPassword, bt)
  2188. && ((nSkipCount++ == nTargetSkipCount)
  2189. || (pcc->GetRefCount() == 1)))
  2190. break;
  2191. else
  2192. pcc = NULL;
  2193. }
  2194. if (pcc)
  2195. pcc->AddRef(); // Add the caller's reference
  2196. m_rgListLocks[n].ShareUnlock();
  2197. DebugTrace( LDAP_CCACHE_DBG, "Cached connection is 0x%x", pcc);
  2198. DebugTrace( LDAP_CCACHE_DBG,
  2199. "nTargetSkipCount = %d, nSkipCount = %d",
  2200. nTargetSkipCount, nSkipCount);
  2201. //
  2202. // If we don't have a cached connection, we need to create a new one.
  2203. //
  2204. if (pcc == NULL) {
  2205. m_rgListLocks[n].ExclusiveLock();
  2206. for (nSkipCount = 0, pcc = NULL, pli = m_rgCache[n].Flink;
  2207. pli != &m_rgCache[n];
  2208. pli = pli->Flink) {
  2209. pcc = CONTAINING_RECORD(pli, CCachedLdapConnection, li);
  2210. if (pcc->IsEqual(szHost, dwPort, szNamingContext, szAccount, szPassword, bt)
  2211. && (++nSkipCount == m_cMaxHostConnections ||
  2212. pcc->GetRefCount() == 1))
  2213. break;
  2214. else
  2215. pcc = NULL;
  2216. }
  2217. if (pcc) {
  2218. pcc->AddRef(); // Add the caller's reference
  2219. } else {
  2220. pcc = CreateCachedLdapConnection(
  2221. szHost, dwPort, szNamingContext,
  2222. szAccount, szPassword, bt, pCreateContext);
  2223. if (pcc != NULL) {
  2224. hr = pcc->Connect();
  2225. if (FAILED(hr)) {
  2226. ERROR_LOG("pcc->Connect");
  2227. ErrorTrace(LDAP_CCACHE_DBG, "Failed to connect 0x%x, hr = 0x%x", pcc, hr);
  2228. pcc->Release();
  2229. pcc = NULL;
  2230. } else {
  2231. pcc->AddRef(); // Reference for the connection in
  2232. // the cache
  2233. InsertTailList( &m_rgCache[n], &pcc->li );
  2234. m_cCachedConnections++;
  2235. m_rgcCachedConnections[n]++;
  2236. }
  2237. } else {
  2238. hr = E_OUTOFMEMORY;
  2239. ERROR_LOG("CreateCachedLdapConnection");
  2240. }
  2241. }
  2242. m_rgListLocks[n].ExclusiveUnlock();
  2243. DebugTrace(LDAP_CCACHE_DBG, "New connection is 0x%x", pcc);
  2244. }
  2245. //
  2246. // If we are returning a connection, then bump up the skip count so we
  2247. // round-robin through valid connections
  2248. //
  2249. if (pcc != NULL) {
  2250. InterlockedIncrement( &m_nNextConnectionSkipCount );
  2251. }
  2252. //
  2253. // Done.
  2254. //
  2255. *ppConn = pcc;
  2256. CatFunctLeave();
  2257. return( hr );
  2258. }
  2259. //+----------------------------------------------------------------------------
  2260. //
  2261. // Function: CLdapConnectionCache::CancelAllConnectionSearches
  2262. //
  2263. // Synopsis: Walks through all connections and cancels any pending searches
  2264. // on them.
  2265. //
  2266. // Arguments: [None]
  2267. //
  2268. // Returns: Nothing
  2269. //
  2270. //-----------------------------------------------------------------------------
  2271. VOID CLdapConnectionCache::CancelAllConnectionSearches(
  2272. ISMTPServer *pISMTPServer)
  2273. {
  2274. CatFunctEnterEx((LPARAM)this, "CLdapConnectionCache::CancelAllConnectionSearches");
  2275. PLIST_ENTRY pli;
  2276. DWORD i;
  2277. DWORD dwcArraySize = 0;
  2278. DWORD dwcArrayElements = 0;
  2279. CCachedLdapConnection **rgpcc = NULL;
  2280. CCachedLdapConnection *pcc = NULL;
  2281. for (i = 0; i < LDAP_CONNECTION_CACHE_TABLE_SIZE; i++) {
  2282. m_rgListLocks[i].ExclusiveLock();
  2283. //
  2284. // Do we have enough space? Realloc if necessary
  2285. //
  2286. if( ((DWORD) m_rgcCachedConnections[i]) > dwcArraySize) {
  2287. dwcArraySize = m_rgcCachedConnections[i];
  2288. //
  2289. // Alloc array
  2290. //
  2291. rgpcc = (CCachedLdapConnection **)
  2292. alloca( dwcArraySize * sizeof(CCachedLdapConnection *));
  2293. }
  2294. for (pli = m_rgCache[i].Flink, dwcArrayElements = 0;
  2295. pli != &m_rgCache[i];
  2296. pli = pli->Flink, dwcArrayElements++) {
  2297. //
  2298. // If this assert fires, it means m_rgcCachedConnections[n] is
  2299. // somehow less than the number of connections in the list.
  2300. //
  2301. _ASSERT(dwcArrayElements < dwcArraySize);
  2302. pcc = CONTAINING_RECORD(pli, CCachedLdapConnection, li);
  2303. //
  2304. // Grab the connection (copy and addref the conn ptr)
  2305. //
  2306. rgpcc[dwcArrayElements] = pcc;
  2307. pcc->AddRef();
  2308. }
  2309. m_rgListLocks[i].ExclusiveUnlock();
  2310. //
  2311. // Cancel all searches outside the lock
  2312. //
  2313. for(DWORD dwCount = 0;
  2314. dwCount < dwcArrayElements;
  2315. dwCount++) {
  2316. rgpcc[dwCount]->CancelAllSearches(
  2317. HRESULT_FROM_WIN32(ERROR_CANCELLED),
  2318. pISMTPServer);
  2319. rgpcc[dwCount]->Release();
  2320. }
  2321. }
  2322. CatFunctLeaveEx((LPARAM)this);
  2323. }
  2324. //+----------------------------------------------------------------------------
  2325. //
  2326. // Function: CLdapConnectionCache::Hash
  2327. //
  2328. // Synopsis: Computes a hash given a connection name. Here, we use a simple
  2329. // xor of all the chars in the name.
  2330. //
  2331. // Arguments: [szConnectionName] -- Name to compute the hash of
  2332. //
  2333. // Returns: A value between 0 and LDAP_CONNECTION_CACHE_TABLE_SIZE-1,
  2334. // inclusive.
  2335. //
  2336. //-----------------------------------------------------------------------------
  2337. unsigned short CLdapConnectionCache::Hash(
  2338. LPSTR szConnectionName)
  2339. {
  2340. CatFunctEnter("CLdapConnectionCache::Hash");
  2341. int i;
  2342. unsigned short n = 0;
  2343. _ASSERT( szConnectionName != NULL );
  2344. for (i = 0, n = szConnectionName[i];
  2345. szConnectionName[i] != 0;
  2346. n ^= szConnectionName[i], i++) {
  2347. // NOTHING TO DO
  2348. }
  2349. CatFunctLeave();
  2350. return( n & (LDAP_CONNECTION_CACHE_TABLE_SIZE-1));
  2351. }
  2352. //+------------------------------------------------------------
  2353. //
  2354. // Function: CLdapConnection::CallCompletion
  2355. //
  2356. // Synopsis: Create all the ICategorizerItemAttributes and call the
  2357. // completion routine
  2358. //
  2359. // Arguments:
  2360. // preq: PENDING_REQUEST
  2361. // pres: LdapMessage
  2362. // hrStatus: Status of lookup
  2363. // fFinalCompletion:
  2364. // FALSE: This is a completion for
  2365. // pending results; there will be another completion
  2366. // called with more results
  2367. // TRUE: This is the final completion call
  2368. //
  2369. // Returns: NOTHING; calls completion routine with any error
  2370. //
  2371. // History:
  2372. // jstamerj 1998/07/02 13:57:20: Created.
  2373. //
  2374. //-------------------------------------------------------------
  2375. VOID CLdapConnection::CallCompletion(
  2376. PPENDING_REQUEST preq,
  2377. PLDAPMessage pres,
  2378. HRESULT hrStatus,
  2379. BOOL fFinalCompletion)
  2380. {
  2381. HRESULT hr = S_OK;
  2382. ICategorizerItemAttributes **rgpIAttributes = NULL;
  2383. BOOL fAllocatedArray = FALSE;
  2384. int nEntries;
  2385. PLDAPMessage pMessage;
  2386. CLdapResultWrap *pResultWrap = NULL;
  2387. CatFunctEnterEx((LPARAM)this, "CLdapConnection::CallCompletion");
  2388. if(pres) {
  2389. //
  2390. // Wrap the result so that pres can be refcounted
  2391. //
  2392. nEntries = ldap_count_entries(GetPLDAP(), pres);
  2393. pResultWrap = new CLdapResultWrap(GetISMTPServerEx(), m_pCPLDAPWrap, pres);
  2394. if(pResultWrap == NULL) {
  2395. hr = E_OUTOFMEMORY;
  2396. ErrorTrace((LPARAM)this, "Out of memory Allocing CLdapResultWrap");
  2397. ERROR_LOG("new CLdapResultWrap");
  2398. goto CALLCOMPLETION;
  2399. }
  2400. //
  2401. // AddRef here, release at the end of this function
  2402. //
  2403. pResultWrap->AddRef();
  2404. } else {
  2405. nEntries = 0;
  2406. }
  2407. if(nEntries > 0) {
  2408. //
  2409. // Allocate array for all these ICategorizerItemAttributes
  2410. //
  2411. rgpIAttributes = new ICategorizerItemAttributes * [nEntries];
  2412. if(rgpIAttributes == NULL) {
  2413. hr = E_OUTOFMEMORY;
  2414. ErrorTrace((LPARAM)this, "Out of memory Allocing ICategorizerItemAttribute array failed");
  2415. ERROR_LOG("new ICategorizerItemAttributes *[]");
  2416. goto CALLCOMPLETION;
  2417. }
  2418. ZeroMemory(rgpIAttributes, nEntries * sizeof(ICategorizerItemAttributes *));
  2419. //
  2420. // Iterage through all the DS Objectes returned and create an
  2421. // ICategorizerItemAttributes implementation for each of them
  2422. //
  2423. pMessage = ldap_first_entry(GetPLDAP(), pres);
  2424. for(int nCount = 0; nCount < nEntries; nCount++) {
  2425. _ASSERT(pMessage);
  2426. rgpIAttributes[nCount] = new CICategorizerItemAttributesIMP(
  2427. GetPLDAP(),
  2428. pMessage,
  2429. pResultWrap);
  2430. if(rgpIAttributes[nCount] == NULL) {
  2431. hr = E_OUTOFMEMORY;
  2432. ErrorTrace((LPARAM)this, "Out of memory Allocing ICategorizerItemAttributesIMP class");
  2433. ERROR_LOG("new CICategorizerItemAttributesIMP");
  2434. goto CALLCOMPLETION;
  2435. }
  2436. rgpIAttributes[nCount]->AddRef();
  2437. pMessage = ldap_next_entry(GetPLDAP(), pMessage);
  2438. }
  2439. // That should have been the last entry
  2440. _ASSERT(pMessage == NULL);
  2441. } else {
  2442. //
  2443. // nEntries is zero
  2444. //
  2445. rgpIAttributes = NULL;
  2446. }
  2447. CALLCOMPLETION:
  2448. if(FAILED(hr)) {
  2449. //
  2450. // Something failed creating the above array
  2451. // Call completion routine with error
  2452. //
  2453. preq->fnCompletion(
  2454. preq->ctxCompletion,
  2455. 0,
  2456. NULL,
  2457. hr,
  2458. fFinalCompletion);
  2459. } else {
  2460. //
  2461. // Nothing failed in this function; call completion with
  2462. // passed in hrStatus
  2463. //
  2464. preq->fnCompletion(
  2465. preq->ctxCompletion,
  2466. nEntries,
  2467. rgpIAttributes,
  2468. hrStatus,
  2469. fFinalCompletion);
  2470. }
  2471. //
  2472. // Clean up
  2473. //
  2474. if(rgpIAttributes) {
  2475. for(int nCount = 0; nCount < nEntries; nCount++) {
  2476. if(rgpIAttributes[nCount])
  2477. rgpIAttributes[nCount]->Release();
  2478. }
  2479. delete rgpIAttributes;
  2480. }
  2481. if(pResultWrap != NULL) {
  2482. pResultWrap->Release();
  2483. } else if(pres) {
  2484. //
  2485. // We were unable to create pResultWrap, so we have to free
  2486. // the LDAP result ourself (normally CLdapResultWrap free's
  2487. // the ldap result when all references have been released)
  2488. //
  2489. FreeResult(pres);
  2490. }
  2491. }
  2492. //+------------------------------------------------------------
  2493. //
  2494. // Function: CLdapConnection::Release
  2495. //
  2496. // Synopsis: Release a refcount to this object. Delete this object
  2497. // when the refcout hits zero
  2498. //
  2499. // Arguments: None
  2500. //
  2501. // Returns:
  2502. // S_OK: Success
  2503. //
  2504. // History:
  2505. // jstamerj 1999/04/01 00:09:36: Created.
  2506. //
  2507. //-------------------------------------------------------------
  2508. DWORD CLdapConnection::Release()
  2509. {
  2510. DWORD dwNewRefCount;
  2511. dwNewRefCount = InterlockedDecrement((PLONG) &m_dwRefCount);
  2512. if(dwNewRefCount == 0) {
  2513. if(m_dwDestructionWaiters) {
  2514. //
  2515. // Threads are waiting on the destruction event, so let
  2516. // the last thread to wakeup delete this object
  2517. //
  2518. _ASSERT(m_hShutdownEvent != INVALID_HANDLE_VALUE);
  2519. _VERIFY(SetEvent(m_hShutdownEvent));
  2520. } else {
  2521. //
  2522. // Nobody is waiting, so delete this object
  2523. //
  2524. FinalRelease();
  2525. }
  2526. }
  2527. return dwNewRefCount;
  2528. } // CLdapConnection::Release
  2529. //+------------------------------------------------------------
  2530. //
  2531. // Function: CLdapConnection::ReleaseAndWaitForDestruction
  2532. //
  2533. // Synopsis: Release a refcount and block this thread until the object
  2534. // is destroyed
  2535. //
  2536. // Arguments: NONE
  2537. //
  2538. // Returns: NOTHING
  2539. //
  2540. // History:
  2541. // jstamerj 1999/04/01 00:12:13: Created.
  2542. //
  2543. //-------------------------------------------------------------
  2544. VOID CLdapConnection::ReleaseAndWaitForDestruction()
  2545. {
  2546. DWORD dw;
  2547. CatFunctEnterEx((LPARAM)this, "CLdapConnection::ReleaseAndWaitForDestruction");
  2548. _ASSERT(m_hShutdownEvent != INVALID_HANDLE_VALUE);
  2549. //
  2550. // Increment the count of threads waiting for destruction
  2551. //
  2552. InterlockedIncrement((PLONG)&m_dwDestructionWaiters);
  2553. //
  2554. // Release our refcount; if the new refcount is zero, this object
  2555. // will NOT be deleted; instead m_hShutdownEvent will be set
  2556. //
  2557. Release();
  2558. //
  2559. // Wait for all refcounts to be released
  2560. //
  2561. dw = WaitForSingleObject(
  2562. m_hShutdownEvent,
  2563. INFINITE);
  2564. _ASSERT(WAIT_OBJECT_0 == dw);
  2565. //
  2566. // Decrement the number of threads waiting for termination; if we
  2567. // are the last thread to leave here, we need to delete this
  2568. // object
  2569. //
  2570. if( InterlockedDecrement((PLONG)&m_dwDestructionWaiters) == 0)
  2571. FinalRelease();
  2572. CatFunctLeaveEx((LPARAM)this);
  2573. } // CLdapConnection::ReleaseAndWaitForDestruction
  2574. //+------------------------------------------------------------
  2575. //
  2576. // Function: CLdapConnection::HrInitialize
  2577. //
  2578. // Synopsis: Initialize error prone members
  2579. //
  2580. // Arguments: NONE
  2581. //
  2582. // Returns:
  2583. // S_OK: Success
  2584. //
  2585. // History:
  2586. // jstamerj 1999/04/01 00:17:56: Created.
  2587. //
  2588. //-------------------------------------------------------------
  2589. HRESULT CLdapConnection::HrInitialize()
  2590. {
  2591. HRESULT hr = S_OK;
  2592. CatFunctEnterEx((LPARAM)this, "CLdapConnection::HrInitialize");
  2593. m_hShutdownEvent = CreateEvent(
  2594. NULL, // Security attributes
  2595. TRUE, // fManualReset
  2596. FALSE, // Initial state is NOT signaled
  2597. NULL); // No name
  2598. if(NULL == m_hShutdownEvent) {
  2599. hr = HRESULT_FROM_WIN32(GetLastError());
  2600. ERROR_LOG("CreateEvent");
  2601. //
  2602. // Remember that m_hShutdownEvent is invalid
  2603. //
  2604. m_hShutdownEvent = INVALID_HANDLE_VALUE;
  2605. FatalTrace((LPARAM)this, "Error creating event hr %08lx", hr);
  2606. goto CLEANUP;
  2607. }
  2608. CLEANUP:
  2609. DebugTrace((LPARAM)this, "returning %08lx", hr);
  2610. CatFunctLeaveEx((LPARAM)this);
  2611. return hr;
  2612. } // CLdapConnection::HrInitialize
  2613. //+------------------------------------------------------------
  2614. //
  2615. // Function: CLdapConnectionCache::CCachedLdapConnection::Release
  2616. //
  2617. // Synopsis: Override Release for the cached LDAP connection
  2618. //
  2619. // Arguments: NONE
  2620. //
  2621. // Returns: New refcount
  2622. //
  2623. // History:
  2624. // jstamerj 1999/04/01 00:30:55: Created.
  2625. //
  2626. //-------------------------------------------------------------
  2627. DWORD CLdapConnectionCache::CCachedLdapConnection::Release()
  2628. {
  2629. DWORD dw;
  2630. CatFunctEnterEx((LPARAM)this, "CLdapConnectionCache::CCachedLdapConnection::Release");
  2631. dw = CLdapConnection::Release();
  2632. if((dw == 1) && (!IsValid())) {
  2633. //
  2634. // The ldap connection cache is the only entity that has a
  2635. // reference to this and this is invalid -- it should be
  2636. // removed from the cache
  2637. //
  2638. m_pCache->RemoveFromCache(this);
  2639. }
  2640. CatFunctLeaveEx((LPARAM)this);
  2641. return dw;
  2642. } // CLdapConnectionCache::CCachedLdapConnection::Release
  2643. //+------------------------------------------------------------
  2644. //
  2645. // Function: CLdapConnectionCache::RemoveFromCache
  2646. //
  2647. // Synopsis: Removes an LDAP connection object from the cache
  2648. //
  2649. // Arguments:
  2650. // pConn: the connection to remove
  2651. //
  2652. // Returns: NOTHING
  2653. //
  2654. // History:
  2655. // jstamerj 1999/04/01 00:38:43: Created.
  2656. //
  2657. //-------------------------------------------------------------
  2658. VOID CLdapConnectionCache::RemoveFromCache(
  2659. CCachedLdapConnection *pConn)
  2660. {
  2661. BOOL fRemoved = FALSE;
  2662. CatFunctEnterEx((LPARAM)this, "CLdapConnectionCache::RemoveFromCache");
  2663. DWORD dwHash = 0;
  2664. DebugTrace((LPARAM)this, "pConn = %08lx", pConn);
  2665. dwHash = Hash(pConn->SzHost());
  2666. //
  2667. // Before locking, check to see if the connection has already been removed
  2668. //
  2669. if(!IsListEmpty( &(pConn->li))) {
  2670. m_rgListLocks[dwHash].ExclusiveLock();
  2671. //
  2672. // Check again in case the connection was removed from the
  2673. // cache before we got the lock
  2674. //
  2675. if(!IsListEmpty( &(pConn->li))) {
  2676. RemoveEntryList( &(pConn->li) );
  2677. //
  2678. // Initialize li just in case someone attempts another removal
  2679. //
  2680. InitializeListHead( &(pConn->li) );
  2681. fRemoved = TRUE;
  2682. m_cCachedConnections--;
  2683. m_rgcCachedConnections[dwHash]--;
  2684. }
  2685. m_rgListLocks[dwHash].ExclusiveUnlock();
  2686. if(fRemoved)
  2687. pConn->Release();
  2688. }
  2689. CatFunctLeaveEx((LPARAM)this);
  2690. } // CLdapConnectionCache::RemoveFromCache
  2691. //+------------------------------------------------------------
  2692. //
  2693. // Function: CLdapConnection::AsyncSearch (UTF8)
  2694. //
  2695. // Synopsis: Same as AsyncSearch, accept this accepts a UTF8 search
  2696. // filter.
  2697. //
  2698. // Arguments: See AsyncSearch
  2699. //
  2700. // Returns:
  2701. // S_OK: Success
  2702. //
  2703. // History:
  2704. // jstamerj 1999/12/09 18:22:41: Created.
  2705. //
  2706. //-------------------------------------------------------------
  2707. HRESULT CLdapConnection::AsyncSearch(
  2708. LPCWSTR szBaseDN, // objects matching specified
  2709. int nScope, // criteria in the DS. The
  2710. LPCSTR szFilterUTF8, // results are passed to
  2711. LPCWSTR szAttributes[], // fnCompletion when they
  2712. DWORD dwPageSize, // Optinal page size
  2713. LPLDAPCOMPLETION fnCompletion, // become available.
  2714. LPVOID ctxCompletion)
  2715. {
  2716. #define FILTER_STRING_CHOOSE_HEAP (10 * 1024) // filter strings 10K or larger will go on the heap
  2717. HRESULT hr = S_OK;
  2718. LPWSTR wszFilter = NULL;
  2719. int cchFilter = 0;
  2720. BOOL fUseHeapBuffer = FALSE;
  2721. int i = 0;
  2722. CatFunctEnterEx((LPARAM)this, "CLdapConnection::AsyncSearch");
  2723. //
  2724. // Convert BaseDN and Filter to unicode (from UTF8)
  2725. //
  2726. // calculate lengths
  2727. //
  2728. cchFilter = MultiByteToWideChar(
  2729. CP_UTF8,
  2730. 0,
  2731. szFilterUTF8,
  2732. -1,
  2733. NULL,
  2734. 0);
  2735. if(cchFilter == 0) {
  2736. hr = HRESULT_FROM_WIN32(GetLastError());
  2737. ERROR_LOG("MultiByteToWideChar - 0");
  2738. goto CLEANUP;
  2739. }
  2740. //
  2741. // allocate space
  2742. //
  2743. if(cchFilter * sizeof(WCHAR) < FILTER_STRING_CHOOSE_HEAP) {
  2744. wszFilter = (LPWSTR) alloca(cchFilter * sizeof(WCHAR));
  2745. } else {
  2746. fUseHeapBuffer = TRUE;
  2747. wszFilter = new WCHAR[cchFilter];
  2748. }
  2749. if(wszFilter == NULL) {
  2750. //$$BUGBUG: alloca does not return NULL. It throws exceptions on error
  2751. // This will catch heap alloc failures though.
  2752. hr = E_OUTOFMEMORY;
  2753. ERROR_LOG("alloca");
  2754. goto CLEANUP;
  2755. }
  2756. i = MultiByteToWideChar(
  2757. CP_UTF8,
  2758. 0,
  2759. szFilterUTF8,
  2760. -1,
  2761. wszFilter,
  2762. cchFilter);
  2763. if(i == 0) {
  2764. hr = HRESULT_FROM_WIN32(GetLastError());
  2765. ERROR_LOG("MultiByteToWideChar - 1");
  2766. goto CLEANUP;
  2767. }
  2768. //
  2769. // Call unicode based AsyncSearch
  2770. //
  2771. hr = AsyncSearch(
  2772. szBaseDN,
  2773. nScope,
  2774. wszFilter,
  2775. szAttributes,
  2776. dwPageSize,
  2777. fnCompletion,
  2778. ctxCompletion);
  2779. if(FAILED(hr)) {
  2780. ERROR_LOG("AsyncSearch");
  2781. }
  2782. CLEANUP:
  2783. if(wszFilter && fUseHeapBuffer) {
  2784. delete [] wszFilter;
  2785. }
  2786. DebugTrace((LPARAM)this, "returning %08lx", hr);
  2787. CatFunctLeaveEx((LPARAM)this);
  2788. return hr;
  2789. } // CLdapConnection::AsyncSearch
  2790. //+------------------------------------------------------------
  2791. //
  2792. // Function: CLdapConnection::AsyncSearch
  2793. //
  2794. // Synopsis: same as above with UTF8 search filter and base DN
  2795. //
  2796. // Arguments: see above
  2797. //
  2798. // Returns:
  2799. // S_OK: Success
  2800. //
  2801. // History:
  2802. // jstamerj 1999/12/09 20:50:53: Created.
  2803. //
  2804. //-------------------------------------------------------------
  2805. HRESULT CLdapConnection::AsyncSearch(
  2806. LPCSTR szBaseDN, // objects matching specified
  2807. int nScope, // criteria in the DS. The
  2808. LPCSTR szFilterUTF8, // results are passed to
  2809. LPCWSTR szAttributes[], // fnCompletion when they
  2810. DWORD dwPageSize, // Optinal page size
  2811. LPLDAPCOMPLETION fnCompletion, // become available.
  2812. LPVOID ctxCompletion)
  2813. {
  2814. HRESULT hr = S_OK;
  2815. LPWSTR wszBaseDN = NULL;
  2816. int cchBaseDN = 0;
  2817. int i = 0;
  2818. CatFunctEnterEx((LPARAM)this, "CLdapConnection::AsyncSearch");
  2819. //
  2820. // Convert BaseDN and Filter to unicode (from UTF8)
  2821. //
  2822. // calculate lengths
  2823. //
  2824. cchBaseDN = MultiByteToWideChar(
  2825. CP_UTF8,
  2826. 0,
  2827. szBaseDN,
  2828. -1,
  2829. NULL,
  2830. 0);
  2831. if(cchBaseDN == 0) {
  2832. hr = HRESULT_FROM_WIN32(GetLastError());
  2833. ERROR_LOG("MultiByteToWideChar - 0");
  2834. goto CLEANUP;
  2835. }
  2836. //
  2837. // allocate space
  2838. //
  2839. wszBaseDN = (LPWSTR) alloca(cchBaseDN * sizeof(WCHAR));
  2840. if(wszBaseDN == NULL) {
  2841. //$$BUGBUG: alloca does not return NULL. It throws exceptions on error.
  2842. hr = E_OUTOFMEMORY;
  2843. ERROR_LOG("alloca");
  2844. goto CLEANUP;
  2845. }
  2846. i = MultiByteToWideChar(
  2847. CP_UTF8,
  2848. 0,
  2849. szBaseDN,
  2850. -1,
  2851. wszBaseDN,
  2852. cchBaseDN);
  2853. if(i == 0) {
  2854. hr = HRESULT_FROM_WIN32(GetLastError());
  2855. ERROR_LOG("MultiByteToWideChar - 1");
  2856. goto CLEANUP;
  2857. }
  2858. //
  2859. // Call unicode based AsyncSearch
  2860. //
  2861. hr = AsyncSearch(
  2862. wszBaseDN,
  2863. nScope,
  2864. szFilterUTF8,
  2865. szAttributes,
  2866. dwPageSize,
  2867. fnCompletion,
  2868. ctxCompletion);
  2869. if(FAILED(hr)) {
  2870. ERROR_LOG("AsyncSearch");
  2871. }
  2872. CLEANUP:
  2873. DebugTrace((LPARAM)this, "returning %08lx", hr);
  2874. CatFunctLeaveEx((LPARAM)this);
  2875. return hr;
  2876. } // CLdapConnection::AsyncSearch
  2877. //+------------------------------------------------------------
  2878. //
  2879. // Function: CLdapConnection::LogLdapError
  2880. //
  2881. // Synopsis: Logs an eventlog for a wldap32 error
  2882. //
  2883. // Arguments:
  2884. // ulLdapErr: LDAP error to log
  2885. // pszFormatString: _snprintf Format string for function name
  2886. // ...: variable list of args for format string
  2887. //
  2888. // Returns: Nothing
  2889. //
  2890. // History:
  2891. // jstamerj 2001/12/12 20:45:09: Created.
  2892. //
  2893. //-------------------------------------------------------------
  2894. VOID CLdapConnection::LogLdapError(
  2895. IN ULONG ulLdapErr,
  2896. IN LPSTR pszFormatString,
  2897. ...)
  2898. {
  2899. int nRet = 0;
  2900. CHAR szArgBuffer[1024 + 1]; // MSDN says wvsprintf never uses more than 1024 bytes
  2901. // ...but wvsprintf really needs an extra byte to store
  2902. // a null terminator in some cases (see X5:198202).
  2903. va_list ap;
  2904. va_start(ap, pszFormatString);
  2905. nRet = wvsprintf(szArgBuffer, pszFormatString, ap);
  2906. _ASSERT(nRet < 1024 + 1);
  2907. ::LogLdapError(
  2908. GetISMTPServerEx(),
  2909. ulLdapErr,
  2910. m_szHost,
  2911. szArgBuffer);
  2912. }
  2913. //+------------------------------------------------------------
  2914. //
  2915. // Function: CLdapConnection::LogLdapError
  2916. //
  2917. // Synopsis: Logs an eventlog for a wldap32 error
  2918. //
  2919. // Arguments:
  2920. // pISMTPServerEx: SMTP server instance
  2921. // ulLdapErr: LDAP error to log
  2922. // pszFormatString: _snprintf Format string for function name
  2923. // ...: variable list of args for format string
  2924. //
  2925. // Returns: Nothing
  2926. //
  2927. // History:
  2928. // dlongley 2002/1/31: Created.
  2929. //
  2930. //-------------------------------------------------------------
  2931. VOID CLdapConnection::LogLdapError(
  2932. IN ISMTPServerEx *pISMTPServerEx,
  2933. IN ULONG ulLdapErr,
  2934. IN LPSTR pszFormatString,
  2935. ...)
  2936. {
  2937. int nRet = 0;
  2938. CHAR szArgBuffer[1024 + 1]; // MSDN says wvsprintf never uses more than 1024 bytes
  2939. // ...but wvsprintf really needs an extra byte to store
  2940. // a null terminator in some cases (see X5:198202).
  2941. va_list ap;
  2942. if( !pISMTPServerEx )
  2943. return;
  2944. va_start(ap, pszFormatString);
  2945. nRet = wvsprintf(szArgBuffer, pszFormatString, ap);
  2946. _ASSERT(nRet < 1024 + 1);
  2947. ::LogLdapError(
  2948. pISMTPServerEx,
  2949. ulLdapErr,
  2950. "",
  2951. szArgBuffer);
  2952. }
  2953. VOID LogLdapError(
  2954. IN ISMTPServerEx *pISMTPServerEx,
  2955. IN ULONG ulLdapErr,
  2956. IN LPSTR pszHost,
  2957. IN LPSTR pszCall)
  2958. {
  2959. int nRet = 0;
  2960. LPCSTR rgSubStrings[3];
  2961. CHAR szErrNo[11];
  2962. nRet = _snprintf(szErrNo, sizeof(szErrNo), "0x%08lx", ulLdapErr);
  2963. _ASSERT(nRet == 10);
  2964. rgSubStrings[0] = szErrNo;
  2965. rgSubStrings[1] = pszCall;
  2966. rgSubStrings[2] = pszHost;
  2967. CatLogEvent(
  2968. pISMTPServerEx,
  2969. CAT_EVENT_LDAP_ERROR,
  2970. 3,
  2971. rgSubStrings,
  2972. ulLdapErr,
  2973. szErrNo,
  2974. LOGEVENT_FLAG_ALWAYS,
  2975. LOGEVENT_LEVEL_FIELD_ENGINEERING);
  2976. }