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.

1554 lines
44 KiB

  1. /*++
  2. Copyright (c) 1999-2000 Microsoft Corporation
  3. Module Name:
  4. RDPRemoteDesktopServerHost
  5. Abstract:
  6. This module contains the CRemoteDesktopServerHost implementation
  7. of RDS session objects.
  8. It manages a collection of open ISAFRemoteDesktopSession objects.
  9. New RDS session objects instances are created using the CreateRemoteDesktopSession
  10. method. Existing RDS session objects are opened using the OpenRemoteDesktopSession
  11. method. RDS session objects are closed using the CloseRemoteDesktopSession method.
  12. When an RDS object is opened or created, the CRemoteDesktopServerHost
  13. object adds a reference of its own to the object so that the object will
  14. stay around, even if the opening application exits. This reference
  15. is retracted when the application calls the CloseRemoteDesktopSession method.
  16. In addition to the reference count the CRemoteDesktopServerHost adds to
  17. the RDS session object, a reference count is also added to itself so that
  18. the associated exe continues to run while RDS session objects are active.
  19. Author:
  20. Tad Brockway 02/00
  21. Revision History:
  22. Aug 3, 01 HueiWang
  23. Add ticket expiration logic, reason is sessmgr will expire the ticket and
  24. rdshost never got inform of this action so it will keep ticket object open
  25. until reboot or user/caller ever try to open the ticket again, this cause leak
  26. in rdshost and sessmgr since ticket object (CRemoteDesktopSession) has a reference
  27. count on sessmgr's IRemoteDesktopHelpSession object.
  28. We have different ways to implement expiration logic, waitable timer or event via
  29. threadpool or simple windows WM_TIMER message, for waitable timer, threads owns timer
  30. must persist (MSDN), WM_TIMER is simpliest but WM_TIMER procedure does not take user
  31. define data as parameter which will require us to store server host object in _Module,
  32. this works fine with STA and SINGLETON but if we change to MTA, we would get into
  33. trouble.
  34. --*/
  35. //#include <RemoteDesktop.h>
  36. #include "stdafx.h"
  37. #ifdef TRC_FILE
  38. #undef TRC_FILE
  39. #endif
  40. #define TRC_FILE "_svrhst"
  41. #include "RemoteDesktopUtils.h"
  42. #include "parseaddr.h"
  43. #include "RDSHost.h"
  44. #include "RemoteDesktopServerHost.h"
  45. #include "TSRDPRemoteDesktopSession.h"
  46. #include "rderror.h"
  47. CRemoteDesktopServerHost* g_pRemoteDesktopServerHostObj = NULL;
  48. void
  49. CRemoteDesktopServerHost::RemoteDesktopDisabled()
  50. /*++
  51. Routine Description:
  52. Function to disconnect all connections because RA is disabled.
  53. Parameters:
  54. None.
  55. Returns:
  56. None.
  57. --*/
  58. {
  59. SessionMap::iterator iter;
  60. SessionMap::iterator iter_delete;
  61. //
  62. // Cleanup m_SessionMap entries.
  63. //
  64. iter = m_SessionMap.begin();
  65. while( iter != m_SessionMap.end() ) {
  66. if( NULL != (*iter).second ) {
  67. //
  68. // We are shutting down, fire disconnect to all client
  69. //
  70. if( NULL != (*iter).second->obj ) {
  71. (*iter).second->obj->Disconnect();
  72. (*iter).second->obj->Release();
  73. }
  74. delete (*iter).second;
  75. (*iter).second = NULL;
  76. }
  77. iter_delete = iter;
  78. iter++;
  79. m_SessionMap.erase(iter_delete);
  80. }
  81. }
  82. ///////////////////////////////////////////////////////
  83. //
  84. // CRemoteDesktopServerHost Methods
  85. //
  86. HRESULT
  87. CRemoteDesktopServerHost::FinalConstruct()
  88. /*++
  89. Routine Description:
  90. Arguments:
  91. Return Value:
  92. S_OK on success. Otherwise, an error code is returned.
  93. --*/
  94. {
  95. DC_BEGIN_FN("CRemoteDesktopServerHost::FinalConstruct");
  96. HRESULT hr = S_OK;
  97. DWORD status;
  98. ASSERT( m_hTicketExpiration == NULL );
  99. ASSERT( g_pRemoteDesktopServerHostObj == NULL );
  100. //
  101. // We are singleton object so cache this object for RA policy change.
  102. //
  103. g_pRemoteDesktopServerHostObj = this;
  104. //
  105. // Create manual event to expire ticket.
  106. //
  107. m_hTicketExpiration = CreateEvent(NULL, TRUE, FALSE, NULL);
  108. if( NULL == m_hTicketExpiration ) {
  109. status = GetLastError();
  110. hr = HRESULT_FROM_WIN32( status );
  111. TRC_ERR((TB, L"CreateEvent: %08X", hr));
  112. ASSERT( FALSE );
  113. }
  114. CLEANUPANDEXIT:
  115. DC_END_FN();
  116. return hr;
  117. }
  118. CRemoteDesktopServerHost::~CRemoteDesktopServerHost()
  119. /*++
  120. Routine Description:
  121. Destructor
  122. Arguments:
  123. Return Value:
  124. S_OK on success. Otherwise, an error code is returned.
  125. --*/
  126. {
  127. DC_BEGIN_FN("CRemoteDesktopServerHost::~CRemoteDesktopServerHost");
  128. BOOL success;
  129. //
  130. // Clean up the local system SID.
  131. //
  132. if (m_LocalSystemSID != NULL) {
  133. FreeSid(m_LocalSystemSID);
  134. }
  135. if( NULL != m_hTicketExpirationWaitObject ) {
  136. success = UnregisterWait( m_hTicketExpirationWaitObject );
  137. ASSERT( TRUE == success );
  138. if( FALSE == success ) {
  139. TRC_ERR((TB, L"UnregisterWait: %08X", GetLastError()));
  140. //
  141. // MSDN on RegisterWaitForSingleObject(),
  142. //
  143. // If this handle (m_hTicketExpiration) is closed while
  144. // the wait is still pending, the function's behavior
  145. // is undefined.
  146. //
  147. // So we ignore closing m_hTicketExpiration and exit.
  148. //
  149. goto CLEANUPANDEXIT;
  150. }
  151. }
  152. //
  153. // Close our expiration handle
  154. //
  155. if( NULL != m_hTicketExpiration ) {
  156. CloseHandle( m_hTicketExpiration );
  157. }
  158. CLEANUPANDEXIT:
  159. DC_END_FN();
  160. }
  161. STDMETHODIMP
  162. CRemoteDesktopServerHost::CreateRemoteDesktopSession(
  163. REMOTE_DESKTOP_SHARING_CLASS sharingClass,
  164. BOOL fEnableCallback,
  165. LONG timeOut,
  166. BSTR userHelpBlob,
  167. ISAFRemoteDesktopSession **session
  168. )
  169. /*++
  170. Routine Description:
  171. Create a new RDS session
  172. Arguments:
  173. Return Value:
  174. S_OK on success. Otherwise, an error code is returned.
  175. --*/
  176. {
  177. DC_BEGIN_FN("CRemoteDesktopServerHost::CreateRemoteDesktopSession");
  178. HRESULT hr;
  179. CComBSTR bstr; bstr = "";
  180. hr = CreateRemoteDesktopSessionEx(
  181. sharingClass,
  182. fEnableCallback,
  183. timeOut,
  184. userHelpBlob,
  185. -1,
  186. bstr,
  187. session
  188. );
  189. DC_END_FN();
  190. return hr;
  191. }
  192. STDMETHODIMP
  193. CRemoteDesktopServerHost::CreateRemoteDesktopSessionEx(
  194. REMOTE_DESKTOP_SHARING_CLASS sharingClass,
  195. BOOL bEnableCallback,
  196. LONG timeout,
  197. BSTR userHelpCreateBlob,
  198. LONG tsSessionID,
  199. BSTR userSID,
  200. ISAFRemoteDesktopSession **session
  201. )
  202. /*++
  203. Routine Description:
  204. Create a new RDS session
  205. Note that the caller MUST call OpenRemoteDesktopSession() subsequent to
  206. a successful completion of this call.
  207. The connection does NOT happen until OpenRemoteDesktopSession() is called.
  208. This call just initializes certain data, it does not open a connection
  209. Arguments:
  210. sharingClass - Desktop Sharing Class
  211. fEnableCallback - TRUE if the Resolver is Enabled
  212. timeOut - Lifespan of Remote Desktop Session
  213. userHelpBlob - Optional User Blob to be Passed
  214. to Resolver.
  215. tsSessionID - Terminal Services Session ID or -1 if
  216. undefined.
  217. userSID - User SID or "" if undefined.
  218. session - Returned Remote Desktop Session Interface.
  219. Return Value:
  220. S_OK on success. Otherwise, an error code is returned.
  221. --*/
  222. {
  223. DC_BEGIN_FN("CRemoteDesktopServerHost::CreateRemoteDesktopSessionEx");
  224. HRESULT hr = S_OK;
  225. HRESULT hr_tmp;
  226. CComObject<CRemoteDesktopSession> *obj = NULL;
  227. PSESSIONMAPENTRY mapEntry;
  228. PSID psid;
  229. DWORD ticketExpireTime;
  230. //
  231. // Get the local system SID.
  232. //
  233. psid = GetLocalSystemSID();
  234. if (psid == NULL) {
  235. hr = HRESULT_FROM_WIN32(GetLastError());
  236. goto CLEANUPANDEXIT;
  237. }
  238. //
  239. // Need to impersonate the caller in order to determine if it is
  240. // running in SYSTEM context.
  241. //
  242. hr = CoImpersonateClient();
  243. if (hr != S_OK) {
  244. TRC_ERR((TB, L"CoImpersonateClient: %08X", hr));
  245. goto CLEANUPANDEXIT;
  246. }
  247. //
  248. // For Whistler, instances of a Remote Desktop Session are only
  249. // "openable" from SYSTEM context, for security reasons.
  250. //
  251. #ifndef DISABLESECURITYCHECKS
  252. if (!IsCallerSystem(psid)) {
  253. TRC_ERR((TB, L"Caller is not SYSTEM."));
  254. ASSERT(FALSE);
  255. CoRevertToSelf();
  256. hr = HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED );
  257. goto CLEANUPANDEXIT;
  258. }
  259. #endif
  260. hr = CoRevertToSelf();
  261. if (hr != S_OK) {
  262. TRC_ERR((TB, L"CoRevertToSelf: %08X", hr));
  263. goto CLEANUPANDEXIT;
  264. }
  265. if( sharingClass != DESKTOPSHARING_DEFAULT &&
  266. sharingClass != NO_DESKTOP_SHARING &&
  267. sharingClass != VIEWDESKTOP_PERMISSION_REQUIRE &&
  268. sharingClass != VIEWDESKTOP_PERMISSION_NOT_REQUIRE &&
  269. sharingClass != CONTROLDESKTOP_PERMISSION_REQUIRE &&
  270. sharingClass != CONTROLDESKTOP_PERMISSION_NOT_REQUIRE ) {
  271. // invalid parameter.
  272. hr = E_INVALIDARG;
  273. goto CLEANUPANDEXIT;
  274. }
  275. if( timeout <= 0 ) {
  276. hr = E_INVALIDARG;
  277. goto CLEANUPANDEXIT;
  278. }
  279. if( NULL == session ) {
  280. hr = E_POINTER;
  281. goto CLEANUPANDEXIT;
  282. }
  283. //
  284. // Instantiate the desktop server. Currently, we only support
  285. // TSRDP.
  286. //
  287. obj = new CComObject<CTSRDPRemoteDesktopSession>();
  288. if (obj != NULL) {
  289. //
  290. // ATL would normally take care of this for us.
  291. //
  292. obj->InternalFinalConstructAddRef();
  293. hr = obj->FinalConstruct();
  294. obj->InternalFinalConstructRelease();
  295. }
  296. else {
  297. TRC_ERR((TB, L"Can't instantiate CTSRDPRemoteDesktopServer"));
  298. hr = E_OUTOFMEMORY;
  299. goto CLEANUPANDEXIT;
  300. }
  301. //
  302. // Initialize the object.
  303. //
  304. hr = obj->Initialize(
  305. NULL, this, sharingClass, bEnableCallback,
  306. timeout, userHelpCreateBlob, tsSessionID, userSID
  307. );
  308. if (!SUCCEEDED(hr)) {
  309. goto CLEANUPANDEXIT;
  310. }
  311. hr = obj->get_ExpireTime( &ticketExpireTime );
  312. if( FAILED(hr) ) {
  313. goto CLEANUPANDEXIT;
  314. }
  315. if( ticketExpireTime < (DWORD)time(NULL) ) {
  316. // ticket already expired, no need to continue,
  317. // overactive assert here is just to check we
  318. // should never come to this.
  319. hr = E_INVALIDARG;
  320. goto CLEANUPANDEXIT;
  321. }
  322. //
  323. // Add it to the session map.
  324. //
  325. mapEntry = new SESSIONMAPENTRY();
  326. if (mapEntry == NULL) {
  327. goto CLEANUPANDEXIT;
  328. }
  329. mapEntry->obj = obj;
  330. mapEntry->ticketExpireTime = ticketExpireTime;
  331. try {
  332. m_SessionMap.insert(
  333. SessionMap::value_type(obj->GetHelpSessionID(), mapEntry)
  334. );
  335. }
  336. catch(CRemoteDesktopException x) {
  337. hr = HRESULT_FROM_WIN32(x.m_ErrorCode);
  338. delete mapEntry;
  339. goto CLEANUPANDEXIT;
  340. }
  341. //
  342. // Get the ISAFRemoteDesktopSession interface pointer.
  343. //
  344. hr = obj->QueryInterface(
  345. IID_ISAFRemoteDesktopSession,
  346. (void**)session
  347. );
  348. if (!SUCCEEDED(hr)) {
  349. //
  350. // TODO : remove from m_SessionMap, this should never
  351. // failed but just in case, then we would have orphan object
  352. // in the m_SessionMap which might cause AV when we loop for
  353. // next ticket to expire
  354. //
  355. TRC_ERR((TB, L"m_RemoteDesktopSession->QueryInterface: %08X", hr));
  356. goto CLEANUPANDEXIT;
  357. }
  358. //
  359. // Add a reference to the object and to ourself so we can both
  360. // stick around, even if the app goes away. The app needs to explicitly
  361. // call CloseRemoteDesktopSession for the object to go away.
  362. //
  363. obj->AddRef();
  364. long count;
  365. count = this->AddRef();
  366. TRC_NRM((TB, TEXT("CreateRemoteDesktopSessionEx AddRef SrvHost count: %08X %08X"), count, m_SessionMap.size()));
  367. //
  368. // Added ticket in expiration monitor list, if anything goes wrong,
  369. // we still can function, just no expiration running until next
  370. // CreateXXX, OpenXXX or CloseXXX call.
  371. //
  372. hr_tmp = AddTicketToExpirationList( ticketExpireTime, obj );
  373. if( FAILED(hr_tmp) ) {
  374. TRC_ERR((TB, L"AddTicketToExpirationList failed : %08X", hr));
  375. ASSERT(FALSE);
  376. }
  377. CLEANUPANDEXIT:
  378. //
  379. // Delete the object on error.
  380. //
  381. if (!SUCCEEDED(hr)) {
  382. if (obj != NULL) delete obj;
  383. }
  384. DC_END_FN();
  385. return hr;
  386. }
  387. /*++
  388. Routine Description:
  389. Open an existing RDS session
  390. This call should ALWAYS be made in order to connect to the client
  391. Once this is called and connection is complete, the caller
  392. MUST call DisConnect() to make another connection to the client
  393. Otherwise, the connection does not happen
  394. Arguments:
  395. Return Value:
  396. S_OK on success. Otherwise, an error code is returned.
  397. --*/
  398. STDMETHODIMP
  399. CRemoteDesktopServerHost::OpenRemoteDesktopSession(
  400. BSTR parms,
  401. BSTR userSID,
  402. ISAFRemoteDesktopSession **session
  403. )
  404. {
  405. DC_BEGIN_FN("CRemoteDesktopServerHost::OpenRemoteDesktopSession");
  406. CComObject<CRemoteDesktopSession> *obj = NULL;
  407. CComBSTR hostname;
  408. CComBSTR tmp("");
  409. HRESULT hr = S_OK;
  410. HRESULT hr_tmp;
  411. SessionMap::iterator iter;
  412. CComBSTR parmsHelpSessionId;
  413. DWORD protocolType;
  414. PSESSIONMAPENTRY mapEntry;
  415. PSID psid;
  416. DWORD ticketExpireTime;
  417. VARIANT_BOOL bUserIsOwner;
  418. //
  419. // Get the local system SID.
  420. //
  421. psid = GetLocalSystemSID();
  422. if (psid == NULL) {
  423. hr = HRESULT_FROM_WIN32(GetLastError());
  424. goto CLEANUPANDEXIT;
  425. }
  426. //
  427. // Need to impersonate the caller in order to determine if it is
  428. // running in SYSTEM context.
  429. //
  430. hr = CoImpersonateClient();
  431. if (hr != S_OK) {
  432. TRC_ERR((TB, L"CoImpersonateClient: %08X", hr));
  433. goto CLEANUPANDEXIT;
  434. }
  435. //
  436. // For Whistler, instances of a Remote Desktop Session are only
  437. // "openable" from SYSTEM context, for security reasons.
  438. //
  439. #ifndef DISABLESECURITYCHECKS
  440. if (!IsCallerSystem(psid)) {
  441. TRC_ERR((TB, L"Caller is not SYSTEM."));
  442. ASSERT(FALSE);
  443. CoRevertToSelf();
  444. hr = HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED );
  445. goto CLEANUPANDEXIT;
  446. }
  447. #endif
  448. hr = CoRevertToSelf();
  449. if (hr != S_OK) {
  450. TRC_ERR((TB, L"CoRevertToSelf: %08X", hr));
  451. goto CLEANUPANDEXIT;
  452. }
  453. //
  454. // Parse out the help session ID.
  455. // TODO: Need to modify this so some of the parms are
  456. // optional.
  457. //
  458. DWORD dwVersion;
  459. DWORD result = ParseConnectParmsString(
  460. parms, &dwVersion, &protocolType, hostname, tmp, tmp,
  461. parmsHelpSessionId, tmp, tmp, tmp
  462. );
  463. if (result != ERROR_SUCCESS) {
  464. hr = HRESULT_FROM_WIN32(result);
  465. goto CLEANUPANDEXIT;
  466. }
  467. //
  468. // If we already have the session open then just return a
  469. // reference.
  470. //
  471. iter = m_SessionMap.find(parmsHelpSessionId);
  472. // refer to DeleteRemoteDesktopSession() why we keep this entry
  473. // in m_SessionMap and check for (*iter).second
  474. if (iter != m_SessionMap.end()) {
  475. mapEntry = (*iter).second;
  476. if( mapEntry == NULL || mapEntry->ticketExpireTime <= time(NULL) ) {
  477. // ticket already expired or about to expire, return
  478. // error and let expiration to take care of ticket.
  479. hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
  480. goto CLEANUPANDEXIT;
  481. }
  482. if( FALSE == mapEntry->obj->CheckAccessRight(userSID) ) {
  483. TRC_ERR((TB, L"CheckAccessRight return FALSE"));
  484. ASSERT( FALSE );
  485. hr = HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED );
  486. goto CLEANUPANDEXIT;
  487. }
  488. hr = mapEntry->obj->QueryInterface(
  489. IID_ISAFRemoteDesktopSession,
  490. (void**)session
  491. );
  492. //
  493. //Start listening if we succeeded
  494. //
  495. if (SUCCEEDED(hr)) {
  496. hr = mapEntry->obj->StartListening();
  497. //
  498. //release the interface pointer if we didn't succeed
  499. //
  500. if (!SUCCEEDED(hr)) {
  501. (*session)->Release();
  502. *session = NULL;
  503. }
  504. }
  505. goto CLEANUPANDEXIT;
  506. }
  507. //
  508. // Instantiate the desktop server. Currently, we only support
  509. // TSRDP.
  510. //
  511. obj = new CComObject<CTSRDPRemoteDesktopSession>();
  512. if (obj != NULL) {
  513. //
  514. // ATL would normally take care of this for us.
  515. //
  516. obj->InternalFinalConstructAddRef();
  517. hr = obj->FinalConstruct();
  518. obj->InternalFinalConstructRelease();
  519. }
  520. else {
  521. TRC_ERR((TB, L"Can't instantiate CTSRDPRemoteDesktopServer"));
  522. hr = E_OUTOFMEMORY;
  523. goto CLEANUPANDEXIT;
  524. }
  525. //
  526. // Initialize the object.
  527. //
  528. // The desktop sharing parameter (NO_DESKTOP_SHARING) is ignored for
  529. // an existing session.
  530. // bEnableCallback and timeout parameter is ignored for existing session
  531. //
  532. hr = obj->Initialize(parms, this, NO_DESKTOP_SHARING, TRUE, 0, CComBSTR(L""), -1, userSID);
  533. if (!SUCCEEDED(hr)) {
  534. goto CLEANUPANDEXIT;
  535. }
  536. hr = obj->StartListening();
  537. if (!SUCCEEDED(hr)) {
  538. goto CLEANUPANDEXIT;
  539. }
  540. hr = obj->UseHostName( hostname );
  541. if( FAILED(hr) ) {
  542. goto CLEANUPANDEXIT;
  543. }
  544. hr = obj->get_ExpireTime( &ticketExpireTime );
  545. if( FAILED(hr) ) {
  546. goto CLEANUPANDEXIT;
  547. }
  548. if( ticketExpireTime < (DWORD)time(NULL) ) {
  549. // ticket already expired, no need to continue,
  550. // overactive assert here is just to check we
  551. // should never come to this.
  552. ASSERT(FALSE);
  553. goto CLEANUPANDEXIT;
  554. }
  555. //
  556. // Add it to the session map.
  557. //
  558. mapEntry = new SESSIONMAPENTRY();
  559. if (mapEntry == NULL) {
  560. goto CLEANUPANDEXIT;
  561. }
  562. mapEntry->obj = obj;
  563. mapEntry->ticketExpireTime = ticketExpireTime;
  564. ASSERT( obj->GetHelpSessionID() == parmsHelpSessionId );
  565. try {
  566. m_SessionMap.insert(
  567. SessionMap::value_type(obj->GetHelpSessionID(), mapEntry)
  568. );
  569. }
  570. catch(CRemoteDesktopException x) {
  571. hr = HRESULT_FROM_WIN32(x.m_ErrorCode);
  572. delete mapEntry;
  573. goto CLEANUPANDEXIT;
  574. }
  575. //
  576. // Get the ISAFRemoteDesktopSession interface pointer.
  577. //
  578. hr = obj->QueryInterface(
  579. IID_ISAFRemoteDesktopSession,
  580. (void**)session
  581. );
  582. if (!SUCCEEDED(hr)) {
  583. //
  584. // TODO : remove from m_SessionMap, this should never
  585. // failed but just in case, then we would have orphan object
  586. // in the m_SessionMap which might cause AV when we loop for
  587. // next ticket to expire
  588. //
  589. TRC_ERR((TB, L"m_RemoteDesktopSession->QueryInterface: %08X", hr));
  590. goto CLEANUPANDEXIT;
  591. }
  592. //
  593. // Add a reference to the object and to ourself so we can both
  594. // stick around, even if the app goes away. The app needs to explicitly
  595. // call CloseRemoteDesktopSession for the object to go away.
  596. //
  597. obj->AddRef();
  598. long count;
  599. count = this->AddRef();
  600. TRC_NRM((TB, TEXT("OpenRemoteDesktopSession AddRef SrvHost count: %08X %08X"), count, m_SessionMap.size()));
  601. //
  602. // Added ticket in expiration monitor list, if anything goes wrong,
  603. // we still can function, just no expiration running until next
  604. // CreateXXX, OpenXXX or CloseXXX call.
  605. //
  606. hr_tmp = AddTicketToExpirationList( ticketExpireTime, obj );
  607. if( FAILED(hr_tmp) ) {
  608. TRC_ERR((TB, L"AddTicketToExpirationList failed : %08X", hr));
  609. ASSERT(FALSE);
  610. }
  611. CLEANUPANDEXIT:
  612. //
  613. // Delete the object on error.
  614. //
  615. if (!SUCCEEDED(hr)) {
  616. if (obj != NULL) delete obj;
  617. }
  618. DC_END_FN();
  619. return hr;
  620. }
  621. STDMETHODIMP
  622. CRemoteDesktopServerHost::CloseRemoteDesktopSession(
  623. ISAFRemoteDesktopSession *session
  624. )
  625. /*++
  626. Routine Description:
  627. Close an existing RDS session
  628. Arguments:
  629. Return Value:
  630. S_OK on success. Otherwise, an error code is returned.
  631. --*/
  632. {
  633. HRESULT hr;
  634. DC_BEGIN_FN("CRemoteDesktopServerHost::CloseRemoteDesktopSession");
  635. hr = DeleteRemoteDesktopSession(session);
  636. //
  637. // Can't call ExpirateTicketAndSetupNextExpiration() because
  638. // ExpirateTicketAndSetupNextExpiration() might have outgoing
  639. // COM call and COM will pump message causing COM re-entry.
  640. // also for performance reason, we don't want to post
  641. // more than on WM_TICKETEXPIRED message until we have it
  642. // processed
  643. //
  644. if( !GetExpireMsgPosted() ) {
  645. //
  646. // We need extra ref. counter here since we still reference
  647. // CRemoteDesktopServerHost object in expiration,
  648. //
  649. long count;
  650. count = this->AddRef();
  651. TRC_NRM((TB, TEXT("CloseRemoteDesktopSession AddRef SrvHost count: %08X"), count));
  652. SetExpireMsgPosted(TRUE);
  653. PostThreadMessage(
  654. _Module.dwThreadID,
  655. WM_TICKETEXPIRED,
  656. (WPARAM) 0,
  657. (LPARAM) this
  658. );
  659. }
  660. return hr;
  661. }
  662. HRESULT
  663. CRemoteDesktopServerHost::DeleteRemoteDesktopSession(
  664. ISAFRemoteDesktopSession *session
  665. )
  666. /*++
  667. Routine Description:
  668. Delete an existing RDS session
  669. Arguments:
  670. Return Value:
  671. S_OK on success. Otherwise, an error code is returned.
  672. --*/
  673. {
  674. DC_BEGIN_FN("CRemoteDesktopServerHost::DeleteRemoteDesktopSession");
  675. HRESULT hr;
  676. HRESULT hr_tmp;
  677. CComBSTR parmsHelpSessionId;
  678. SessionMap::iterator iter;
  679. long count;
  680. //
  681. // Get the connection parameters.
  682. //
  683. hr = session->get_HelpSessionId(&parmsHelpSessionId);
  684. if (!SUCCEEDED(hr)) {
  685. TRC_ERR((TB, TEXT("get_HelpSessionId: %08X"), hr));
  686. //
  687. // This is really bad, we will leave object hanging
  688. // around in cache, not much can be done since our map
  689. // entry is indexed on HelpSession ID
  690. //
  691. ASSERT(FALSE);
  692. goto CLEANUPANDEXIT;
  693. }
  694. //
  695. // Delete the entry from the session map.
  696. //
  697. iter = m_SessionMap.find(parmsHelpSessionId);
  698. if (iter != m_SessionMap.end()) {
  699. if( NULL != (*iter).second ) {
  700. delete (*iter).second;
  701. (*iter).second = NULL;
  702. }
  703. else {
  704. // Ticket has been delete by expiration loop.
  705. hr = S_OK;
  706. goto CLEANUPANDEXIT;
  707. }
  708. //
  709. // Two CloseRemoteDesktopSession() re-enter calls while we are in expire loop
  710. // causes AV. In expire loop, we go thru all entries in m_SessionMap, if
  711. // entry is expired, we invoke DeleteRemoteDesktopSession() to delete entry
  712. // in m_SessionMap but DeleteRemoteDesktopSession() makes outgoing COM call
  713. // which permit incoming COM call so if during outgoing COM call, two consecutive
  714. // CloseRemoteDesktopSession() re-enter calls, we will erase the iterator and
  715. // causes AV. Need to keep this entry for expire loop to erase.
  716. //
  717. // m_SessionMap.erase(iter);
  718. }
  719. else {
  720. //
  721. // It's possible that we expire ticket
  722. // from m_SessionMap but client still holding object.
  723. //
  724. //
  725. // Cached entry has been deleted via expire loop which already
  726. // release the associated AddRef() we put on session object and
  727. // host object, also ticket has been deleted from session,
  728. // so simply return S_OK
  729. //
  730. hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
  731. goto CLEANUPANDEXIT;
  732. }
  733. //
  734. // Remove our reference to the session object. This way it can
  735. // go away when the application releases it.
  736. //
  737. session->Release();
  738. //
  739. // Remove the reference to ourself that we added when we opened
  740. // the session object.
  741. //
  742. count = this->Release();
  743. TRC_NRM((TB, TEXT("DeleteRemoteDesktopSession Release SrvHost count: %08X"), count));
  744. ASSERT( count >= 0 );
  745. //
  746. // Get the session manager interface, if we don't already have one.
  747. //
  748. //
  749. // Open an instance of the Remote Desktop Help Session Manager service.
  750. //
  751. if (m_HelpSessionManager == NULL) {
  752. hr = m_HelpSessionManager.CoCreateInstance(CLSID_RemoteDesktopHelpSessionMgr, NULL, CLSCTX_LOCAL_SERVER | CLSCTX_DISABLE_AAA);
  753. if (!SUCCEEDED(hr)) {
  754. TRC_ERR((TB, TEXT("Can't create help session manager: %08X"), hr));
  755. goto CLEANUPANDEXIT;
  756. }
  757. //
  758. // Set the security level to impersonate. This is required by
  759. // the session manager.
  760. //
  761. hr = CoSetProxyBlanket(
  762. (IUnknown *)m_HelpSessionManager,
  763. RPC_C_AUTHN_DEFAULT,
  764. RPC_C_AUTHZ_DEFAULT,
  765. NULL,
  766. RPC_C_AUTHN_LEVEL_DEFAULT,
  767. RPC_C_IMP_LEVEL_IDENTIFY,
  768. NULL,
  769. EOAC_NONE
  770. );
  771. if (!SUCCEEDED(hr)) {
  772. TRC_ERR((TB, TEXT("CoSetProxyBlanket: %08X"), hr));
  773. goto CLEANUPANDEXIT;
  774. }
  775. }
  776. //
  777. // Remove the help session with the session manager.
  778. //
  779. hr = m_HelpSessionManager->DeleteHelpSession(parmsHelpSessionId);
  780. if (!SUCCEEDED(hr)) {
  781. if( HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr ) {
  782. // HelpSession might have been expired by sessmgr, reset
  783. // error code here.
  784. hr = S_OK;
  785. }
  786. else {
  787. TRC_ERR((TB, L"DeleteHelpSession: %08X", hr));
  788. goto CLEANUPANDEXIT;
  789. }
  790. }
  791. CLEANUPANDEXIT:
  792. DC_END_FN();
  793. return hr;
  794. }
  795. STDMETHODIMP
  796. CRemoteDesktopServerHost::ConnectToExpert(
  797. /*[in]*/ BSTR connectParmToExpert,
  798. /*[in]*/ LONG timeout,
  799. /*[out, retval]*/ LONG* pSafErrCode
  800. )
  801. /*++
  802. Description:
  803. Given connection parameters to expert machine, routine invoke TermSrv winsta API to
  804. initiate connection from TS server to TS client ActiveX control on the expert side.
  805. Parameters:
  806. connectParmToExpert : connection parameter to connect to expert machine.
  807. timeout : Connection timeout, this timeout is per ip address listed in connection parameter
  808. not total connection timeout for the routine.
  809. pSafErrCode : Pointer to LONG to receive detail error code.
  810. Returns:
  811. S_OK or E_FAIL
  812. --*/
  813. {
  814. HRESULT hr = S_OK;
  815. ServerAddress expertAddress;
  816. ServerAddressList expertAddressList;
  817. LONG SafErrCode = SAFERROR_NOERROR;
  818. TDI_ADDRESS_IP expertTDIAddress;
  819. ULONG netaddr;
  820. WSADATA wsaData;
  821. PSID psid;
  822. DC_BEGIN_FN("CRemoteDesktopServerHost::ConnectToExpert");
  823. //
  824. // Get the local system SID.
  825. //
  826. psid = GetLocalSystemSID();
  827. if (psid == NULL) {
  828. hr = HRESULT_FROM_WIN32(GetLastError());
  829. goto CLEANUPANDEXIT;
  830. }
  831. //
  832. // Need to impersonate the caller in order to determine if it is
  833. // running in SYSTEM context.
  834. //
  835. hr = CoImpersonateClient();
  836. if (hr != S_OK) {
  837. TRC_ERR((TB, L"CoImpersonateClient: %08X", hr));
  838. goto CLEANUPANDEXIT;
  839. }
  840. //
  841. // For Whistler, instances of a Remote Desktop Session are only
  842. // "openable" from SYSTEM context, for security reasons.
  843. //
  844. #ifndef DISABLESECURITYCHECKS
  845. if (!IsCallerSystem(psid)) {
  846. TRC_ERR((TB, L"Caller is not SYSTEM."));
  847. ASSERT(FALSE);
  848. CoRevertToSelf();
  849. hr = HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED );
  850. goto CLEANUPANDEXIT;
  851. }
  852. #endif
  853. hr = CoRevertToSelf();
  854. if (hr != S_OK) {
  855. TRC_ERR((TB, L"CoRevertToSelf: %08X", hr));
  856. goto CLEANUPANDEXIT;
  857. }
  858. //
  859. // Parse address list in connection parameter.
  860. //
  861. hr = ParseAddressList( connectParmToExpert, expertAddressList );
  862. if( FAILED(hr) ) {
  863. TRC_ERR((TB, TEXT("ParseAddressList: %08X"), hr));
  864. hr = E_INVALIDARG;
  865. SafErrCode = SAFERROR_INVALIDPARAMETERSTRING;
  866. goto CLEANUPANDEXIT;
  867. }
  868. if( 0 == expertAddressList.size() ) {
  869. TRC_ERR((TB, L"Invalid connection address list"));
  870. SafErrCode = SAFERROR_INVALIDPARAMETERSTRING;
  871. hr = E_INVALIDARG;
  872. goto CLEANUPANDEXIT;
  873. }
  874. //
  875. // Loop thru all address in parm and try connection one
  876. // at a time, bail out if system is shutting down or
  877. // some critical error
  878. //
  879. while( expertAddressList.size() > 0 ) {
  880. expertAddress = expertAddressList.front();
  881. expertAddressList.pop_front();
  882. //
  883. // Invalid connect parameters, we must have port number at least.
  884. //
  885. if( 0 == expertAddress.portNumber ||
  886. 0 == lstrlen(expertAddress.ServerName) ) {
  887. TRC_ERR((TB, L"Invalid address/port %s %d", expertAddress.ServerName, expertAddress.portNumber));
  888. SafErrCode = SAFERROR_INVALIDPARAMETERSTRING;
  889. continue;
  890. }
  891. hr = TranslateStringAddress( expertAddress.ServerName, &netaddr );
  892. if( FAILED(hr) ) {
  893. TRC_ERR((TB, L"TranslateStringAddress() on %s failed with 0x%08x", expertAddress.ServerName, hr));
  894. SafErrCode = SAFERROR_INVALIDPARAMETERSTRING;
  895. continue;
  896. }
  897. ZeroMemory(&expertTDIAddress, TDI_ADDRESS_LENGTH_IP);
  898. expertTDIAddress.in_addr = netaddr;
  899. expertTDIAddress.sin_port = htons(expertAddress.portNumber);
  900. if( FALSE == WinStationConnectCallback(
  901. SERVERNAME_CURRENT,
  902. timeout,
  903. TDI_ADDRESS_TYPE_IP,
  904. (PBYTE)&expertTDIAddress,
  905. TDI_ADDRESS_LENGTH_IP
  906. ) ) {
  907. //
  908. // TransferConnectionToIdleWinstation() in TermSrv might just return -1
  909. // few of them we need to bail out.
  910. DWORD dwStatus;
  911. dwStatus = GetLastError();
  912. if( ERROR_SHUTDOWN_IN_PROGRESS == dwStatus ) {
  913. // system or termsrv is shuting down.
  914. hr = HRESULT_FROM_WIN32( ERROR_SHUTDOWN_IN_PROGRESS );
  915. SafErrCode = SAFERROR_SYSTEMSHUTDOWN;
  916. break;
  917. }
  918. else if( ERROR_ACCESS_DENIED == dwStatus ) {
  919. // security check failed
  920. hr = HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED );
  921. SafErrCode = SAFERROR_BYSERVER;
  922. ASSERT(FALSE);
  923. break;
  924. }
  925. else if( ERROR_INVALID_PARAMETER == dwStatus ) {
  926. // internal error in rdshost.
  927. hr = HRESULT_FROM_WIN32( ERROR_INTERNAL_ERROR );
  928. SafErrCode = SAFERROR_INTERNALERROR;
  929. ASSERT(FALSE);
  930. break;
  931. }
  932. SafErrCode = SAFERROR_WINSOCK_FAILED;
  933. }
  934. else {
  935. //
  936. // successful connection
  937. //
  938. SafErrCode = SAFERROR_NOERROR;
  939. break;
  940. }
  941. //
  942. // Try next connection.
  943. //
  944. }
  945. CLEANUPANDEXIT:
  946. *pSafErrCode = SafErrCode;
  947. DC_END_FN();
  948. return hr;
  949. }
  950. HRESULT
  951. CRemoteDesktopServerHost::TranslateStringAddress(
  952. IN LPTSTR pszAddress,
  953. OUT ULONG* pNetAddr
  954. )
  955. /*++
  956. Routine Description:
  957. Translate IP Address or machine name to network address.
  958. Parameters:
  959. pszAddress : Pointer to IP address or machine name.
  960. pNetAddr : Point to ULONG to receive address in IPV4.
  961. Returns:
  962. S_OK or error code
  963. --*/
  964. {
  965. HRESULT hr = S_OK;
  966. unsigned long addr;
  967. LPSTR pszAnsiAddress = NULL;
  968. DWORD dwAddressBufSize;
  969. DWORD dwStatus;
  970. DC_BEGIN_FN("CRemoteDesktopServerHost::TranslateStringAddress");
  971. dwAddressBufSize = lstrlen(pszAddress) + 1;
  972. pszAnsiAddress = (LPSTR)LocalAlloc(LPTR, dwAddressBufSize); // converting from WCHAR to CHAR.
  973. if( NULL == pszAnsiAddress ) {
  974. hr = E_OUTOFMEMORY;
  975. goto CLEANUPANDEXIT;
  976. }
  977. //
  978. // Convert wide char to ANSI string
  979. //
  980. dwStatus = WideCharToMultiByte(
  981. GetACP(),
  982. 0,
  983. pszAddress,
  984. -1,
  985. pszAnsiAddress,
  986. dwAddressBufSize,
  987. NULL,
  988. NULL
  989. );
  990. if( 0 == dwStatus ) {
  991. dwStatus = GetLastError();
  992. hr = HRESULT_FROM_WIN32(dwStatus);
  993. TRC_ERR((TB, L"WideCharToMultiByte() failed with %d", dwStatus));
  994. goto CLEANUPANDEXIT;
  995. }
  996. addr = inet_addr( pszAnsiAddress );
  997. if( INADDR_NONE == addr ) {
  998. struct hostent* pHostEnt = NULL;
  999. pHostEnt = gethostbyname( pszAnsiAddress );
  1000. if( NULL != pHostEnt ) {
  1001. addr = ((struct sockaddr_in *)(pHostEnt->h_addr))->sin_addr.S_un.S_addr;
  1002. }
  1003. }
  1004. if( INADDR_NONE == addr ) {
  1005. dwStatus = GetLastError();
  1006. hr = HRESULT_FROM_WIN32(dwStatus);
  1007. TRC_ERR((TB, L"Can't translate address %w", pszAddress));
  1008. goto CLEANUPANDEXIT;
  1009. }
  1010. *pNetAddr = addr;
  1011. CLEANUPANDEXIT:
  1012. if( NULL != pszAnsiAddress ) {
  1013. LocalFree(pszAnsiAddress);
  1014. }
  1015. DC_END_FN();
  1016. return hr;
  1017. }
  1018. VOID
  1019. CRemoteDesktopServerHost::TicketExpirationProc(
  1020. IN LPVOID lpArg,
  1021. IN BOOLEAN TimerOrWaitFired
  1022. )
  1023. /*++
  1024. Routine Description:
  1025. Ticket expiration procedure, this routine is invoked by threadpool, refer
  1026. to RegisterWaitForSingleObject() and WAITORTIMERCALLBACK for detail.
  1027. Parameters:
  1028. lpArg : Pointer to user defined data, expecting CRemoteDesktopServerHost*.
  1029. TimerOrWaitFired : Refer to WAITORTIMERCALLBACK.
  1030. Returns:
  1031. None.
  1032. --*/
  1033. {
  1034. DC_BEGIN_FN("CRemoteDesktopServerHost::TicketExpirationProc");
  1035. if( NULL == lpArg ) {
  1036. ASSERT( NULL != lpArg );
  1037. goto CLEANUPANDEXIT;
  1038. }
  1039. CRemoteDesktopServerHost *pServerHostObj = (CRemoteDesktopServerHost *)lpArg;
  1040. if( TimerOrWaitFired ) {
  1041. if( !pServerHostObj->GetExpireMsgPosted() ) {
  1042. // Wait has timed out, Post main thread an message to expire ticket
  1043. pServerHostObj->SetExpireMsgPosted(TRUE);
  1044. PostThreadMessage(
  1045. _Module.dwThreadID,
  1046. WM_TICKETEXPIRED,
  1047. (WPARAM) 0,
  1048. (LPARAM) pServerHostObj
  1049. );
  1050. }
  1051. else {
  1052. long count;
  1053. count = pServerHostObj->Release();
  1054. TRC_NRM((TB, TEXT("TicketExpirationProc Release SrvHost count: %08X"), count));
  1055. ASSERT( count >= 0 );
  1056. }
  1057. }
  1058. else {
  1059. // Do nothing, our manual event never signal.
  1060. }
  1061. CLEANUPANDEXIT:
  1062. DC_END_FN();
  1063. return;
  1064. }
  1065. HRESULT
  1066. CRemoteDesktopServerHost::AddTicketToExpirationList(
  1067. DWORD ticketExpireTime,
  1068. CComObject<CRemoteDesktopSession> *pTicketObj
  1069. )
  1070. /*++
  1071. Routine Description:
  1072. Routine to sets up timer for newly created/opened ticket.
  1073. Parameters:
  1074. ticketExpireTime : Ticket expiration time, expecting time_t value.
  1075. pTicketObj : Pointer to ticket object to be expired.
  1076. Returns:
  1077. S_OK or error code.
  1078. --*/
  1079. {
  1080. HRESULT hr = S_OK;
  1081. BOOL success;
  1082. DWORD status;
  1083. DWORD currentTime;
  1084. DWORD waitTime;
  1085. long count;
  1086. DC_BEGIN_FN("CRemoteDesktopServerHost::AddTicketToExpirationList");
  1087. //
  1088. // Invalid parameters
  1089. //
  1090. if( NULL == pTicketObj ) {
  1091. hr = E_INVALIDARG;
  1092. ASSERT( FALSE );
  1093. goto CLEANUPANDEXIT;
  1094. }
  1095. //
  1096. // Notice in the case of ticket already expire, we immediately signal
  1097. //
  1098. // Created at FinalConstruct() and deleted at destructor time,
  1099. // so can't be NULL
  1100. ASSERT( NULL != m_hTicketExpiration );
  1101. //
  1102. // Check to see if there is already a ticket waiting to be expired,
  1103. // if so, Check ticket expire time and reset timer if necessary.
  1104. //
  1105. if( m_ToBeExpireTicketExpirateTime > ticketExpireTime ) {
  1106. //
  1107. // Cancel the thread pool wait if there is one already in progress.
  1108. //
  1109. if( m_hTicketExpirationWaitObject ) {
  1110. success = UnregisterWait( m_hTicketExpirationWaitObject );
  1111. if( FALSE == success ) {
  1112. TRC_ERR((TB, TEXT("UnRegisterWait() failed: %08X"), GetLastError()));
  1113. // ASSERT( TRUE == success );
  1114. // Leak handle but not critical error, could return ERROR_IO_PENDING.
  1115. }
  1116. m_hTicketExpirationWaitObject = NULL;
  1117. }
  1118. if( m_ToBeExpireTicketExpirateTime == INFINITE_TICKET_EXPIRATION ) {
  1119. //
  1120. // put an extra ref. counter on srv. host object so we don't
  1121. // accidently delete it, this ref. count will be decrement on the
  1122. // corresponding ExpirateTicketAndSetupNextExpiration() call.
  1123. //
  1124. count = this->AddRef();
  1125. TRC_NRM((TB, TEXT("AddTicketToExpirationList AddRef SrvHost count: %08X"), count));
  1126. }
  1127. //
  1128. // Setup new ticket to be expired.
  1129. //
  1130. InterlockedExchange(
  1131. (LONG *)&m_ToBeExpireTicketExpirateTime,
  1132. ticketExpireTime
  1133. );
  1134. currentTime = (DWORD)time(NULL);
  1135. if( ticketExpireTime < currentTime ) {
  1136. // if ticket already expire, immediately signal TicketExpirationProc
  1137. // to expire ticket.
  1138. waitTime = 0;
  1139. }
  1140. else {
  1141. waitTime = (ticketExpireTime - currentTime) * 1000;
  1142. }
  1143. TRC_NRM((TB, TEXT("Expiration Wait Time : %d"), waitTime));
  1144. // Setup threadpool wait, there might not be any more object to be expired so
  1145. // it is executed only once.
  1146. success = RegisterWaitForSingleObject(
  1147. &m_hTicketExpirationWaitObject,
  1148. m_hTicketExpiration,
  1149. (WAITORTIMERCALLBACK) TicketExpirationProc,
  1150. (PVOID)this,
  1151. waitTime,
  1152. WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE
  1153. );
  1154. if( FALSE == success ) {
  1155. status = GetLastError();
  1156. hr = HRESULT_FROM_WIN32(status);
  1157. TRC_ERR((TB, TEXT("RegisterWaitForSingleObject() failed: %08X"), hr));
  1158. ASSERT(FALSE);
  1159. count = this->Release();
  1160. TRC_NRM((TB, TEXT("AddTicketToExpirationList Release SrvHost count: %08X"), count));
  1161. ASSERT( count >= 0 );
  1162. // TODO : what can we do here, no signal (expiration) until next close or
  1163. // create.
  1164. }
  1165. }
  1166. CLEANUPANDEXIT:
  1167. DC_END_FN();
  1168. return hr;
  1169. }
  1170. HRESULT
  1171. CRemoteDesktopServerHost::ExpirateTicketAndSetupNextExpiration()
  1172. /*++
  1173. Routine Description:
  1174. Routine to process all expired ticket and sets up timer for next
  1175. ticket expiration. Routine loop thru m_SessionMap cache so entry
  1176. must be removed first if setting up timer for next run.
  1177. Parameters:
  1178. None.
  1179. Returns:
  1180. S_OK or error code.
  1181. --*/
  1182. {
  1183. DC_BEGIN_FN("CRemoteDesktopServerHost::ExpirateTicketAndSetupNextExpiration");
  1184. HRESULT hr = S_OK;
  1185. SessionMap::iterator iter;
  1186. SessionMap::iterator iter_delete;
  1187. DWORD nextExpireTicketTime = INFINITE_TICKET_EXPIRATION;
  1188. CComObject<CRemoteDesktopSession> *pNextTicketObj = NULL;
  1189. CComObject<CRemoteDesktopSession> *pDeleteTicketObj = NULL;
  1190. //
  1191. // processing ticket expiration, set next ticket expiration
  1192. // time to infinite, this also prevent missing ticket,
  1193. // for example, adding/opening new ticket while we are in the
  1194. // middle of expiring ticket but new ticket is added at the
  1195. // beginning of m_SessionMap.
  1196. //
  1197. InterlockedExchange(
  1198. (LONG *)&m_ToBeExpireTicketExpirateTime,
  1199. INFINITE_TICKET_EXPIRATION
  1200. );
  1201. // we are deleting next ticket to be expired, loop m_SessionMap
  1202. // to find next candidate.
  1203. TRC_NRM((TB, TEXT("ExpirateTicketAndSetupNextExpiration Begin Loop: %08X"), m_SessionMap.size()));
  1204. iter = m_SessionMap.begin();
  1205. while( iter != m_SessionMap.end() ) {
  1206. if( NULL == (*iter).second ) {
  1207. // this entry has been deleted via DeleteRemoteDesktopSession.
  1208. iter_delete = iter;
  1209. iter++;
  1210. m_SessionMap.erase(iter_delete);
  1211. }
  1212. else if( (*iter).second->ticketExpireTime < (DWORD) time(NULL) ) {
  1213. //
  1214. // close ticket that is already expire.
  1215. //
  1216. pDeleteTicketObj = (*iter).second->obj;
  1217. ASSERT( pDeleteTicketObj != NULL );
  1218. // DeleteRemoteDesktopSession() will delete iterator from m_SessionMap
  1219. // which makes iter invalid so we advance pointer first before
  1220. // calling DeleteRemoteDesktopSession().
  1221. iter_delete = iter;
  1222. iter++;
  1223. DeleteRemoteDesktopSession(pDeleteTicketObj);
  1224. m_SessionMap.erase(iter_delete);
  1225. }
  1226. else {
  1227. if( (*iter).second->ticketExpireTime < nextExpireTicketTime ) {
  1228. pNextTicketObj = (*iter).second->obj;
  1229. ASSERT( pNextTicketObj != NULL );
  1230. nextExpireTicketTime = (*iter).second->ticketExpireTime;
  1231. }
  1232. iter++;
  1233. }
  1234. }
  1235. TRC_NRM((TB, TEXT("ExpirateTicketAndSetupNextExpiration End Loop: %08X"), m_SessionMap.size()));
  1236. // ready to process next expiration.
  1237. SetExpireMsgPosted(FALSE);
  1238. if( pNextTicketObj != NULL ) {
  1239. hr = AddTicketToExpirationList( nextExpireTicketTime, pNextTicketObj );
  1240. if( FAILED(hr) ) {
  1241. TRC_ERR((TB, TEXT("AddTicketToExpirationList() failed: %08X"), hr));
  1242. }
  1243. }
  1244. //
  1245. // Release the extra ref. counter to prevent 'this' from been
  1246. // deleted at AddTicketToExpirationList() or CloseRemoteDesktopSession().
  1247. //
  1248. long count;
  1249. count = this->Release();
  1250. TRC_NRM((TB, TEXT("ExpirateTicketAndSetupNextExpiration Release SrvHost count: %08X %08X"), count, m_SessionMap.size()));
  1251. ASSERT( count >= 0 );
  1252. DC_END_FN();
  1253. return hr;
  1254. }