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.

2088 lines
54 KiB

  1. /**********************************************************************/
  2. /** Microsoft Windows NT **/
  3. /** Copyright(c) Microsoft Corp., 2001 **/
  4. /**********************************************************************/
  5. /*
  6. adio.cxx
  7. This module manages access to Active Directory.
  8. Functions exported by this module:
  9. FILE HISTORY:
  10. RobSol 17-May-2001 Created.
  11. */
  12. #include <ftpdp.hxx>
  13. # include <mbstring.h>
  14. //--------------------------------------------------------------------------------------------
  15. // LdapCacheItem globals/statics
  16. //--------------------------------------------------------------------------------------------
  17. //
  18. // number of retries to find a DC
  19. //
  20. const ULONG GetDcNameMaxRetries = 2;
  21. //--------------------------------------------------------------------------------------------
  22. // ADIO_ASYNC globals/statics
  23. //--------------------------------------------------------------------------------------------
  24. //
  25. // Time to sleep (in msec) when no request is ready before re-checking
  26. // If the async work list is empty, or no request is ready, this is the time
  27. // we sleep before re-examining the list.
  28. //
  29. const ULONG AsyncPollXval = 1000;
  30. //
  31. // constants for managing the async request object cache
  32. //
  33. const LONG AsyncMaxFreeReqRate = 50; // start freeing when this percent of free req objects
  34. const LONG AsyncMinFreeReqRate = 10; // stop freeing when this percent of free objects
  35. const LONG AsyncMinFreeReqObjs = 10; // leave at least this number of free objs
  36. //
  37. // FTP related attributes stored in AD for user objects
  38. //
  39. const PSTR pszFtpRoot = "msIIS-FTPRoot";
  40. const PSTR pszFtpDir = "msIIS-FTPDir";
  41. static PSTR aszRetAttribs[] = { pszFtpRoot, pszFtpDir, NULL };
  42. //
  43. // format string to construct a query
  44. //
  45. const PCHAR pszQueryFormatString = "(&(objectClass=user)(samAccountName=%s))";
  46. //------------------------------------------------------------------------------------------
  47. //
  48. // implementation of the LdapCacheItem Class methods
  49. //
  50. //------------------------------------------------------------------------------------------
  51. /*++
  52. class constructor. Initializes the LDAP connection and binds to the server
  53. uses ldap_bind_s, a synchronous call, as the asynchronous call is limited in
  54. taking authentication parameters and will only perform clear text authentication
  55. Arguments:
  56. DomainName the domain name for which the LDAP connection is made. We search for
  57. and connect to a DC servicing this domain name
  58. strUser name of user to authenticate with active directory
  59. strDomain domain of user to authenticate with active directory
  60. strPassword password of user to authenticate with active directory
  61. Returns:
  62. SetLastError() to indicate success/failure
  63. --*/
  64. LdapCacheItem::LdapCacheItem(
  65. const STR & DomainName,
  66. const STR & strUser,
  67. const STR & strDomain,
  68. const STR & strPassword) :
  69. m_ldapConnection( NULL),
  70. m_RefCount( 1)
  71. {
  72. ULONG err;
  73. ULONG ulOptVal;
  74. SEC_WINNT_AUTH_IDENTITY sSecIdent;
  75. DWORD fDsGetDcName = DS_DIRECTORY_SERVICE_REQUIRED | DS_RETURN_DNS_NAME;
  76. DWORD dwDsGetDcNameRetriesLeft = GetDcNameMaxRetries;
  77. PDOMAIN_CONTROLLER_INFO pDCInfo;
  78. STACK_STR (localPassword, PWLEN+1);
  79. //
  80. // store the domain name
  81. //
  82. m_strDomainName.Copy( DomainName );
  83. do {
  84. //
  85. // locate a DC for the domain of interest
  86. //
  87. err = pfn_DsGetDcName(
  88. NULL,
  89. m_strDomainName.QueryStr(),
  90. NULL,
  91. NULL,
  92. fDsGetDcName,
  93. &pDCInfo);
  94. if (err != NO_ERROR) {
  95. DBGPRINTF((DBG_CONTEXT, "DsGetDcName() failed (0x%X)", err));
  96. break;
  97. }
  98. DBG_ASSERT( pDCInfo );
  99. //
  100. // save a converted forest name (for later use by ldap_search)
  101. // and initialize the DC connection
  102. //
  103. err = Forest2DN(pDCInfo->DomainName);
  104. m_ldapConnection = pfn_ldap_init(
  105. pDCInfo->DomainControllerName + 2,
  106. 0);
  107. //
  108. // we no longer need the DCInfo structure, so release it.
  109. //
  110. pfn_NetApiBufferFree( pDCInfo );
  111. //
  112. // now check the result of the previous calls
  113. //
  114. if ( err != NO_ERROR) {
  115. DBGPRINTF((DBG_CONTEXT, "Forest2DN() failed (0x%X)", err));
  116. break;
  117. }
  118. if ( m_ldapConnection == NULL ) {
  119. err = pfn_LdapGetLastError();
  120. if (err == LDAP_SUCCESS) {
  121. err = LDAP_OPERATIONS_ERROR;
  122. }
  123. DBGPRINTF((DBG_CONTEXT, "ldap_init() failed (0x%X)", err));
  124. break;
  125. }
  126. //
  127. // now tell LDAP what version of the API we are using, and set some other options
  128. //
  129. ulOptVal = LDAP_VERSION3;
  130. err = pfn_ldap_set_option(
  131. m_ldapConnection,
  132. LDAP_OPT_VERSION,
  133. &ulOptVal );
  134. if ( err != LDAP_SUCCESS ) {
  135. DBGPRINTF((DBG_CONTEXT, "ldap_set_option(LDAP_OPT_VERSION) failed (0x%X)\n", err));
  136. break;
  137. }
  138. ulOptVal = 1;
  139. err = pfn_ldap_set_option(
  140. m_ldapConnection,
  141. LDAP_OPT_SIZELIMIT,
  142. &ulOptVal );
  143. if ( err != LDAP_SUCCESS ) {
  144. DBGPRINTF((DBG_CONTEXT, "ldap_set_option(LDAP_OPT_SIZELIMIT) failed (0x%X)\n", err));
  145. break;
  146. }
  147. ulOptVal = LDAP_NO_LIMIT; //10; /* BUGBUG: make this configurable */ // timelimit in seconds
  148. err = pfn_ldap_set_option(
  149. m_ldapConnection,
  150. LDAP_OPT_TIMELIMIT,
  151. &ulOptVal );
  152. if ( err != LDAP_SUCCESS ) {
  153. DBGPRINTF((DBG_CONTEXT, "ldap_set_option(LDAP_OPT_TIMELIMIT) failed (0x%X)\n", err));
  154. break;
  155. }
  156. err = pfn_ldap_set_option(
  157. m_ldapConnection,
  158. LDAP_OPT_AUTO_RECONNECT,
  159. LDAP_OPT_ON );
  160. if ( err != LDAP_SUCCESS ) {
  161. DBGPRINTF((DBG_CONTEXT, "ldap_set_option(LDAP_OPT_AUTO_RECONNECT) failed (0x%X)\n", err));
  162. break;
  163. }
  164. err = pfn_ldap_set_option(
  165. m_ldapConnection,
  166. LDAP_OPT_REFERRALS,
  167. LDAP_OPT_OFF );
  168. if ( err != LDAP_SUCCESS ) {
  169. DBGPRINTF((DBG_CONTEXT, "ldap_set_option(LDAP_OPT_REFERRALS) failed (0x%X)\n", err));
  170. break;
  171. }
  172. //
  173. // unhash the password so we can use it
  174. //
  175. strPassword.Clone( &localPassword);
  176. localPassword.Unhash();
  177. //
  178. // next, bind to the DC using the provided user credentianls
  179. //
  180. sSecIdent.User = (PUCHAR)strUser.QueryStr();
  181. sSecIdent.UserLength = strUser.QueryCCH();
  182. sSecIdent.Domain = (PUCHAR)strDomain.QueryStr();
  183. sSecIdent.DomainLength = strDomain.QueryCCH();
  184. sSecIdent.Password = (PUCHAR)localPassword.QueryStr();
  185. sSecIdent.PasswordLength = localPassword.QueryCCH();
  186. sSecIdent.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
  187. err = pfn_ldap_bind_s(
  188. m_ldapConnection,
  189. NULL,
  190. (PCHAR)&sSecIdent,
  191. LDAP_AUTH_NEGOTIATE);
  192. // clear the password first
  193. localPassword.Clear();
  194. if ( err == LDAP_SUCCESS ) {
  195. //
  196. // successful bind - we are done here.
  197. //
  198. break;
  199. }
  200. DBGPRINTF((DBG_CONTEXT, "ldap_bind_s() failed (0x%X)\n", err));
  201. //
  202. // add rediscovery flag for the retry call to DsGetDcName()
  203. //
  204. fDsGetDcName |= DS_FORCE_REDISCOVERY;
  205. } while (--dwDsGetDcNameRetriesLeft > 0);
  206. SetLastError( err );
  207. }
  208. /*++
  209. class distructor. if there is a valid LDAP connection handle, close it.
  210. Arguments:
  211. none.
  212. Returns:
  213. none.
  214. --*/
  215. LdapCacheItem::~LdapCacheItem()
  216. {
  217. if ( m_ldapConnection != NULL ) {
  218. pfn_ldap_unbind( m_ldapConnection );
  219. }
  220. }
  221. /*++
  222. Given a forest name, format a Distinguished Name string. Stores the result in
  223. m_strForestDN. Result format: DC=dom,DC=org,DC=comp,DC=com
  224. Arguments:
  225. pszForest - input of the format dom.org.corp.com
  226. return:
  227. LDAP error codes. LDAP_SUCCESS or appropriate error code.
  228. --*/
  229. DWORD
  230. LdapCacheItem::Forest2DN(
  231. IN PCSTR pszForest)
  232. {
  233. BOOL fOk = TRUE;
  234. PCSTR cp;
  235. m_strForestDN.Reset();
  236. //
  237. // for empty input we are done.
  238. //
  239. if ( (pszForest == NULL) || (*pszForest == '\0') ) {
  240. fOk = FALSE;
  241. }
  242. //
  243. // iterate through the '.' separated components of the forest name
  244. //
  245. while (fOk) {
  246. //
  247. // start each component with a 'DC='
  248. //
  249. fOk &= m_strForestDN.Append( "DC=" );
  250. cp = (PCSTR)_mbschr( (const UCHAR *)pszForest, '.');
  251. if (cp == NULL) {
  252. //
  253. // last component
  254. //
  255. fOk &= m_strForestDN.Append( pszForest );
  256. break;
  257. }
  258. fOk &= m_strForestDN.Append( pszForest, DIFF(cp - pszForest) );
  259. fOk &= m_strForestDN.Append( "," ); // append ',' to separate next component
  260. pszForest = cp+1;
  261. }
  262. return fOk ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
  263. }
  264. //------------------------------------------------------------------------------------------
  265. //
  266. // implementation of the LDAP_CONN_CACHE Class methods
  267. //
  268. //------------------------------------------------------------------------------------------
  269. /*++
  270. class constructor. initialize critical section and list head.
  271. Arguments:
  272. none.
  273. Returns:
  274. none.
  275. --*/
  276. LDAP_CONN_CACHE::LDAP_CONN_CACHE()
  277. {
  278. INITIALIZE_CRITICAL_SECTION( &m_cs );
  279. InitializeListHead( &m_ConnList );
  280. m_User.Reset();
  281. m_Domain.Reset();
  282. m_Pass.Reset();
  283. }
  284. /*++
  285. class distructor. free the list of cached connections.
  286. Arguments:
  287. none.
  288. Returns:
  289. none.
  290. --*/
  291. LDAP_CONN_CACHE::~LDAP_CONN_CACHE()
  292. {
  293. Lock();
  294. //
  295. // indicates the object is invalid
  296. //
  297. m_User.Reset();
  298. //
  299. // unlink from the list and release.
  300. //
  301. while( !IsListEmpty( &m_ConnList ) ) {
  302. CONTAINING_RECORD(RemoveHeadList( &m_ConnList ), LdapCacheItem, m_Link)->Release();
  303. }
  304. Unlock();
  305. DeleteCriticalSection( &m_cs );
  306. }
  307. /*++
  308. set/change configuration. set the credentials of the user authenticating the LDAP connections.
  309. Arguments:
  310. strUser - name of authentiating user
  311. strDomain - Domain of authenticating user
  312. strPassword - Password of authenticating user
  313. Returns:
  314. NO_ERROR. the function always succeeds
  315. --*/
  316. DWORD
  317. LDAP_CONN_CACHE::Configure(
  318. const STR & strUser,
  319. const STR & strDomain,
  320. const STR & strPassword)
  321. {
  322. Lock();
  323. //
  324. // if all configuration parameters match, we do nothing.
  325. // otherwise, just close all current connections.
  326. //
  327. if ( !m_User.Equ( strUser ) ||
  328. !m_Domain.Equ( strDomain ) ||
  329. !m_Pass.Equ( strPassword ) ) {
  330. m_User.Copy( strUser );
  331. m_Domain.Copy( strDomain );
  332. m_Pass.Copy( strPassword );
  333. //
  334. // delete the existing connections.
  335. // could clone the list head and do this outside the lock, but the list should
  336. // be very short so we would not gain much
  337. //
  338. while( !IsListEmpty( &m_ConnList ) ) {
  339. CONTAINING_RECORD(RemoveHeadList( &m_ConnList ), LdapCacheItem, m_Link)->Release();
  340. }
  341. }
  342. Unlock();
  343. return NO_ERROR;
  344. }
  345. /*++
  346. look up a cached connection or create a new one.
  347. Arguments:
  348. Domain - name of domain for which we need a connection.
  349. Returns:
  350. Pointer to LdapCacheItem object if successful. If returning a valid pointer,
  351. we AddRef the object, and the caller must call ->Release() when done.
  352. On failure, return NULL and SetLastError();
  353. --*/
  354. PLdapCacheItem
  355. LDAP_CONN_CACHE::QueryLdapConnection( const STR & Domain )
  356. {
  357. DWORD err = NO_ERROR;
  358. PLdapCacheItem pCacheItem = NULL;
  359. PLdapCacheItem pNewCacheItem;
  360. PLIST_ENTRY pElem;
  361. BOOL fFound = FALSE;
  362. Lock();
  363. if ( m_User.IsEmpty() ) {
  364. //
  365. // we might be shuting down
  366. //
  367. err = ERROR_NOT_READY;
  368. } else {
  369. //
  370. // search for a cached connection
  371. //
  372. for ( pElem = m_ConnList.Flink; pElem != &m_ConnList; pElem = pElem->Flink)
  373. {
  374. pCacheItem = CONTAINING_RECORD(pElem, LdapCacheItem, m_Link);
  375. if ( pCacheItem->IsDomainNameMatch( Domain ) )
  376. {
  377. fFound = TRUE;
  378. pCacheItem->AddRef();
  379. break;
  380. }
  381. }
  382. }
  383. Unlock();
  384. //
  385. // found one or error, we are done.
  386. //
  387. if (fFound || err) {
  388. goto QueryLdapConnection_Exit;
  389. }
  390. //
  391. // create a new object, that will also bind etc.
  392. //
  393. pNewCacheItem = new LdapCacheItem(
  394. Domain,
  395. m_User,
  396. m_Domain,
  397. m_Pass);
  398. if ( pNewCacheItem == NULL ) {
  399. err = ERROR_NOT_ENOUGH_MEMORY;
  400. goto QueryLdapConnection_Exit;
  401. }
  402. if ( (err = GetLastError()) != NO_ERROR) {
  403. //
  404. // object initialization failed
  405. //
  406. pNewCacheItem->Release();
  407. goto QueryLdapConnection_Exit;
  408. }
  409. //
  410. // now that we have the new connection, we'll check again if we are not shuting down
  411. // and if the same domain has not been cached in the meantime by another thread
  412. //
  413. Lock();
  414. fFound = FALSE;
  415. if ( m_User.IsEmpty() ) {
  416. //
  417. // we might be shuting down
  418. //
  419. err = ERROR_NOT_READY;
  420. } else {
  421. for ( pElem = m_ConnList.Flink; pElem != &m_ConnList; pElem = pElem->Flink)
  422. {
  423. pCacheItem = CONTAINING_RECORD(pElem, LdapCacheItem, m_Link);
  424. if ( pCacheItem->IsDomainNameMatch( Domain ) )
  425. {
  426. fFound = TRUE;
  427. pCacheItem->AddRef();
  428. break;
  429. }
  430. }
  431. }
  432. //
  433. // if we found another cached item (or shutting down), release the new one and use the
  434. // cached one. If we did not (which is the mainstream scenario), then link in the new item.
  435. //
  436. if ( fFound || err )
  437. {
  438. pNewCacheItem->Release();
  439. } else {
  440. InsertHeadList( &m_ConnList, &pNewCacheItem->m_Link );
  441. pCacheItem = pNewCacheItem;
  442. pCacheItem->AddRef();
  443. }
  444. Unlock();
  445. QueryLdapConnection_Exit:
  446. if ( err != NO_ERROR ) {
  447. SetLastError( err );
  448. pCacheItem = NULL;
  449. }
  450. return pCacheItem;
  451. }
  452. //------------------------------------------------------------------------------------------
  453. //
  454. // implementation of the AD_IO Class methods
  455. //
  456. //------------------------------------------------------------------------------------------
  457. #if !defined( USE_STATIC_FUNCTION_BINDING )
  458. HMODULE AD_IO::hNetApi32 = NULL;
  459. type_DsGetDcName AD_IO::_pfn_DsGetDcName = NULL;
  460. type_NetApiBufferFree AD_IO::_pfn_NetApiBufferFree = NULL;
  461. HMODULE AD_IO::hWLdap32 = NULL;
  462. type_ldap_init AD_IO::_pfn_ldap_init = NULL;
  463. type_ldap_set_option AD_IO::_pfn_ldap_set_option = NULL;
  464. type_ldap_bind_s AD_IO::_pfn_ldap_bind_s = NULL;
  465. type_ldap_unbind AD_IO::_pfn_ldap_unbind = NULL;
  466. type_ldap_search AD_IO::_pfn_ldap_search = NULL;
  467. type_ldap_search_s AD_IO::_pfn_ldap_search_s = NULL;
  468. type_ldap_first_entry AD_IO::_pfn_ldap_first_entry = NULL;
  469. type_ldap_get_values AD_IO::_pfn_ldap_get_values = NULL;
  470. type_ldap_abandon AD_IO::_pfn_ldap_abandon = NULL;
  471. type_ldap_result AD_IO::_pfn_ldap_result = NULL;
  472. type_ldap_parse_result AD_IO::_pfn_ldap_parse_result = NULL;
  473. type_ldap_value_free AD_IO::_pfn_ldap_value_free = NULL;
  474. type_ldap_msgfree AD_IO::_pfn_ldap_msgfree = NULL;
  475. type_LdapGetLastError AD_IO::_pfn_LdapGetLastError = NULL;
  476. #endif // USE_STATIC_FUNCTION_BINDING
  477. DWORD AD_IO::m_dwRefCount = 0;
  478. CRITICAL_SECTION AD_IO::m_cs;
  479. BOOL AD_IO::m_fLibsInitOK = FALSE;
  480. /*++
  481. AD_IO constructor
  482. There is one instance of this class for each FTP server instance that is configured for
  483. Active Directory Isolation mode.
  484. THe first instance will dynamically load the LDAP DLLs and initialize the function pointers,
  485. and intialize the threads servicing the asynchronous LDAP requests.
  486. Must be followed by call to Configure() before any other call is made. to configure the
  487. per instance data, including a cache for the anonymous user properties, and one for a
  488. list of cached LDAP connections.
  489. Arguments:
  490. None.
  491. Returns:
  492. SetLastError().
  493. --*/
  494. AD_IO::AD_IO() :
  495. m_pConnCache( NULL ),
  496. m_pAnonymCache( NULL )
  497. {
  498. BOOL fOk = TRUE;
  499. Lock();
  500. m_dwRefCount++;
  501. #if !defined( USE_STATIC_FUNCTION_BINDING )
  502. if ( !m_fLibsInitOK ) {
  503. //
  504. // Initialize the dynamic bind function pointers
  505. //
  506. #define GPA( h, FN ) (_pfn_##FN = (type_ ## FN) GetProcAddress( h, #FN ))
  507. #define GPAA( h, FN ) (_pfn_##FN = (type_ ## FN) GetProcAddress( h, #FN "A" ))
  508. fOk =
  509. (hNetApi32 = LoadLibrary( "netapi32.dll" )) &&
  510. GPAA( hNetApi32, DsGetDcName) &&
  511. GPA( hNetApi32, NetApiBufferFree) ;
  512. fOk = fOk &&
  513. (hWLdap32 = LoadLibrary( "wldap32.dll" )) &&
  514. GPA( hWLdap32, LdapGetLastError) &&
  515. GPA( hWLdap32, ldap_init) &&
  516. GPA( hWLdap32, ldap_set_option) &&
  517. GPA( hWLdap32, ldap_bind_s) &&
  518. GPA( hWLdap32, ldap_unbind) &&
  519. GPA( hWLdap32, ldap_search) &&
  520. GPA( hWLdap32, ldap_search_s) &&
  521. GPA( hWLdap32, ldap_first_entry) &&
  522. GPA( hWLdap32, ldap_abandon) &&
  523. GPA( hWLdap32, ldap_result) &&
  524. GPA( hWLdap32, ldap_parse_result) &&
  525. GPA( hWLdap32, ldap_get_values) &&
  526. GPA( hWLdap32, ldap_value_free) &&
  527. GPA( hWLdap32, ldap_msgfree) ;
  528. fOk = fOk && ADIO_ASYNC::Initialize();
  529. if ( !fOk ) {
  530. if (hNetApi32) {
  531. FreeLibrary( hNetApi32 );
  532. hNetApi32 = NULL;
  533. }
  534. if (hWLdap32) {
  535. FreeLibrary( hWLdap32 );
  536. hWLdap32 = NULL;
  537. }
  538. ADIO_ASYNC::Terminate();
  539. }
  540. }
  541. #endif // !USE_STATIC_FUNCTION_BINDING
  542. if ( fOk ) {
  543. //
  544. // communicate success. Failure would have been communicated above.
  545. //
  546. SetLastError( NO_ERROR );
  547. m_fLibsInitOK = TRUE;
  548. } else if (GetLastError() == NO_ERROR) {
  549. //
  550. // if anything failed, this is our only way to communicate the initialization failed.
  551. //
  552. SetLastError( ERROR_INVALID_HANDLE );
  553. }
  554. Unlock();
  555. }
  556. //
  557. // AD_IO destructor
  558. //
  559. // Last object causes AD Client DLLs to be freed.
  560. //
  561. /*++
  562. AD_IO destructor. Called when an instance configured to enterprise isolation is terminating.
  563. When the last instance using AD_IO is terminating, the LDAP libraries are unloaded, and the
  564. asynchronous request processing threads are ended.
  565. Arguments:
  566. None.
  567. Returns:
  568. None.
  569. --*/
  570. AD_IO::~AD_IO()
  571. {
  572. //
  573. // clean up the connection and anonymous caches
  574. //
  575. if (m_pAnonymCache) {
  576. delete m_pAnonymCache;
  577. }
  578. if (m_pConnCache) {
  579. delete m_pConnCache;
  580. }
  581. //
  582. // clean up static data if this is the last instance
  583. //
  584. Lock();
  585. if (--m_dwRefCount == 0 ) {
  586. m_fLibsInitOK = FALSE;
  587. ADIO_ASYNC::Terminate();
  588. #if !defined( USE_STATIC_FUNCTION_BINDING )
  589. if (hNetApi32) {
  590. FreeLibrary( hNetApi32 );
  591. hNetApi32 = NULL;
  592. }
  593. if (hWLdap32) {
  594. FreeLibrary( hWLdap32 );
  595. hWLdap32 = NULL;
  596. }
  597. #endif // !USE_STATIC_FUNCTION_BINDING
  598. }
  599. Unlock();
  600. }
  601. /*++
  602. Configure the AD_IO object. This method must be called after creation of the object
  603. and before it can be called for service. The method is also called to reconfigure
  604. the object.
  605. AD Access creadential are mandatory arguments.
  606. anonymous user credentials are optional - if provided, an anonymous user cache object
  607. is created.
  608. Arguments:
  609. strADAccUser,
  610. strADAccDomain,
  611. strADAccPass credentials for the user authenticating access to Active Directory
  612. strAnonUser,
  613. strAnonDomain name and domain of the anonymous user.
  614. Returns:
  615. Win32 error.
  616. --*/
  617. DWORD
  618. AD_IO::Configure(
  619. const STR & strADAccUser,
  620. const STR & strADAccDomain,
  621. const STR & strADAccPass,
  622. PCSTR pszAnonUser,
  623. PCSTR pszAnonDomain)
  624. {
  625. DWORD dwError = NO_ERROR;
  626. //
  627. // create/reconfigure the LDAP Cache object
  628. //
  629. if ( !m_pConnCache ) {
  630. m_pConnCache = new LDAP_CONN_CACHE;
  631. if (m_pConnCache == NULL) {
  632. return ERROR_NOT_ENOUGH_MEMORY;
  633. }
  634. }
  635. dwError = m_pConnCache->Configure(
  636. strADAccUser,
  637. strADAccDomain,
  638. strADAccPass);
  639. if ( dwError != NO_ERROR ) {
  640. return dwError;
  641. }
  642. //
  643. // create / reconfigure / delete the anonymous cache object
  644. //
  645. if (pszAnonUser == NULL) {
  646. if ( m_pAnonymCache ) {
  647. delete m_pAnonymCache;
  648. m_pAnonymCache = NULL;
  649. }
  650. } else {
  651. if ( !m_pAnonymCache ) {
  652. m_pAnonymCache = new ADIO_ANONYM_CACHE;
  653. }
  654. if (m_pAnonymCache == NULL) {
  655. return ERROR_NOT_ENOUGH_MEMORY;
  656. }
  657. dwError = m_pAnonymCache->Configure(
  658. pszAnonUser,
  659. pszAnonDomain);
  660. }
  661. return dwError;
  662. }
  663. /*++
  664. the main service entry point!
  665. this routine is called to submit a request to fetch the user home directory
  666. Arguments:
  667. strUser,
  668. strDomain name and domain of user to get home directory
  669. pstrTarget, pointer for storing the home directory path
  670. ppadioReqCtx, Request handle for leter reference if needed
  671. pfnCallback, Callback function for the AD_IO to notify the caller when results are ready
  672. hCLientCtx caller context to be used when calling callback function
  673. Returns:
  674. ERROR_IO_PENDING on success, other Win32 error on failure
  675. --*/
  676. DWORD
  677. AD_IO::GetUserHomeDir(
  678. const STR & strUser,
  679. const STR & strDomain,
  680. STR * pstrTarget,
  681. ADIO_ASYNC ** ppadioReqCtx,
  682. tpAdioAsyncCallback pfnCallback,
  683. HANDLE hCLientCtx)
  684. {
  685. if (!m_pConnCache) {
  686. return ERROR_BAD_CONFIGURATION;
  687. }
  688. return ADIO_ASYNC::QueryRootDir(
  689. strUser,
  690. strDomain,
  691. m_pConnCache,
  692. pstrTarget,
  693. ppadioReqCtx,
  694. pfnCallback,
  695. hCLientCtx);
  696. }
  697. //---------------------------------------------------------------------------------------
  698. //
  699. // implementation of the ADIO_ANONYM_CACHE class
  700. //
  701. //---------------------------------------------------------------------------------------
  702. /*++
  703. constructor of the ADIO_ANONYM_CACHE object
  704. Arguments:
  705. None.
  706. Returns:
  707. None.
  708. --*/
  709. ADIO_ANONYM_CACHE::ADIO_ANONYM_CACHE() :
  710. m_Valid( FALSE )
  711. {
  712. INITIALIZE_CRITICAL_SECTION( &m_cs );
  713. m_Reference = 1;
  714. }
  715. /*++
  716. destructor of the ADIO_ANONYM_CACHE object
  717. Arguments:
  718. None.
  719. Returns:
  720. None.
  721. --*/
  722. ADIO_ANONYM_CACHE::~ADIO_ANONYM_CACHE()
  723. {
  724. DeleteCriticalSection( &m_cs );
  725. }
  726. /*++
  727. Configure the ADIO_ANONYM_CACHE object. Must be called immediately after creating the
  728. instance. Also called to reconfigure the user name and domain.
  729. Arguments:
  730. strUser - user name that impersonates the anonymous user
  731. strDomain - user domain that impersonates the anonymous user
  732. Returns:
  733. NO_ERROR or Win32 error.
  734. --*/
  735. DWORD
  736. ADIO_ANONYM_CACHE::Configure(
  737. IN PCSTR pszUser,
  738. IN PCSTR pszDomain)
  739. {
  740. DWORD err;
  741. Lock();
  742. m_TimeStamp = 0;
  743. if ( m_User.Copy( pszUser ) &&
  744. m_Domain.Copy( pszDomain )) {
  745. m_Valid = TRUE;
  746. err = NO_ERROR;
  747. } else {
  748. m_Valid = FALSE;
  749. err = ERROR_INVALID_PARAMETER;
  750. }
  751. Unlock();
  752. return err;
  753. }
  754. /*++
  755. increase ref count of the object. If shuting down, or object not initialized (!m_Valid)
  756. ref count not incremented, and returning error.
  757. Argunents:
  758. None.
  759. Returns:
  760. TRUE if object is valid and ref count incremented.
  761. FALSE if object not valid and no ref count incremented
  762. --*/
  763. BOOL
  764. ADIO_ANONYM_CACHE::Reference()
  765. {
  766. BOOL Valid;
  767. Lock();
  768. Valid = m_Valid;
  769. if (Valid) {
  770. m_Reference++;
  771. }
  772. Unlock();
  773. return Valid;
  774. }
  775. /*++
  776. Release reference to the instance. Will decrement the current reference count and delete
  777. the instance when ref count reaches zero.
  778. Arguments:
  779. Shutdown - TRUE on shutdown to indicate m_Valid should be changed to FALSE
  780. Returns:
  781. None.
  782. --*/
  783. VOID
  784. ADIO_ANONYM_CACHE::Release(BOOL Shutdown)
  785. {
  786. BOOL DoDelete = FALSE;
  787. Lock();
  788. if ( Shutdown ) {
  789. m_Valid = FALSE;
  790. }
  791. DBG_ASSERT( m_Reference > 0 );
  792. if ( (m_Reference > 0) && (--m_Reference == 0) ) {
  793. m_Valid = FALSE;
  794. DoDelete = TRUE;
  795. }
  796. Unlock();
  797. if ( DoDelete ) {
  798. delete this;
  799. }
  800. }
  801. /*++
  802. Returns the cahced path for the anonymous user, as previously obtained from AD.
  803. If the cache has not been populated yet, or the refresh interval has elapsed, the
  804. cache is refreshed and the fresh information returned.
  805. Arguments:
  806. TargetPath - reference to buffer where the path is stored
  807. pConnCache - pointer to the ldap connection cache
  808. returns:
  809. NO_ERROR on success, Win32 error on failure.
  810. --*/
  811. DWORD
  812. ADIO_ANONYM_CACHE::GetCachedPath(
  813. STR & TargetPath,
  814. PLDAP_CONN_CACHE pConnCache
  815. )
  816. {
  817. ULONGLONG CurrentTime;
  818. DWORD err = NO_ERROR;
  819. DBG_ASSERT( TargetPath.QuerySize() >= m_Path.QuerySize() );
  820. //
  821. // reference the object so it does not get invalidated while we use it.
  822. // if the object is already invalid, return now.
  823. //
  824. if ( !Reference() ) {
  825. return ERROR_INVALID_HANDLE;
  826. }
  827. //
  828. // first, check if our cached data is up-to-date
  829. //
  830. GetSystemTimeAsFileTime( (PFILETIME)&CurrentTime );
  831. if ( (CurrentTime - m_TimeStamp) < g_MaxAdPropCacheTime ) {
  832. //
  833. // fresh data - return to caller
  834. //
  835. Lock();
  836. if ( !TargetPath.Copy( m_Path )) {
  837. err = ERROR_INSUFFICIENT_BUFFER;
  838. }
  839. Unlock();
  840. goto GetCachedPath_Exit;
  841. }
  842. //
  843. // old data, need to update. Use caller buffer to avoid overrun by another thread
  844. // that may be refreshing simultaneously.
  845. //
  846. err = ADIO_ASYNC::QueryRootDir_Sync(
  847. m_User,
  848. m_Domain,
  849. pConnCache,
  850. TargetPath);
  851. if (err != NO_ERROR) {
  852. goto GetCachedPath_Exit;
  853. }
  854. //
  855. // now update our cache copy. another thread may be doing this in paralel, but that's OK.
  856. //
  857. GetSystemTimeAsFileTime( (PFILETIME)&CurrentTime );
  858. Lock();
  859. if ( m_Path.Copy( TargetPath )) {
  860. m_TimeStamp = CurrentTime;
  861. }
  862. Unlock();
  863. GetCachedPath_Exit:
  864. Release();
  865. return err;
  866. }
  867. //========================================================================================
  868. CRITICAL_SECTION ADIO_ASYNC::m_cs;
  869. LONG ADIO_ASYNC::m_ActiveThreads = 0;
  870. HANDLE ADIO_ASYNC::m_Threads[32];
  871. HANDLE ADIO_ASYNC::m_Events[2] = { NULL, NULL };
  872. const DWORD WaitEventShutdown = 0;
  873. const DWORD WaitEventNotEmpty = 1;
  874. volatile LONG ADIO_ASYNC::m_NumTotalAlloc;
  875. volatile LONG ADIO_ASYNC::m_NumTotalFree;
  876. LIST_ENTRY ADIO_ASYNC::m_WorkListHead;
  877. LIST_ENTRY ADIO_ASYNC::m_FreeListHead;
  878. /*++
  879. Initialize the async request service component. create shutdown event and the
  880. service threads, and init all the static data.
  881. Arguments:
  882. None.
  883. Returns:
  884. None.
  885. --*/
  886. BOOL
  887. ADIO_ASYNC::Initialize()
  888. {
  889. SYSTEM_INFO SysInfo;
  890. INITIALIZE_CRITICAL_SECTION( &m_cs );
  891. m_NumTotalAlloc = 0;
  892. m_NumTotalFree = 0;
  893. InitializeListHead( &m_WorkListHead );
  894. InitializeListHead( &m_FreeListHead );
  895. m_ActiveThreads = 0;
  896. m_Events[ WaitEventShutdown ] = CreateEvent(
  897. NULL, // security
  898. TRUE, // manual reset
  899. FALSE, // initial state non-signaled
  900. NULL); // name
  901. if ( m_Events[ WaitEventShutdown ] == NULL ) {
  902. return FALSE;
  903. }
  904. m_Events[ WaitEventNotEmpty ] = CreateEvent(
  905. NULL, // security
  906. FALSE, // auto reset
  907. FALSE, // initial state non-signaled
  908. NULL); // name
  909. if ( m_Events[ WaitEventNotEmpty ] == NULL ) {
  910. return FALSE;
  911. }
  912. GetSystemInfo( &SysInfo );
  913. //
  914. // start ADIO_ASYNC threads
  915. //
  916. DWORD NumThreads = SysInfo.dwNumberOfProcessors;
  917. if (NumThreads > 4) {
  918. NumThreads = (NumThreads >> 1) + 2;
  919. }
  920. if (NumThreads > 25) {
  921. NumThreads = 25;
  922. }
  923. for (DWORD i = 0; i < NumThreads; i++) {
  924. m_Threads[i] = CreateThread(
  925. NULL, // SecAttribs
  926. 0, // stack size
  927. &WorkerThread,
  928. NULL, // param to thread
  929. 0, // flags
  930. NULL); // ThreadID
  931. if (m_Threads[i] == NULL) {
  932. return FALSE;
  933. }
  934. m_ActiveThreads++;
  935. }
  936. return TRUE;
  937. }
  938. /*++
  939. Terminates the threads serving the asynchronous requests, and cleans up the request objects
  940. Arguments:
  941. None.
  942. Returns:
  943. TRUE.
  944. --*/
  945. BOOL
  946. ADIO_ASYNC::Terminate()
  947. {
  948. PADIO_ASYNC pReq;
  949. //
  950. // if the object was initialized, the shutdown event exists
  951. //
  952. if (m_Events[ WaitEventShutdown ]) {
  953. //
  954. // signal to the threads it's time to bail out
  955. //
  956. SetEvent( m_Events[ WaitEventShutdown ] );
  957. //
  958. // wait for all threads to get the message
  959. //
  960. if (m_ActiveThreads) {
  961. DWORD dwError = WaitForMultipleObjects(
  962. m_ActiveThreads,
  963. m_Threads,
  964. TRUE,
  965. 6000);
  966. DBG_ASSERT( dwError != WAIT_TIMEOUT && dwError != WAIT_FAILED);
  967. while (m_ActiveThreads > 0) {
  968. CloseHandle(m_Threads[ --m_ActiveThreads ]);
  969. }
  970. }
  971. CloseHandle( m_Events[ WaitEventShutdown ] );
  972. m_Events[ WaitEventShutdown ] = NULL;
  973. //
  974. // at this point, all async threads are gone, so we don't need to worry about
  975. // locking any shared resource.
  976. //
  977. //
  978. // complete all requests and free the work list objects
  979. //
  980. while( !IsListEmpty( &m_WorkListHead ) ) {
  981. pReq = CONTAINING_RECORD( RemoveHeadList( &m_WorkListHead ), ADIO_ASYNC, m_Link);
  982. if (pReq->m_State == RequestStateRetrieve) {
  983. //
  984. // we have pending async operation - terminate it
  985. //
  986. pfn_ldap_abandon(
  987. pReq->m_pLdapCacheItem->QueryConnection(),
  988. pReq->m_AsyncMsgNum);
  989. }
  990. pReq->ProcessComplete();
  991. Free( pReq );
  992. }
  993. //
  994. // free the free list objects
  995. //
  996. while( !IsListEmpty( &m_FreeListHead ) ) {
  997. delete CONTAINING_RECORD( RemoveHeadList( &m_FreeListHead ), ADIO_ASYNC, m_Link);
  998. }
  999. if (m_Events[ WaitEventNotEmpty ]) {
  1000. CloseHandle( m_Events[ WaitEventNotEmpty ] );
  1001. m_Events[ WaitEventNotEmpty ] = NULL;
  1002. }
  1003. }
  1004. DeleteCriticalSection( &m_cs );
  1005. return TRUE;
  1006. }
  1007. /*++
  1008. Initiate an LDAP search for the ftproot and ftpdir properties of a user
  1009. Arguments:
  1010. fSyncSearch - boolean flag indicating whether or not to start a synchronous
  1011. search. By default, the search is asynchronous.
  1012. Returns:
  1013. TRUE / FALSE to indicate Success / Failure.
  1014. --*/
  1015. BOOL
  1016. ADIO_ASYNC::ProcessSearch(BOOL fSyncSearch)
  1017. {
  1018. ULONG err = LDAP_SUCCESS;
  1019. CHAR szSearchQuery[ MAX_PATH ];
  1020. INT iWritten;
  1021. //
  1022. // get a cached connection handle. if this is not yet cached, the call will
  1023. // block until the connection is bound.
  1024. //
  1025. m_pLdapCacheItem = m_pConnCache->QueryLdapConnection( m_strDomain );
  1026. if ( !m_pLdapCacheItem ) {
  1027. m_Status = GetLastError();
  1028. return FALSE;
  1029. }
  1030. //
  1031. // format the query string and run the query
  1032. //
  1033. iWritten = _snprintf( szSearchQuery,
  1034. sizeof( szSearchQuery ),
  1035. pszQueryFormatString,
  1036. m_strUser.QueryStr());
  1037. if ((iWritten <= 0) || (iWritten >= sizeof( szSearchQuery ))) {
  1038. m_Status = ERROR_INSUFFICIENT_BUFFER;
  1039. return FALSE;
  1040. }
  1041. if (fSyncSearch) {
  1042. //
  1043. // do a synchronous search
  1044. //
  1045. err = pfn_ldap_search_s(
  1046. m_pLdapCacheItem->QueryConnection(),
  1047. m_pLdapCacheItem->QueryForestDN(),
  1048. LDAP_SCOPE_SUBTREE,
  1049. szSearchQuery,
  1050. aszRetAttribs,
  1051. FALSE, // attribute type & values
  1052. &m_pLdapMsg);
  1053. } else {
  1054. //
  1055. // initiate an asynchronous search
  1056. //
  1057. m_AsyncMsgNum = pfn_ldap_search(
  1058. m_pLdapCacheItem->QueryConnection(),
  1059. m_pLdapCacheItem->QueryForestDN(),
  1060. LDAP_SCOPE_SUBTREE,
  1061. szSearchQuery,
  1062. aszRetAttribs,
  1063. FALSE); // attribute type & values
  1064. if (m_AsyncMsgNum == -1) {
  1065. err = pfn_LdapGetLastError();
  1066. if (err == LDAP_SUCCESS) {
  1067. err = LDAP_OPERATIONS_ERROR;
  1068. }
  1069. } else {
  1070. err = LDAP_SUCCESS;
  1071. }
  1072. }
  1073. if ( err != LDAP_SUCCESS ) {
  1074. DBGPRINTF((DBG_CONTEXT, "ldap_search%s() failed (0x%X)\n",
  1075. fSyncSearch ? "_s" : "", err));
  1076. m_Status = err;
  1077. return FALSE;
  1078. }
  1079. m_Status = ERROR_IO_PENDING;
  1080. return TRUE;
  1081. }
  1082. /*++
  1083. after an LDAP query (search) has compleeted, this routine extracts the results
  1084. and constructs the user home directory
  1085. Arguments:
  1086. None.
  1087. Returns:
  1088. TRUE on success, FALSE on failure.
  1089. --*/
  1090. BOOL
  1091. ADIO_ASYNC::ProcessRetrieve()
  1092. {
  1093. ULONG err = LDAP_SUCCESS;
  1094. LDAPMessage *pLdapEntry = NULL;
  1095. PCHAR *ppszAttribValRoot = NULL, *ppszAttribValDir = NULL;
  1096. //
  1097. // retrieve the first (and only) result entry
  1098. //
  1099. pLdapEntry = pfn_ldap_first_entry( m_pLdapCacheItem->QueryConnection(), m_pLdapMsg );
  1100. if ( pLdapEntry == NULL ) {
  1101. err = pfn_LdapGetLastError();
  1102. if (err == LDAP_SUCCESS) {
  1103. // no entry exists, map to an appropriate error
  1104. err = LDAP_NO_RESULTS_RETURNED;
  1105. }
  1106. DBGPRINTF((DBG_CONTEXT, "ldap_first_entry() failed (0x%X)\n", err));
  1107. m_Status = err;
  1108. return FALSE;
  1109. }
  1110. //
  1111. // we currently do not allow more than one value, but this could become a feature
  1112. // to support alternate share points...
  1113. //
  1114. ppszAttribValRoot = pfn_ldap_get_values(
  1115. m_pLdapCacheItem->QueryConnection(),
  1116. pLdapEntry,
  1117. pszFtpRoot );
  1118. ppszAttribValDir = pfn_ldap_get_values(
  1119. m_pLdapCacheItem->QueryConnection(),
  1120. pLdapEntry,
  1121. pszFtpDir );
  1122. if ( ppszAttribValRoot && // attribute not found
  1123. *ppszAttribValRoot && // no value for attribute
  1124. **ppszAttribValRoot && // empty value for attribute
  1125. !*(ppszAttribValRoot + 1) && // more than one value
  1126. ppszAttribValDir && // attribute not found
  1127. *ppszAttribValDir && // no value for attribute
  1128. **ppszAttribValDir && // empty value for attribute
  1129. !*(ppszAttribValDir + 1)) // more than one value
  1130. {
  1131. BOOL fOk = TRUE;
  1132. //
  1133. // construct the combined path
  1134. //
  1135. //
  1136. // copy the root part first
  1137. //
  1138. fOk = fOk && m_pstrTarget->Copy( *ppszAttribValRoot );
  1139. //
  1140. // add a path separator if there is not one already
  1141. //
  1142. if ( !IS_PATH_SEP( m_pstrTarget->QueryLastChar() ) ) {
  1143. fOk = fOk && m_pstrTarget->Append( "\\", 1);
  1144. }
  1145. //
  1146. // copy the dir part, skipping the separator if present
  1147. //
  1148. if ( IS_PATH_SEP( **ppszAttribValDir ) ) {
  1149. fOk = fOk && m_pstrTarget->Append( *ppszAttribValDir + 1 );
  1150. } else {
  1151. fOk = fOk && m_pstrTarget->Append( *ppszAttribValDir );
  1152. }
  1153. if ( !fOk ) {
  1154. err = LDAP_NO_RESULTS_RETURNED;
  1155. }
  1156. } else {
  1157. err = LDAP_NO_RESULTS_RETURNED;
  1158. }
  1159. if ( ppszAttribValRoot ) {
  1160. pfn_ldap_value_free( ppszAttribValRoot );
  1161. }
  1162. if ( ppszAttribValDir ) {
  1163. pfn_ldap_value_free( ppszAttribValDir );
  1164. }
  1165. m_Status = err;
  1166. return err == LDAP_SUCCESS;
  1167. }
  1168. /*++
  1169. Complete the processing of an asynchronous request. Notify the client.
  1170. Arguments:
  1171. None.
  1172. Returns:
  1173. None.
  1174. --*/
  1175. VOID
  1176. ADIO_ASYNC::ProcessComplete()
  1177. {
  1178. Lock();
  1179. if ( m_pfnClientCallback ) {
  1180. m_pfnClientCallback( m_hClientCtx, m_Status );
  1181. } // else, the request may have been canceled.
  1182. Unlock();
  1183. }
  1184. /*++
  1185. Checks if the object is ready for processing. Either a newly submitted object, or one for
  1186. which the LDAP processing has completed.
  1187. Arguments:
  1188. None.
  1189. Returns:
  1190. TRUE if object is ready.
  1191. --*/
  1192. BOOL
  1193. ADIO_ASYNC::IsResultReady()
  1194. {
  1195. static LDAP_TIMEVAL sZero = {0,0};
  1196. ULONG ResType;
  1197. BOOL fRet;
  1198. if (m_State == RequestStateInitial) {
  1199. return TRUE;
  1200. }
  1201. ResType = pfn_ldap_result(
  1202. m_pLdapCacheItem->QueryConnection(),
  1203. m_AsyncMsgNum,
  1204. LDAP_MSG_ALL,
  1205. &sZero,
  1206. &m_pLdapMsg);
  1207. if (ResType == 0) {
  1208. //
  1209. // no result yet
  1210. //
  1211. fRet = FALSE;
  1212. } else {
  1213. //
  1214. // it's either a valid completion (>0) or a failure (-1). (yes, I know this is ULONG,
  1215. // but I did not write the MSDN documentation). If it is a failure, no matter why this
  1216. // failed, we can't recover. So just let it be handled as an error.
  1217. //
  1218. m_LdapMsgType = ResType;
  1219. fRet = TRUE;
  1220. }
  1221. return fRet;
  1222. }
  1223. /*++
  1224. Find the next request object that is ready for processing. It's either a new request
  1225. or one that and async LDAP call has completed. Removes the object from the work list.
  1226. Arguments:
  1227. ppReq - will be set to point to the ready request object.
  1228. Returns:
  1229. TRUE if ready object found.
  1230. --*/
  1231. BOOL
  1232. ADIO_ASYNC::FetchRequest(
  1233. ADIO_ASYNC **ppReq)
  1234. {
  1235. BOOL fFound = FALSE;
  1236. ADIO_ASYNC *pReq;
  1237. LIST_ENTRY *pEntry;
  1238. Lock();
  1239. for( pEntry = m_WorkListHead.Flink;
  1240. pEntry != &m_WorkListHead;
  1241. pEntry = pEntry->Flink) {
  1242. pReq = CONTAINING_RECORD( pEntry, ADIO_ASYNC, m_Link);
  1243. if ( pReq->IsResultReady() ) {
  1244. *ppReq = pReq;
  1245. RemoveEntryList( pEntry );
  1246. fFound = TRUE;
  1247. break;
  1248. }
  1249. }
  1250. Unlock();
  1251. return fFound;
  1252. }
  1253. /*++
  1254. Terminate a pending asynchronous LDAP operation
  1255. Arguments:
  1256. None.
  1257. Returns:
  1258. None.
  1259. --*/
  1260. VOID
  1261. ADIO_ASYNC::EndRequest()
  1262. {
  1263. BOOL fFound = FALSE;
  1264. LIST_ENTRY *pEntry;
  1265. Lock();
  1266. for( pEntry = m_WorkListHead.Flink;
  1267. pEntry != &m_WorkListHead;
  1268. pEntry = pEntry->Flink) {
  1269. if (pEntry == &m_Link) {
  1270. RemoveEntryList( pEntry );
  1271. fFound = TRUE;
  1272. break;
  1273. }
  1274. }
  1275. //
  1276. // make sure we do not callback the user object. even if the request is being processed
  1277. // and we do not cancel it, at least we will not call the user object if it is no longer
  1278. // around
  1279. //
  1280. m_pfnClientCallback = NULL;
  1281. m_hClientCtx = NULL;
  1282. Unlock();
  1283. if ( fFound ) {
  1284. //
  1285. // it was on the work list. stop the operation and free the object.
  1286. // if the object was not found, it may be in some transient state, or processed
  1287. // if so, the processing will complete.
  1288. //
  1289. DBG_ASSERT( this == CONTAINING_RECORD( pEntry, ADIO_ASYNC, m_Link) );
  1290. if (m_State == RequestStateRetrieve) {
  1291. pfn_ldap_abandon(
  1292. m_pLdapCacheItem->QueryConnection(),
  1293. m_AsyncMsgNum);
  1294. }
  1295. //
  1296. // note! we delete the object here! no code after this point
  1297. //
  1298. Free( this );
  1299. }
  1300. }
  1301. /*++
  1302. Allocate a request object either from the free cache or from system memory
  1303. Arguments:
  1304. None.
  1305. Returns:
  1306. pointer to allocated object, or NULL on error.
  1307. --*/
  1308. PADIO_ASYNC
  1309. ADIO_ASYNC::Alloc()
  1310. {
  1311. PADIO_ASYNC pReq = NULL;
  1312. Lock();
  1313. if ( !IsListEmpty( &m_FreeListHead ) ) {
  1314. pReq = CONTAINING_RECORD( RemoveHeadList( &m_FreeListHead ), ADIO_ASYNC, m_Link);
  1315. m_NumTotalFree--;
  1316. }
  1317. Unlock();
  1318. if (pReq == NULL) {
  1319. pReq = new ADIO_ASYNC;
  1320. if (pReq != NULL) {
  1321. InterlockedIncrement( &m_NumTotalAlloc );
  1322. }
  1323. }
  1324. return pReq;
  1325. }
  1326. /*++
  1327. Cleanup a request that has completed, and return the object to a cache of free objects.
  1328. If we have too many free objects, free up memory.
  1329. Assume the object is *NOT* on any list.
  1330. Arguments:
  1331. pReq - pointer to the instance (this is a static method)
  1332. Returns:
  1333. None.
  1334. --*/
  1335. VOID
  1336. ADIO_ASYNC::Free(PADIO_ASYNC pReq)
  1337. {
  1338. //
  1339. // cleanup the object, free resources
  1340. //
  1341. if ( pReq->m_pLdapMsg ) {
  1342. pfn_ldap_msgfree( pReq->m_pLdapMsg );
  1343. pReq->m_pLdapMsg = NULL;
  1344. }
  1345. if ( pReq->m_pLdapCacheItem ) {
  1346. pReq->m_pLdapCacheItem->Release();
  1347. pReq->m_pLdapCacheItem = NULL;
  1348. }
  1349. //
  1350. // if we are shuting down, don't bother returning this to the free list
  1351. //
  1352. if (m_Events[ WaitEventShutdown ] == NULL) {
  1353. delete pReq;
  1354. return;
  1355. }
  1356. Lock();
  1357. //
  1358. // add the object to the list of free
  1359. //
  1360. InsertHeadList( &m_FreeListHead, &pReq->m_Link);
  1361. m_NumTotalFree++;
  1362. //
  1363. // is it time for cleanup? (are we over the high threshold)
  1364. //
  1365. if ((m_NumTotalFree > AsyncMinFreeReqObjs) &&
  1366. (m_NumTotalFree > AsyncMaxFreeReqRate * m_NumTotalAlloc / 100 ) ) {
  1367. LONG NumToFree;
  1368. //
  1369. // delete objects until we hit the percentage or numeric threshold
  1370. //
  1371. NumToFree = m_NumTotalFree - (AsyncMinFreeReqRate * m_NumTotalAlloc / 100);
  1372. if (NumToFree > (m_NumTotalFree - AsyncMinFreeReqObjs) ) {
  1373. NumToFree = (m_NumTotalFree - AsyncMinFreeReqObjs);
  1374. }
  1375. m_NumTotalFree -= NumToFree;
  1376. m_NumTotalAlloc -= NumToFree;
  1377. while ( NumToFree ) {
  1378. delete CONTAINING_RECORD( RemoveHeadList( &m_FreeListHead ), ADIO_ASYNC, m_Link );
  1379. }
  1380. }
  1381. Unlock();
  1382. }
  1383. /*++
  1384. Work thread for processing asynchronous requests to fetch user properties from AD.
  1385. Loop until shutdown, fetch requests ready for processing, process, then either complete
  1386. the request and free the object or return to the work queue for further processing.
  1387. Arguments:
  1388. None.
  1389. Returns:
  1390. None.
  1391. --*/
  1392. DWORD WINAPI
  1393. ADIO_ASYNC::WorkerThread(
  1394. LPVOID pParam)
  1395. {
  1396. DWORD resWait;
  1397. PADIO_ASYNC pReq;
  1398. //
  1399. // we loop in here until shutdown. the inner loop runs as long as there
  1400. // are request objects ready to be serviced. The outer loop will go to sleep
  1401. // when there are no ready requests, and wake up after a timeout. We do not have
  1402. // LDAP notifications, so this is the best we can do.
  1403. //
  1404. while(1) {
  1405. while (FetchRequest( &pReq )) {
  1406. if ( pReq->m_LdapMsgType == ~0 ) { // MSDN says this ULONG is -1 on error
  1407. //
  1408. // the call to ldap_result failed, so we can't do anything about it.
  1409. //
  1410. pReq->m_Status = pfn_LdapGetLastError();
  1411. pReq->ProcessComplete();
  1412. Free( pReq );
  1413. continue;
  1414. }
  1415. switch (pReq->m_State) {
  1416. case RequestStateInitial:
  1417. pReq->ProcessSearch();
  1418. pReq->m_State = RequestStateRetrieve;
  1419. break;
  1420. case RequestStateRetrieve:
  1421. if (pReq->m_LdapMsgType != LDAP_RES_SEARCH_RESULT) {
  1422. DBG_ASSERT( FALSE );
  1423. pReq->m_Status = LDAP_LOCAL_ERROR;
  1424. break;
  1425. }
  1426. pfn_ldap_parse_result(
  1427. pReq->m_pLdapCacheItem->QueryConnection(),
  1428. pReq->m_pLdapMsg,
  1429. &(pReq->m_Status),
  1430. NULL,
  1431. NULL,
  1432. NULL,
  1433. NULL,
  1434. FALSE); // don't free the message
  1435. if (pReq->m_Status != LDAP_SUCCESS) {
  1436. break;
  1437. }
  1438. pReq->ProcessRetrieve();
  1439. pReq->m_State = RequestStateDone;
  1440. break;
  1441. default:
  1442. DBG_ASSERT( FALSE );
  1443. pReq->m_Status = LDAP_LOCAL_ERROR;
  1444. break;
  1445. }
  1446. if (pReq->m_Status == ERROR_IO_PENDING) {
  1447. //
  1448. // just queue the request. no need to signal the NotEmpty event, as this thread
  1449. // will only go for a short nap as the queue is not empty.
  1450. //
  1451. pReq->QueueWork();
  1452. } else {
  1453. //
  1454. // processing of this request is complete.
  1455. //
  1456. pReq->ProcessComplete();
  1457. Free( pReq );
  1458. }
  1459. }
  1460. if ( IsListEmpty( &m_WorkListHead ) ) {
  1461. //
  1462. // there is no critical section around testing the above condition and determining
  1463. // the wait method. Any thread that just inserted a request to the queue, will likely
  1464. // find it not-empty (unless it has been picked up by another thread.
  1465. //
  1466. //
  1467. // nothing in the queue (or another thread is handling it), go for a long sleep
  1468. // only wake up if a new request comes in, or shutdown.
  1469. //
  1470. resWait = WaitForMultipleObjects( 2, m_Events, FALSE, INFINITE );
  1471. } else {
  1472. //
  1473. // there is something in the queue, but results are not ready yet. go take a nap
  1474. // and see if the results are ready then.
  1475. //
  1476. resWait = WaitForSingleObject( m_Events[ WaitEventShutdown ], AsyncPollXval);
  1477. }
  1478. if (resWait == WaitEventShutdown) {
  1479. //
  1480. // Shutdown.
  1481. //
  1482. break;
  1483. }
  1484. DBG_ASSERT( (resWait == WAIT_TIMEOUT ) || (resWait == WaitEventNotEmpty ) );
  1485. }
  1486. return ERROR_SUCCESS;
  1487. }
  1488. /*++
  1489. Submit a request to fetch AD properties for a user logging in. This is an asynchronous call,
  1490. Arguments:
  1491. strUser,
  1492. strDomain name and domain of user logging in
  1493. pConnCache Connection cache list for obtaining a cached LDAP connection
  1494. pstrTarget points to output buffer where results are stored
  1495. ppadioReqCtx on return, contains request context, so the request can be reerenced by the caller
  1496. pfnCallback callback function to notify caller when results are available
  1497. hClientCtx client context to use when calling callback function
  1498. Returns:
  1499. Upon successful submition, return ERROR_IO_PENDING. otherwise, Win32 error.
  1500. --*/
  1501. DWORD
  1502. ADIO_ASYNC::QueryRootDir(
  1503. const STR & strUser,
  1504. const STR & strDomain,
  1505. PLDAP_CONN_CACHE pConnCache,
  1506. STR * pstrTarget,
  1507. ADIO_ASYNC ** ppadioReqCtx,
  1508. tpAdioAsyncCallback pfnCallback,
  1509. HANDLE hClientCtx)
  1510. {
  1511. DBG_ASSERT( pConnCache != NULL );
  1512. DBG_ASSERT( pstrTarget != NULL );
  1513. DBG_ASSERT( ppadioReqCtx != NULL );
  1514. DBG_ASSERT( pfnCallback != NULL );
  1515. DBG_ASSERT( hClientCtx != NULL );
  1516. PADIO_ASYNC pReq = Alloc();
  1517. if (pReq == NULL) {
  1518. return ERROR_NOT_ENOUGH_MEMORY;
  1519. }
  1520. pReq->m_Status = NO_ERROR;
  1521. pReq->m_State = RequestStateInitial;
  1522. pReq->m_pConnCache = pConnCache;
  1523. pReq->m_pLdapCacheItem = NULL;
  1524. pReq->m_strDomain.Copy( strDomain );
  1525. pReq->m_strUser.Copy( strUser );
  1526. pReq->m_pstrTarget = pstrTarget;
  1527. pReq->m_pLdapMsg = NULL;
  1528. pReq->m_LdapMsgType = 0;
  1529. pReq->m_pfnClientCallback = pfnCallback;
  1530. pReq->m_hClientCtx = hClientCtx;
  1531. *ppadioReqCtx = pReq;
  1532. //
  1533. // submit the request for processing, and wake up a thread to handle it.
  1534. //
  1535. pReq->QueueWork();
  1536. SetEvent( m_Events[ WaitEventNotEmpty ] );
  1537. return ERROR_IO_PENDING;
  1538. }
  1539. /*++
  1540. Fetch user properties from AD. this is a synchronous (blocking) call, used for the
  1541. anonymous user properties.
  1542. Arguments
  1543. strUser,
  1544. strDomain name and domain of user logging in
  1545. pConnCache Connection cache list for obtaining a cached LDAP connection
  1546. pstrTarget points to output buffer where results are stored
  1547. Returns:
  1548. Win32 Error.
  1549. --*/
  1550. DWORD
  1551. ADIO_ASYNC::QueryRootDir_Sync(
  1552. const STR & strUser,
  1553. const STR & strDomain,
  1554. PLDAP_CONN_CACHE pConnCache,
  1555. STR & strTarget)
  1556. {
  1557. DBG_ASSERT( pConnCache != NULL );
  1558. PADIO_ASYNC pReq = Alloc();
  1559. if (pReq == NULL) {
  1560. return ERROR_NOT_ENOUGH_MEMORY;
  1561. }
  1562. pReq->m_Status = NO_ERROR;
  1563. pReq->m_pConnCache = pConnCache;
  1564. pReq->m_pLdapCacheItem = NULL;
  1565. pReq->m_strDomain.Copy( strDomain );
  1566. pReq->m_strUser.Copy( strUser );
  1567. pReq->m_pstrTarget = &strTarget;
  1568. pReq->m_pLdapMsg = NULL;
  1569. DWORD err = NO_ERROR;
  1570. if (!pReq->ProcessSearch( TRUE ) || !pReq->ProcessRetrieve()) {
  1571. err = pReq->m_Status;
  1572. DBG_ASSERT( err );
  1573. if (err == NO_ERROR) {
  1574. err = LDAP_OPERATIONS_ERROR;
  1575. }
  1576. }
  1577. Free( pReq );
  1578. return err;
  1579. }