Source code of Windows XP (NT5)
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.

2331 lines
60 KiB

  1. /*++
  2. Copyright (c) 1999-2000 Microsoft Corporation
  3. Module Name:
  4. rdsaddin.cpp
  5. Abstract:
  6. The TSRDP Assistant Session VC Add-In is an executable that is
  7. loaded in the session that is created when the TSRDP client plug-in
  8. first logs in to the server machine. It acts, primarily, as a
  9. proxy between the client VC interface and the Remote Desktop Host
  10. COM Object. Channel data is routed from the TSRDP Assistant Session
  11. VC Add-In to the Remote Desktop Host COM Object using a named pipe
  12. that is created by the Remote Desktop Host COM Object when it enters
  13. "listen" mode.
  14. In addition to its duties as a proxy, the Add-In also manages a control
  15. channel between the client and the server. This control channel is
  16. used by the client-side to direct the server side to initiate remote
  17. control of the end user's TS session.
  18. TODO: We should make the pipe IO synchronous since we now have two
  19. IO threads.
  20. Author:
  21. Tad Brockway 02/00
  22. Revision History:
  23. --*/
  24. #ifdef TRC_FILE
  25. #undef TRC_FILE
  26. #endif
  27. #define TRC_FILE "_sesa"
  28. #include <windows.h>
  29. #include <process.h>
  30. #include <RemoteDesktop.h>
  31. #include <RemoteDesktopDBG.h>
  32. #include <RemoteDesktopChannels.h>
  33. #include <TSRDPRemoteDesktop.h>
  34. #include <wtblobj.h>
  35. #include <wtsapi32.h>
  36. #include <sessmgr.h>
  37. #include <winsta.h>
  38. #include <atlbase.h>
  39. #include <RemoteDesktopUtils.h>
  40. #include <sessmgr_i.c>
  41. #include <pchannel.h>
  42. #include <RDCHost.h>
  43. #include <regapi.h>
  44. ///////////////////////////////////////////////////////
  45. //
  46. // Defines
  47. //
  48. #define CLIENTPIPE_CONNECTTIMEOUT (20 * 1000) // 20 seconds.
  49. #define VCBUFFER_RESIZE_DELTA CHANNEL_CHUNK_LENGTH
  50. #define RDS_CHECKCONN_TIMEOUT (30 * 1000) //millisec. default value to ping is 30 seconds
  51. #define RDC_CONNCHECK_ENTRY L"ConnectionCheck"
  52. #define THREADSHUTDOWN_WAITTIMEOUT 30 * 1000
  53. ///////////////////////////////////////////////////////
  54. //
  55. // Typedefs
  56. //
  57. typedef struct _IOBuffer {
  58. PREMOTEDESKTOP_CHANNELBUFHEADER buffer;
  59. DWORD bufSize;
  60. DWORD offset;
  61. } IOBUFFER;
  62. ///////////////////////////////////////////////////////
  63. //
  64. // Internal Prototypes
  65. //
  66. DWORD ReturnResultToClient(
  67. LONG result
  68. );
  69. VOID RemoteControlDesktop(
  70. BSTR parms
  71. );
  72. BOOL ClientVersionCompatible(
  73. DWORD dwMajor,
  74. DWORD dwMinor
  75. );
  76. VOID ClientAuthenticate(
  77. BSTR parms,
  78. BSTR blob
  79. );
  80. DWORD ProcessControlChannelRequest(
  81. IOBUFFER &msg
  82. );
  83. DWORD SendMsgToClient(
  84. PREMOTEDESKTOP_CHANNELBUFHEADER msg
  85. );
  86. VOID HandleVCReadComplete(
  87. HANDLE waitableObject,
  88. PVOID clientData
  89. );
  90. DWORD HandleReceivedVCMsg(
  91. IOBUFFER &msg
  92. );
  93. VOID HandleVCClientConnect(
  94. HANDLE waitableObject,
  95. PVOID clientData
  96. );
  97. VOID HandleVCClientDisconnect(
  98. HANDLE waitableObject,
  99. PVOID clientData
  100. );
  101. VOID HandleNamedPipeReadComplete(
  102. OVERLAPPED &incomingPipeOL,
  103. IOBUFFER &incomingPipeBuf
  104. );
  105. VOID HandleReceivedPipeMsg(
  106. IOBUFFER &msg
  107. );
  108. DWORD ConnectVC();
  109. DWORD ConnectClientSessionPipe();
  110. DWORD IssueVCOverlappedRead(
  111. IOBUFFER &msg,
  112. OVERLAPPED &ol
  113. );
  114. DWORD IssueNamedPipeOverlappedRead(
  115. IOBUFFER &msg,
  116. OVERLAPPED &ol
  117. );
  118. void __cdecl
  119. NamedPipeReadThread(
  120. void* ptr
  121. );
  122. VOID WakeUpFunc(
  123. HANDLE waitableObject,
  124. PVOID clientData
  125. );
  126. VOID HandleHelpCenterExit(
  127. HANDLE waitableObject,
  128. PVOID clientData
  129. );
  130. DWORD
  131. SendNullDataToClient(
  132. );
  133. BOOL GetDwordFromRegistry(PDWORD pdwValue);
  134. ///////////////////////////////////////////////////////
  135. //
  136. // Globals to this Module
  137. //
  138. CComBSTR g_bstrCmdLineHelpSessionId;
  139. WTBLOBJMGR g_WaitObjMgr = NULL;
  140. BOOL g_Shutdown = FALSE;
  141. HANDLE g_VCHandle = NULL;
  142. HANDLE g_ProcHandle = NULL;
  143. DWORD g_SessionID = 0;
  144. HANDLE g_ProcToken = NULL;
  145. HANDLE g_WakeUpForegroundThreadEvent = NULL;
  146. DWORD g_PrevTimer = 0;
  147. DWORD g_dwTimeOutInterval = 0;
  148. HANDLE g_ShutdownEvent = NULL;
  149. HANDLE g_RemoteControlDesktopThread = NULL;
  150. HANDLE g_NamedPipeReadThread = NULL;
  151. HANDLE g_NamedPipeWriteEvent = NULL;
  152. //
  153. // VC Globals
  154. //
  155. HANDLE g_ClientIsconnectEvent = NULL;
  156. HANDLE g_VCFileHandle = NULL;
  157. OVERLAPPED g_VCReadOverlapped = { 0, 0, 0, 0, NULL };
  158. BOOL g_ClientConnected = FALSE;
  159. //
  160. // Client Session Information
  161. //
  162. LONG g_ClientSessionID = -1;
  163. HANDLE g_ClientSessionPipe = NULL;
  164. //
  165. // True if the client has been successfully authenticated.
  166. //
  167. BOOL g_ClientAuthenticated = FALSE;
  168. //
  169. // Incoming Virtual Channel Buf.
  170. //
  171. IOBUFFER g_IncomingVCBuf = { NULL, 0, 0 };
  172. //
  173. // Global help session manager object, this need to be
  174. // global so that when process exit, object destructor
  175. // can inform resolver about the disconnect
  176. //
  177. CComPtr<IRemoteDesktopHelpSessionMgr> g_HelpSessionManager;
  178. //
  179. // Help Session Identifier for the Current Client Connection
  180. //
  181. CComBSTR g_HelpSessionID;
  182. //
  183. // Client (expert side) rdchost major version
  184. //
  185. DWORD g_ClientMajor;
  186. DWORD g_ClientMinor;
  187. //
  188. // Handle to Help Center : B2 blocker workaround for BUG:342742
  189. //
  190. HANDLE g_hHelpCenterProcess = NULL;
  191. //------------------------------------------------------------------
  192. BOOL WINAPI
  193. ControlHandler(
  194. IN DWORD dwCtrlType
  195. )
  196. /*++
  197. Abstract:
  198. Parameter:
  199. IN dwCtrlType : control type
  200. Return:
  201. ++*/
  202. {
  203. switch( dwCtrlType )
  204. {
  205. case CTRL_BREAK_EVENT: // use Ctrl+C or Ctrl+Break to simulate
  206. case CTRL_C_EVENT: // SERVICE_CONTROL_STOP in debug mode
  207. case CTRL_CLOSE_EVENT:
  208. case CTRL_LOGOFF_EVENT:
  209. case CTRL_SHUTDOWN_EVENT:
  210. SetEvent( g_ShutdownEvent );
  211. g_Shutdown = TRUE;
  212. return TRUE;
  213. }
  214. return FALSE;
  215. }
  216. DWORD
  217. ReturnResultToClient(
  218. LONG clientResult
  219. )
  220. /*++
  221. Routine Description:
  222. Return a result code to the client in the form of a
  223. REMOTEDESKTOP_RC_CONTROL_CHANNEL channel REMOTEDESKTOP_CTL_RESULT message.
  224. Arguments:
  225. Return Value:
  226. ERROR_SUCCESS on success. Otherwise, an error code is returned.
  227. --*/
  228. {
  229. DC_BEGIN_FN("ReturnResultToClient");
  230. DWORD result;
  231. REMOTEDESKTOP_CTL_RESULT_PACKET msg;
  232. memcpy(msg.packetHeader.channelName, REMOTEDESKTOP_RC_CONTROL_CHANNEL,
  233. sizeof(REMOTEDESKTOP_RC_CONTROL_CHANNEL));
  234. msg.packetHeader.channelBufHeader.channelNameLen = REMOTEDESKTOP_RC_CHANNELNAME_LENGTH;
  235. #ifdef USE_MAGICNO
  236. msg.packetHeader.channelBufHeader.magicNo = CHANNELBUF_MAGICNO;
  237. #endif
  238. msg.packetHeader.channelBufHeader.dataLen = sizeof(REMOTEDESKTOP_CTL_RESULT_PACKET) -
  239. sizeof(REMOTEDESKTOP_CTL_PACKETHEADER);
  240. msg.msgHeader.msgType = REMOTEDESKTOP_CTL_RESULT;
  241. msg.result = clientResult;
  242. result = SendMsgToClient((PREMOTEDESKTOP_CHANNELBUFHEADER )&msg);
  243. DC_END_FN();
  244. return result;
  245. }
  246. void __cdecl
  247. RemoteControlDesktopThread(
  248. void* ptr
  249. )
  250. /*++
  251. Routine Description:
  252. Thread func for Remote Control
  253. Arguments:
  254. Return Value:
  255. This function returns a status back to the Salem client when shadow
  256. terminates. It is only allowed to return error codes that are prefixed by
  257. SAFERROR_SHADOWEND
  258. --*/
  259. {
  260. BSTR parms = (BSTR) ptr;
  261. DC_BEGIN_FN("RemoteControlDesktopThread");
  262. CComPtr<IRemoteDesktopHelpSessionMgr> helpSessionManager;
  263. CComPtr<IRemoteDesktopHelpSession> helpSession;
  264. HRESULT hr;
  265. DWORD result;
  266. LONG errReturnCode = SAFERROR_SHADOWEND_UNKNOWN;
  267. //
  268. // If we have not resolve the right user session ID
  269. //
  270. if( g_ClientSessionID == -1 ) {
  271. TRC_ALT((TB, L"Invalid user session ID %ld",
  272. g_ClientSessionID));
  273. hr = HRESULT_FROM_WIN32(ERROR_INVALID_PASSWORD);
  274. errReturnCode = SAFERROR_SHADOWEND_UNKNOWN;
  275. ASSERT(FALSE);
  276. goto CLEANUPANDEXIT;
  277. }
  278. CoInitialize(NULL);
  279. //
  280. // Create a new instance of helpmgr object to get around threading issue
  281. // in COM
  282. //
  283. hr = helpSessionManager.CoCreateInstance(CLSID_RemoteDesktopHelpSessionMgr);
  284. if (!SUCCEEDED(hr)) {
  285. TRC_ERR((TB, TEXT("Can't create help session manager: %08X"), hr));
  286. // Setup issue
  287. errReturnCode = SAFERROR_SHADOWEND_UNKNOWN;
  288. ASSERT(FALSE);
  289. goto CLEANUPANDEXIT;
  290. }
  291. //
  292. // Set the security level to impersonate. This is required by
  293. // the session manager.
  294. //
  295. hr = CoSetProxyBlanket(
  296. (IUnknown *)helpSessionManager,
  297. RPC_C_AUTHN_DEFAULT,
  298. RPC_C_AUTHZ_DEFAULT,
  299. NULL,
  300. RPC_C_AUTHN_LEVEL_DEFAULT,
  301. RPC_C_IMP_LEVEL_IMPERSONATE,
  302. NULL,
  303. EOAC_NONE
  304. );
  305. if (!SUCCEEDED(hr)) {
  306. TRC_ERR((TB, TEXT("CoSetProxyBlanket: %08X"), hr));
  307. ASSERT(FALSE);
  308. errReturnCode = SAFERROR_SHADOWEND_UNKNOWN;
  309. goto CLEANUPANDEXIT;
  310. }
  311. //
  312. // Retrieve help session object for the incident
  313. //
  314. hr = helpSessionManager->RetrieveHelpSession(
  315. g_HelpSessionID,
  316. &helpSession
  317. );
  318. if (!SUCCEEDED(hr)) {
  319. TRC_ERR((TB, L"RetrieveHelpSession: %08X", hr));
  320. errReturnCode = SAFERROR_SHADOWEND_UNKNOWN;
  321. goto CLEANUPANDEXIT;
  322. }
  323. //
  324. // Set shadow configuration to help session RDS setting
  325. // Console shadow always reset shadow class back to
  326. // original value.
  327. //
  328. hr = helpSession->EnableUserSessionRdsSetting(TRUE);
  329. if( FAILED(hr) ) {
  330. TRC_ERR((TB, L"Can't set shadow setting on %ld : %08X.", hr));
  331. errReturnCode = SAFERROR_SHADOWEND_UNKNOWN;
  332. goto CLEANUPANDEXIT;
  333. }
  334. //
  335. // Shadow the desktop.
  336. //
  337. if (!WinStationShadow(
  338. SERVERNAME_CURRENT,
  339. NULL, //machineName,
  340. g_ClientSessionID,
  341. TSRDPREMOTEDESKTOP_SHADOWVKEY,
  342. TSRDPREMOTEDESKTOP_SHADOWVKEYMODIFIER
  343. )) {
  344. result = GetLastError();
  345. hr = HRESULT_FROM_WIN32(result);
  346. //
  347. // Map the error code to a SAF error code.
  348. //
  349. if( result == ERROR_CTX_SHADOW_ENDED_BY_MODE_CHANGE ) {
  350. errReturnCode = SAFERROR_SHADOWEND_CONFIGCHANGE;
  351. }
  352. else {
  353. errReturnCode = SAFERROR_SHADOWEND_UNKNOWN;
  354. }
  355. }
  356. //
  357. // No need to reset g_ClientSessionID, we don't support multiple instance.
  358. //
  359. //
  360. // Inform help session object that shadow has completed, NotifyRemoteControl()
  361. // internally invoke EnableUserSessionRdsSetting(TRUE) to change
  362. // TS shadow class
  363. // No need to reset g_ClientSessionID, we don't support multiple instance.
  364. //
  365. //
  366. // Inform help session object that shadow has completed
  367. //
  368. hr = helpSession->EnableUserSessionRdsSetting( FALSE );
  369. if (FAILED(hr)) {
  370. TRC_ERR((TB, L"Can't reset shadow setting on %ld : %08X.",
  371. g_ClientSessionID, hr));
  372. //
  373. // not a critical error.
  374. //
  375. }
  376. CLEANUPANDEXIT:
  377. //
  378. // Send the result to the client on failure to shadow.
  379. //
  380. ReturnResultToClient(errReturnCode);
  381. CoUninitialize();
  382. DC_END_FN();
  383. _endthread();
  384. }
  385. VOID
  386. RemoteControlDesktop(
  387. BSTR parms
  388. )
  389. /*++
  390. Routine Description:
  391. Arguments:
  392. Connection Parameters
  393. Return Value:
  394. --*/
  395. {
  396. DC_BEGIN_FN("RemoteControlDesktop");
  397. //
  398. // RDCHOST.DLL will not send any control message so there is no checking on
  399. // second remote control command.
  400. //
  401. g_RemoteControlDesktopThread = (HANDLE)_beginthread( RemoteControlDesktopThread, 0, (void *)parms );
  402. if ((uintptr_t)g_RemoteControlDesktopThread == -1 ) {
  403. g_RemoteControlDesktopThread = NULL;
  404. TRC_ERR((TB, L"Failed to create RemoteControlDesktopThread for session %s - %ld",
  405. g_ClientSessionID, GetLastError()));
  406. // return error code only when
  407. // failed to spawn another thread
  408. ReturnResultToClient(SAFERROR_SHADOWEND_UNKNOWN);
  409. }
  410. DC_END_FN();
  411. }
  412. BOOL
  413. ClientVersionCompatible(
  414. DWORD dwMajor,
  415. DWORD dwMinor
  416. )
  417. /*++
  418. Routine Description:
  419. Verify client (expert) version is compatible with our version.
  420. Parameters:
  421. dwMajor : Client major version.
  422. dwMinor : Client minor version.
  423. Returns:
  424. None.
  425. --*/
  426. {
  427. //
  428. // Build 2409 or earlier (including B1 release has major version of 1 and minor version of 1
  429. // rdchost/rdsaddin need to deal with versioning, for build 2409 or earlier, we
  430. // just make it in-compatible since we need some expert identity from rdchost.dll
  431. //
  432. #if FEATURE_USERBLOBS
  433. if( dwMajor == 1 && dwMinor == 1 ) {
  434. return FALSE;
  435. }
  436. #endif
  437. return TRUE;
  438. }
  439. VOID
  440. ClientAuthenticate(
  441. BSTR parms,
  442. BSTR blob
  443. )
  444. /*++
  445. Routine Description:
  446. Handle a REMOTEDESKTOP_CTL_AUTHENTICATE request from the client.
  447. Arguments:
  448. Return Value:
  449. This function will return the following results back to the client,
  450. based on the following
  451. --*/
  452. {
  453. DC_BEGIN_FN("ClientAuthenticate");
  454. HRESULT hr;
  455. DWORD result = ERROR_NOT_AUTHENTICATED;
  456. CComBSTR machineName;
  457. CComBSTR assistantAccount;
  458. CComBSTR assistantAccountPwd;
  459. CComBSTR helpSessionPwd;
  460. CComBSTR helpSessionName;
  461. CComBSTR protocolSpecificParms;
  462. BOOL match;
  463. DWORD protocolType;
  464. long userTSSessionID;
  465. DWORD dwVersion;
  466. LONG clientReturnCode = SAFERROR_NOERROR;
  467. if( FALSE == ClientVersionCompatible( g_ClientMajor, g_ClientMinor ) ) {
  468. clientReturnCode = SAFERROR_INCOMPATIBLEVERSION;
  469. goto CLEANUPANDEXIT;
  470. }
  471. //
  472. // Parse the parms.
  473. //
  474. result = ParseConnectParmsString(
  475. parms,
  476. &dwVersion,
  477. &protocolType,
  478. machineName,
  479. assistantAccount,
  480. assistantAccountPwd,
  481. g_HelpSessionID,
  482. helpSessionName,
  483. helpSessionPwd,
  484. protocolSpecificParms
  485. );
  486. if (result != ERROR_SUCCESS) {
  487. clientReturnCode = SAFERROR_INVALIDPARAMETERSTRING;
  488. goto CLEANUPANDEXIT;
  489. }
  490. //
  491. // Verify HelpSession ID and password match with our command line
  492. // parameter
  493. //
  494. if( !(g_bstrCmdLineHelpSessionId == g_HelpSessionID) ) {
  495. clientReturnCode = SAFERROR_MISMATCHPARMS;
  496. TRC_ERR((TB, TEXT("Parameter mismatched")));
  497. goto CLEANUPANDEXIT;
  498. }
  499. //
  500. // Open an instance of the Remote Desktop Help Session Manager service.
  501. //
  502. hr = g_HelpSessionManager.CoCreateInstance(CLSID_RemoteDesktopHelpSessionMgr);
  503. if (!SUCCEEDED(hr)) {
  504. clientReturnCode = SAFERROR_INTERNALERROR;
  505. goto CLEANUPANDEXIT;
  506. }
  507. //
  508. // Set the security level to impersonate. This is required by
  509. // the session manager.
  510. //
  511. hr = CoSetProxyBlanket(
  512. (IUnknown *)g_HelpSessionManager,
  513. RPC_C_AUTHN_DEFAULT,
  514. RPC_C_AUTHZ_DEFAULT,
  515. NULL,
  516. RPC_C_AUTHN_LEVEL_DEFAULT,
  517. RPC_C_IMP_LEVEL_IMPERSONATE,
  518. NULL,
  519. EOAC_NONE
  520. );
  521. if (!SUCCEEDED(hr)) {
  522. TRC_ERR((TB, TEXT("CoSetProxyBlanket: %08X"), hr));
  523. ASSERT(FALSE);
  524. clientReturnCode = SAFERROR_INTERNALERROR;
  525. goto CLEANUPANDEXIT;
  526. }
  527. //
  528. // Resolve the Terminal Services session with help from the session
  529. // manager. This gives the help application the opportunity to "find
  530. // the user" and to start the TS-session named pipe component,
  531. // by opening the relevant Remote Desktopping Session Object.
  532. //
  533. hr = g_HelpSessionManager->VerifyUserHelpSession(
  534. g_HelpSessionID,
  535. helpSessionPwd,
  536. CComBSTR(parms),
  537. blob,
  538. GetCurrentProcessId(),
  539. (ULONG_PTR*)&g_hHelpCenterProcess,
  540. &clientReturnCode,
  541. &userTSSessionID
  542. );
  543. if (SUCCEEDED(hr)) {
  544. if( userTSSessionID != -1 ) {
  545. //
  546. // Cache the session ID so we don't have to make extra call
  547. // to get the actual session ID, note, one instance of RDSADDIN
  548. // per help assistant connection.
  549. //
  550. g_ClientSessionID = userTSSessionID;
  551. match = TRUE;
  552. }
  553. if (match) {
  554. TRC_NRM((TB, L"Successful password authentication for %ld",
  555. g_ClientSessionID));
  556. }
  557. else {
  558. TRC_ALT((TB, L"Can't authenticate pasword %s for %s",
  559. helpSessionPwd, g_HelpSessionID));
  560. clientReturnCode = SAFERROR_INVALIDPASSWORD;
  561. goto CLEANUPANDEXIT;
  562. }
  563. }
  564. else {
  565. TRC_ERR((TB, L"Can't verify user help session %s: %08X.",
  566. g_HelpSessionID, hr));
  567. if( SAFERROR_NOERROR == clientReturnCode ) {
  568. ASSERT(FALSE);
  569. TRC_ERR((TB, L"Sessmgr did not return correct error code for VerifyUserHelpSession."));
  570. clientReturnCode = SAFERROR_UNKNOWNSESSMGRERROR;
  571. }
  572. goto CLEANUPANDEXIT;
  573. }
  574. #ifndef DISABLESECURITYCHECKS
  575. //
  576. // Wait on Help Center to terminate as a fix for B2 Stopper: 342742.
  577. //
  578. if (g_hHelpCenterProcess == NULL) {
  579. TRC_ERR((TB, L"Invalid g_HelpCenterProcess."));
  580. ASSERT(FALSE);
  581. clientReturnCode = SAFERROR_INTERNALERROR;
  582. goto CLEANUPANDEXIT;
  583. }
  584. result = WTBLOBJ_AddWaitableObject(
  585. g_WaitObjMgr, NULL,
  586. g_hHelpCenterProcess,
  587. HandleHelpCenterExit
  588. );
  589. if (result != ERROR_SUCCESS) {
  590. goto CLEANUPANDEXIT;
  591. }
  592. #endif
  593. //
  594. // Connect to the client session's named pipe.
  595. //
  596. result = ConnectClientSessionPipe();
  597. if (result != ERROR_SUCCESS) {
  598. clientReturnCode = SAFERROR_CANTFORMLINKTOUSERSESSION;
  599. }
  600. CLEANUPANDEXIT:
  601. if (result == ERROR_SUCCESS) {
  602. g_ClientAuthenticated = TRUE;
  603. }
  604. //
  605. // Send the result to the client.
  606. //
  607. ReturnResultToClient(clientReturnCode);
  608. DC_END_FN();
  609. }
  610. DWORD
  611. ProcessControlChannelRequest(
  612. IOBUFFER &msg
  613. )
  614. /*++
  615. Routine Description:
  616. Arguments:
  617. Return Value:
  618. ERROR_SUCCESS on success. Otherwise, an error status is returned.
  619. --*/
  620. {
  621. DC_BEGIN_FN("ProcessControlChannelRequest");
  622. PREMOTEDESKTOP_CTL_BUFHEADER ctlHdr;
  623. PBYTE ptr;
  624. //
  625. // Sanity check the message size.
  626. //
  627. DWORD minSize = sizeof(REMOTEDESKTOP_CHANNELBUFHEADER) + sizeof(REMOTEDESKTOP_CTL_BUFHEADER);
  628. if (msg.bufSize < minSize) {
  629. TRC_ERR((TB, L"minSize == %ld", minSize));
  630. ASSERT(FALSE);
  631. DC_END_FN();
  632. return E_FAIL;
  633. }
  634. //
  635. // Switch on the request type.
  636. //
  637. ptr = (PBYTE)(msg.buffer + 1);
  638. ptr += msg.buffer->channelNameLen;
  639. ctlHdr = (PREMOTEDESKTOP_CTL_BUFHEADER)ptr;
  640. switch(ctlHdr->msgType)
  641. {
  642. case REMOTEDESKTOP_CTL_AUTHENTICATE:
  643. {
  644. CComBSTR bstrConnectParm;
  645. #if FEATURE_USERBLOBS
  646. CComBSTR bstrExpertBlob;
  647. #endif
  648. bstrConnectParm = (BSTR)(ptr+sizeof(REMOTEDESKTOP_CTL_BUFHEADER));
  649. #if FEATURE_USERBLOBS
  650. bstrExpertBlob = (BSTR)(ptr+sizeof(REMOTEDESKTOP_CTL_BUFHEADER)+(bstrConnectParm.Length()+1)*sizeof(WCHAR));
  651. #endif
  652. ClientAuthenticate(
  653. bstrConnectParm,
  654. #if FEATURE_USERBLOBS
  655. bstrExpertBlob
  656. #else
  657. CComBSTR(L"")
  658. #endif
  659. );
  660. }
  661. break;
  662. case REMOTEDESKTOP_CTL_REMOTE_CONTROL_DESKTOP :
  663. RemoteControlDesktop((BSTR)(ptr+sizeof(REMOTEDESKTOP_CTL_BUFHEADER)));
  664. break;
  665. case REMOTEDESKTOP_CTL_VERSIONINFO:
  666. g_ClientMajor = *(DWORD *)(ptr + sizeof(REMOTEDESKTOP_CTL_BUFHEADER));
  667. g_ClientMinor = *(DWORD *)(ptr + sizeof(REMOTEDESKTOP_CTL_BUFHEADER) + sizeof(DWORD));
  668. TRC_NRM((TB, L"dwMajor = %ld, dwMinor = %d", g_ClientMajor, g_ClientMinor));
  669. //
  670. // We only store version number and let ClientAuthenticate() disconnect client,
  671. // rdchost.dll send two packets, version and AUTHENTICATE in sequence.
  672. //
  673. break;
  674. default:
  675. //
  676. // We will ignore unknown control messages for forward compatibility
  677. //
  678. TRC_NRM((TB, L"Unknown ctl message from client: %ld", ctlHdr->msgType));
  679. }
  680. DC_END_FN();
  681. return ERROR_SUCCESS;
  682. }
  683. DWORD
  684. SendMsgToClient(
  685. PREMOTEDESKTOP_CHANNELBUFHEADER msg
  686. )
  687. /*++
  688. Routine Description:
  689. Arguments:
  690. msg - Message to send.
  691. Return Value:
  692. ERROR_SUCCESS on success. Otherwise, an error status is returned.
  693. --*/
  694. {
  695. DC_BEGIN_FN("SendMsgToClient");
  696. OVERLAPPED overlapped;
  697. PBYTE ptr;
  698. DWORD bytesToWrite;
  699. DWORD bytesWritten;
  700. DWORD result = ERROR_SUCCESS;
  701. #ifdef USE_MAGICNO
  702. ASSERT(msg->magicNo == CHANNELBUF_MAGICNO);
  703. #endif
  704. //
  705. // Send the data out the virtual channel interface.
  706. //
  707. // TODO: Figure out why this flag is not getting set ... and
  708. // if it really matters. Likely, remove the flag.
  709. //
  710. //if (g_ClientConnected) {
  711. {
  712. ptr = (PBYTE)msg;
  713. bytesToWrite = msg->dataLen + msg->channelNameLen +
  714. sizeof(REMOTEDESKTOP_CHANNELBUFHEADER);
  715. while (bytesToWrite > 0) {
  716. //
  717. // Write
  718. //
  719. memset(&overlapped, 0, sizeof(overlapped));
  720. if (!WriteFile(g_VCFileHandle, ptr, bytesToWrite,
  721. &bytesWritten, &overlapped)) {
  722. if (GetLastError() == ERROR_IO_PENDING) {
  723. if (!GetOverlappedResult(
  724. g_VCFileHandle,
  725. &overlapped,
  726. &bytesWritten,
  727. TRUE)) {
  728. result = GetLastError();
  729. TRC_ERR((TB, L"GetOverlappedResult: %08X", result));
  730. break;
  731. }
  732. }
  733. else {
  734. result = GetLastError();
  735. TRC_ERR((TB, L"WriteFile: %08X", result));
  736. // ASSERT(FALSE); overactive assert after disconnect
  737. break;
  738. }
  739. }
  740. //
  741. // Increment the ptr and decrement the bytes remaining.
  742. //
  743. bytesToWrite -= bytesWritten;
  744. ptr += bytesWritten;
  745. }
  746. }
  747. /*
  748. else {
  749. result = ERROR_NOT_CONNECTED;
  750. }
  751. */
  752. //
  753. //update the timer
  754. //
  755. g_PrevTimer = GetTickCount();
  756. DC_END_FN();
  757. return result;
  758. }
  759. VOID
  760. HandleVCReadComplete(
  761. HANDLE waitableObject,
  762. PVOID clientData
  763. )
  764. /*++
  765. Routine Description:
  766. Arguments:
  767. Return Value:
  768. --*/
  769. {
  770. DC_BEGIN_FN("HandleVCReadComplete");
  771. DWORD bytesRead;
  772. DWORD result = ERROR_SUCCESS;
  773. BOOL resizeBuf = FALSE;
  774. //
  775. // Get the results of the read.
  776. //
  777. if (!GetOverlappedResult(
  778. g_VCFileHandle,
  779. &g_VCReadOverlapped,
  780. &bytesRead,
  781. FALSE)) {
  782. //
  783. // If we are too small, then reissue the read with a larger buffer.
  784. //
  785. if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
  786. resizeBuf = TRUE;
  787. }
  788. else {
  789. result = GetLastError();
  790. TRC_ERR((TB, L"GetOverlappedResult: %08X", result));
  791. goto CLEANUPANDEXIT;
  792. }
  793. }
  794. else {
  795. g_IncomingVCBuf.offset += bytesRead;
  796. }
  797. //
  798. // See if we have a complete packet from the client.
  799. //
  800. if (g_IncomingVCBuf.offset >= sizeof(REMOTEDESKTOP_CHANNELBUFHEADER)) {
  801. DWORD packetSize = g_IncomingVCBuf.buffer->dataLen +
  802. g_IncomingVCBuf.buffer->channelNameLen +
  803. sizeof(REMOTEDESKTOP_CHANNELBUFHEADER);
  804. //
  805. // If we have a complete packet, then handle the read and reset the offset.
  806. //
  807. if (g_IncomingVCBuf.offset >= packetSize) {
  808. result = HandleReceivedVCMsg(g_IncomingVCBuf);
  809. if (result == ERROR_SUCCESS) {
  810. g_IncomingVCBuf.offset = 0;
  811. }
  812. else {
  813. goto CLEANUPANDEXIT;
  814. }
  815. }
  816. //
  817. // Otherwise, resize the incoming buf if we are exactly at the incoming
  818. // buffer boundary.
  819. //
  820. else if (g_IncomingVCBuf.offset == g_IncomingVCBuf.bufSize) {
  821. resizeBuf = TRUE;
  822. }
  823. }
  824. //
  825. // Resize, if necessary.
  826. //
  827. if (resizeBuf) {
  828. g_IncomingVCBuf.buffer =
  829. (PREMOTEDESKTOP_CHANNELBUFHEADER )REALLOCMEM(
  830. g_IncomingVCBuf.buffer,
  831. g_IncomingVCBuf.bufSize + VCBUFFER_RESIZE_DELTA
  832. );
  833. if (g_IncomingVCBuf.buffer != NULL) {
  834. result = ERROR_SUCCESS;
  835. g_IncomingVCBuf.bufSize = g_IncomingVCBuf.bufSize +
  836. VCBUFFER_RESIZE_DELTA;
  837. }
  838. else {
  839. result = ERROR_NOT_ENOUGH_MEMORY;
  840. TRC_ERR((TB, L"Couldn't allocate incoming VC buf."));
  841. goto CLEANUPANDEXIT;
  842. }
  843. }
  844. //
  845. //update the timer
  846. //
  847. g_PrevTimer = GetTickCount();
  848. //
  849. // Issue the next read request.
  850. //
  851. result = IssueVCOverlappedRead(g_IncomingVCBuf, g_VCReadOverlapped) ;
  852. CLEANUPANDEXIT:
  853. //
  854. // Any failure is fatal. The client will need to reconnect to get things
  855. // started again.
  856. //
  857. if (result != ERROR_SUCCESS) {
  858. TRC_ERR((TB, L"Client considered disconnected. Shutting down."));
  859. g_Shutdown = TRUE;
  860. }
  861. DC_END_FN();
  862. }
  863. DWORD
  864. IssueVCOverlappedRead(
  865. IOBUFFER &msg,
  866. OVERLAPPED &ol
  867. )
  868. /*++
  869. Routine Description:
  870. Issue an overlapped read for the next VC buffer.
  871. Arguments:
  872. msg - Incoming VC buffer.
  873. ol - Corresponding overlapped IO struct.
  874. Return Value:
  875. Returns ERROR_SUCCESS on success. Otherwise, an error code is returned.
  876. --*/
  877. {
  878. DC_BEGIN_FN("IssueVCOverlappedRead");
  879. DWORD result = ERROR_SUCCESS;
  880. ol.Internal = 0;
  881. ol.InternalHigh = 0;
  882. ol.Offset = 0;
  883. ol.OffsetHigh = 0;
  884. ResetEvent(ol.hEvent);
  885. if (!ReadFile(g_VCFileHandle, ((PBYTE)msg.buffer)+msg.offset,
  886. msg.bufSize - msg.offset, NULL, &ol)) {
  887. if (GetLastError() != ERROR_IO_PENDING) {
  888. result = GetLastError();
  889. TRC_ERR((TB, L"ReadFile failed: %08X", result));
  890. }
  891. }
  892. DC_END_FN();
  893. return result;
  894. }
  895. DWORD
  896. IssueNamedPipeOverlappedRead(
  897. IOBUFFER &msg,
  898. OVERLAPPED &ol,
  899. DWORD len
  900. )
  901. /*++
  902. Routine Description:
  903. Issue an overlapped read for the next named pipe buffer.
  904. Arguments:
  905. msg - Incoming Named Pipe buffer.
  906. ol - Corresponding overlapped IO struct.
  907. Return Value:
  908. Returns ERROR_SUCCESS on success. Otherwise, an error code is returned.
  909. --*/
  910. {
  911. DC_BEGIN_FN("IssueNamedPipeOverlappedRead");
  912. DWORD result = ERROR_SUCCESS;
  913. ol.Internal = 0;
  914. ol.InternalHigh = 0;
  915. ol.Offset = 0;
  916. ol.OffsetHigh = 0;
  917. ResetEvent(ol.hEvent);
  918. if (!ReadFile(g_ClientSessionPipe, ((PBYTE)msg.buffer), len, NULL, &ol)) {
  919. if (GetLastError() != ERROR_IO_PENDING) {
  920. result = GetLastError();
  921. TRC_ERR((TB, L"ReadFile failed: %08X", result));
  922. }
  923. }
  924. DC_END_FN();
  925. return result;
  926. }
  927. DWORD
  928. HandleReceivedVCMsg(
  929. IOBUFFER &msg
  930. )
  931. /*++
  932. Routine Description:
  933. Arguments:
  934. Return Value:
  935. --*/
  936. {
  937. DC_BEGIN_FN("HandleReceivedVCMsg");
  938. OVERLAPPED overlapped;
  939. PBYTE ptr;
  940. DWORD bytesToWrite;
  941. DWORD bytesWritten;
  942. DWORD result = ERROR_SUCCESS;
  943. BSTR channelName;
  944. BSTREqual isBSTREqual;
  945. CComBSTR tmpStr;
  946. #ifdef USE_MAGICNO
  947. ASSERT(msg.buffer->magicNo == CHANNELBUF_MAGICNO);
  948. #endif
  949. //
  950. // Get the channel name.
  951. // TODO: We could actually be smarter about this by checking the
  952. // length for a match, first.
  953. //
  954. channelName = SysAllocStringByteLen(NULL, msg.buffer->channelNameLen);
  955. if (channelName == NULL) {
  956. TRC_ERR((TB, TEXT("Can't allocate channel name.")));
  957. goto CLEANUPANDEXIT;
  958. }
  959. ptr = (PBYTE)(msg.buffer + 1);
  960. memcpy(channelName, ptr, msg.buffer->channelNameLen);
  961. //
  962. // Filter control channel data.
  963. //
  964. tmpStr = REMOTEDESKTOP_RC_CONTROL_CHANNEL;
  965. if (isBSTREqual(channelName, tmpStr)) {
  966. result = ProcessControlChannelRequest(msg);
  967. goto CLEANUPANDEXIT;
  968. }
  969. //
  970. // If the client is not yet authenticated.
  971. //
  972. if (!g_ClientAuthenticated) {
  973. ASSERT(FALSE);
  974. result = E_FAIL;
  975. goto CLEANUPANDEXIT;
  976. }
  977. //
  978. // Send the message header.
  979. //
  980. memset(&overlapped, 0, sizeof(overlapped));
  981. overlapped.hEvent = g_NamedPipeWriteEvent;
  982. ResetEvent(g_NamedPipeWriteEvent);
  983. if (!WriteFile(g_ClientSessionPipe,
  984. msg.buffer, sizeof(REMOTEDESKTOP_CHANNELBUFHEADER),
  985. &bytesWritten, &overlapped)) {
  986. if (GetLastError() == ERROR_IO_PENDING) {
  987. if (WaitForSingleObject(
  988. g_NamedPipeWriteEvent,
  989. INFINITE
  990. ) != WAIT_OBJECT_0) {
  991. result = GetLastError();
  992. TRC_ERR((TB, L"WaitForSingleObject: %08X", result));
  993. goto CLEANUPANDEXIT;
  994. }
  995. if (!GetOverlappedResult(
  996. g_ClientSessionPipe,
  997. &overlapped,
  998. &bytesWritten,
  999. FALSE)) {
  1000. result = GetLastError();
  1001. TRC_ERR((TB, L"GetOverlappedResult: %08X", result));
  1002. goto CLEANUPANDEXIT;
  1003. }
  1004. }
  1005. else {
  1006. result = GetLastError();
  1007. TRC_ERR((TB, L"WriteFile: %08X", result));
  1008. goto CLEANUPANDEXIT;
  1009. }
  1010. }
  1011. ASSERT(bytesWritten == sizeof(REMOTEDESKTOP_CHANNELBUFHEADER));
  1012. //
  1013. // Send the message data.
  1014. //
  1015. ptr = ((PBYTE)msg.buffer) + sizeof(REMOTEDESKTOP_CHANNELBUFHEADER);
  1016. memset(&overlapped, 0, sizeof(overlapped));
  1017. overlapped.hEvent = g_NamedPipeWriteEvent;
  1018. ResetEvent(g_NamedPipeWriteEvent);
  1019. if (!WriteFile(g_ClientSessionPipe,
  1020. ptr, msg.buffer->dataLen +
  1021. msg.buffer->channelNameLen,
  1022. &bytesWritten, &overlapped)) {
  1023. if (GetLastError() == ERROR_IO_PENDING) {
  1024. if (WaitForSingleObject(
  1025. g_NamedPipeWriteEvent,
  1026. INFINITE
  1027. ) != WAIT_OBJECT_0) {
  1028. result = GetLastError();
  1029. TRC_ERR((TB, L"WaitForSingleObject: %08X", result));
  1030. goto CLEANUPANDEXIT;
  1031. }
  1032. if (!GetOverlappedResult(
  1033. g_ClientSessionPipe,
  1034. &overlapped,
  1035. &bytesWritten,
  1036. FALSE)) {
  1037. result = GetLastError();
  1038. TRC_ERR((TB, L"GetOverlappedResult: %08X", result));
  1039. goto CLEANUPANDEXIT;
  1040. }
  1041. }
  1042. else {
  1043. result = GetLastError();
  1044. TRC_ERR((TB, L"WriteFile: %08X", result));
  1045. goto CLEANUPANDEXIT;
  1046. }
  1047. }
  1048. ASSERT(bytesWritten == msg.buffer->dataLen +
  1049. msg.buffer->channelNameLen);
  1050. CLEANUPANDEXIT:
  1051. if (channelName != NULL) {
  1052. SysFreeString(channelName);
  1053. }
  1054. DC_END_FN();
  1055. return result;
  1056. }
  1057. VOID
  1058. HandleVCClientConnect(
  1059. HANDLE waitableObject,
  1060. PVOID clientData
  1061. )
  1062. /*++
  1063. Routine Description:
  1064. Arguments:
  1065. Return Value:
  1066. --*/
  1067. {
  1068. DC_BEGIN_FN("HandleVCClientConnect");
  1069. g_ClientConnected = TRUE;
  1070. DC_END_FN();
  1071. }
  1072. VOID
  1073. HandleVCClientDisconnect(
  1074. HANDLE waitableObject,
  1075. PVOID clientData
  1076. )
  1077. /*++
  1078. Routine Description:
  1079. Arguments:
  1080. Return Value:
  1081. ERROR_SUCCESS on success. Otherwise, an error status is returned.
  1082. --*/
  1083. {
  1084. DC_BEGIN_FN("HandleVCClientDisconnect");
  1085. DWORD dwCurTimer = GetTickCount();
  1086. //
  1087. //see if the timer wrapped around to zero (does so if the system was up 49.7 days or something), if so reset it
  1088. //
  1089. if(dwCurTimer > g_PrevTimer && ( dwCurTimer - g_PrevTimer >= g_dwTimeOutInterval)) {
  1090. //
  1091. //enough time passed since the last check. send data to client
  1092. if( SendNullDataToClient() != ERROR_SUCCESS ) {
  1093. //
  1094. //set the shutdown flag
  1095. //
  1096. g_Shutdown = TRUE;
  1097. g_ClientConnected = FALSE;
  1098. }
  1099. }
  1100. g_PrevTimer = dwCurTimer;
  1101. DC_END_FN();
  1102. }
  1103. VOID
  1104. HandleNamedPipeReadComplete(
  1105. OVERLAPPED &incomingPipeOL,
  1106. IOBUFFER &incomingPipeBuf
  1107. )
  1108. /*++
  1109. Routine Description:
  1110. Handle a read complete event on the session's named pipe.
  1111. Arguments:
  1112. incomingPipeOL - Overlapped Read Struct
  1113. incomingPipeBuf - Incoming Data Buffer.
  1114. Return Value:
  1115. --*/
  1116. {
  1117. DC_BEGIN_FN("HandleNamedPipeReadComplete");
  1118. DWORD bytesRead;
  1119. DWORD requiredSize;
  1120. BOOL disconnectClientPipe = FALSE;
  1121. DWORD result;
  1122. DWORD bytesToRead;
  1123. HANDLE waitableObjects[2];
  1124. DWORD waitResult;
  1125. //
  1126. // Get the results of the read on the buffer header.
  1127. //
  1128. if (!GetOverlappedResult(
  1129. g_ClientSessionPipe,
  1130. &incomingPipeOL,
  1131. &bytesRead,
  1132. FALSE)
  1133. || (bytesRead != sizeof(REMOTEDESKTOP_CHANNELBUFHEADER))) {
  1134. disconnectClientPipe = TRUE;
  1135. goto CLEANUPANDEXIT;
  1136. }
  1137. //
  1138. // Make sure the incoming buffer is large enough.
  1139. //
  1140. requiredSize = incomingPipeBuf.buffer->dataLen +
  1141. incomingPipeBuf.buffer->channelNameLen +
  1142. sizeof(REMOTEDESKTOP_CHANNELBUFHEADER);
  1143. if (incomingPipeBuf.bufSize < requiredSize) {
  1144. incomingPipeBuf.buffer = (PREMOTEDESKTOP_CHANNELBUFHEADER )REALLOCMEM(
  1145. incomingPipeBuf.buffer,
  1146. requiredSize
  1147. );
  1148. if (incomingPipeBuf.buffer != NULL) {
  1149. incomingPipeBuf.bufSize = requiredSize;
  1150. }
  1151. else {
  1152. TRC_ERR((TB, L"Shutting down because of memory allocation failure."));
  1153. g_Shutdown = TRUE;
  1154. goto CLEANUPANDEXIT;
  1155. }
  1156. }
  1157. //
  1158. // Now read the buffer data.
  1159. //
  1160. incomingPipeOL.Internal = 0;
  1161. incomingPipeOL.InternalHigh = 0;
  1162. incomingPipeOL.Offset = 0;
  1163. incomingPipeOL.OffsetHigh = 0;
  1164. ResetEvent(incomingPipeOL.hEvent);
  1165. if (!ReadFile(
  1166. g_ClientSessionPipe,
  1167. incomingPipeBuf.buffer + 1,
  1168. incomingPipeBuf.buffer->channelNameLen +
  1169. incomingPipeBuf.buffer->dataLen,
  1170. &bytesRead, &incomingPipeOL)
  1171. ) {
  1172. if (GetLastError() == ERROR_IO_PENDING) {
  1173. waitableObjects[0] = incomingPipeOL.hEvent;
  1174. waitableObjects[1] = g_ShutdownEvent;
  1175. waitResult = WaitForMultipleObjects(
  1176. 2, waitableObjects,
  1177. FALSE,
  1178. INFINITE
  1179. );
  1180. if ((waitResult != WAIT_OBJECT_0) || g_Shutdown) {
  1181. disconnectClientPipe = TRUE;
  1182. goto CLEANUPANDEXIT;
  1183. }
  1184. if (!GetOverlappedResult(
  1185. g_ClientSessionPipe,
  1186. &incomingPipeOL,
  1187. &bytesRead,
  1188. FALSE)) {
  1189. disconnectClientPipe = TRUE;
  1190. goto CLEANUPANDEXIT;
  1191. }
  1192. }
  1193. else {
  1194. disconnectClientPipe = TRUE;
  1195. goto CLEANUPANDEXIT;
  1196. }
  1197. }
  1198. //
  1199. // Make sure we got all the data.
  1200. //
  1201. bytesToRead = incomingPipeBuf.buffer->channelNameLen +
  1202. incomingPipeBuf.buffer->dataLen;
  1203. if (bytesRead != bytesToRead) {
  1204. TRC_ERR((TB, L"Bytes read: %ld != bytes requested: %ld",
  1205. bytesRead, bytesToRead));
  1206. ASSERT(FALSE);
  1207. disconnectClientPipe = TRUE;
  1208. goto CLEANUPANDEXIT;
  1209. }
  1210. //
  1211. // Handle the read data.
  1212. //
  1213. HandleReceivedPipeMsg(incomingPipeBuf);
  1214. //
  1215. // Issue the read for the next message header.
  1216. //
  1217. result = IssueNamedPipeOverlappedRead(
  1218. incomingPipeBuf,
  1219. incomingPipeOL,
  1220. sizeof(REMOTEDESKTOP_CHANNELBUFHEADER)
  1221. );
  1222. disconnectClientPipe = (result != ERROR_SUCCESS);
  1223. CLEANUPANDEXIT:
  1224. //
  1225. // This is considered a fatal error because the client session must
  1226. // no longer be in "listen" mode.
  1227. //
  1228. if (disconnectClientPipe) {
  1229. TRC_ERR((TB, L"Connection to client pipe lost: %08X",
  1230. GetLastError()));
  1231. g_Shutdown = TRUE;
  1232. }
  1233. DC_END_FN();
  1234. }
  1235. VOID
  1236. HandleReceivedPipeMsg(
  1237. IOBUFFER &msg
  1238. )
  1239. /*++
  1240. Routine Description:
  1241. Arguments:
  1242. Return Value:
  1243. --*/
  1244. {
  1245. DC_BEGIN_FN("HandleReceivedPipeMsg");
  1246. DWORD result;
  1247. //
  1248. // Forward the message to the client.
  1249. //
  1250. result = SendMsgToClient(msg.buffer);
  1251. //
  1252. // This is considered a fatal error. The client will need to reconnect
  1253. // to get things started again.
  1254. //
  1255. if (result != ERROR_SUCCESS) {
  1256. TRC_ERR((TB, L"Shutting down because of VC IO error."));
  1257. g_Shutdown = TRUE;
  1258. }
  1259. DC_END_FN();
  1260. }
  1261. DWORD
  1262. ConnectVC()
  1263. /*++
  1264. Routine Description:
  1265. Arguments:
  1266. Return Value:
  1267. ERROR_SUCCESS on success. Otherwise, an error status is returned.
  1268. --*/
  1269. {
  1270. DC_BEGIN_FN("ConnectVC");
  1271. WCHAR buf[256];
  1272. DWORD len;
  1273. PVOID vcFileHandlePtr;
  1274. REMOTEDESKTOP_CTL_SERVERANNOUNCE_PACKET msg;
  1275. REMOTEDESKTOP_CTL_VERSIONINFO_PACKET versionInfoMsg;
  1276. DWORD result = ERROR_SUCCESS;
  1277. //
  1278. // Open the virtual channel.
  1279. //
  1280. g_VCHandle = WTSVirtualChannelOpen(
  1281. WTS_CURRENT_SERVER_HANDLE,
  1282. WTS_CURRENT_SESSION,
  1283. TSRDPREMOTEDESKTOP_VC_CHANNEL_A
  1284. );
  1285. if (g_VCHandle == NULL) {
  1286. result = GetLastError();
  1287. if (result == ERROR_SUCCESS) { result = E_FAIL; }
  1288. TRC_ERR((TB, L"WTSVirtualChannelOpen: %08X", result));
  1289. goto CLEANUPANDEXIT;
  1290. }
  1291. //
  1292. // Get access to the underlying file handle for async IO.
  1293. //
  1294. if (!WTSVirtualChannelQuery(
  1295. g_VCHandle,
  1296. WTSVirtualFileHandle,
  1297. &vcFileHandlePtr,
  1298. &len
  1299. )) {
  1300. result = GetLastError();
  1301. TRC_ERR((TB, L"WTSQuerySessionInformation: %08X", result));
  1302. goto CLEANUPANDEXIT;
  1303. }
  1304. ASSERT(len == sizeof(g_VCFileHandle));
  1305. //
  1306. // WTSVirtualChannelQuery allocates the returned buffer.
  1307. //
  1308. memcpy(&g_VCFileHandle, vcFileHandlePtr, sizeof(g_VCFileHandle));
  1309. LocalFree(vcFileHandlePtr);
  1310. //
  1311. //create the timer event, we will start it later
  1312. //it will be signaled when the time is up
  1313. //
  1314. g_ClientIsconnectEvent = CreateWaitableTimer( NULL, FALSE, NULL);
  1315. if (g_ClientIsconnectEvent == NULL) {
  1316. result = GetLastError();
  1317. TRC_ERR((TB, L"CreateEvent: %08X", result));
  1318. goto CLEANUPANDEXIT;
  1319. }
  1320. //
  1321. // Create the read finish event.
  1322. //
  1323. g_VCReadOverlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  1324. if (g_VCReadOverlapped.hEvent == NULL) {
  1325. result = GetLastError();
  1326. TRC_ERR((TB, L"CreateEvent: %08X", result));
  1327. goto CLEANUPANDEXIT;
  1328. }
  1329. //
  1330. // Register the read finish event.
  1331. //
  1332. result = WTBLOBJ_AddWaitableObject(
  1333. g_WaitObjMgr, NULL,
  1334. g_VCReadOverlapped.hEvent,
  1335. HandleVCReadComplete
  1336. );
  1337. if (result != ERROR_SUCCESS) {
  1338. goto CLEANUPANDEXIT;
  1339. }
  1340. // register the disconnect event
  1341. //NOTE : order IS important
  1342. //waitformultipleobjects returns the lowest index
  1343. //when more than one are signaled
  1344. //we want to use the read event, not the disconnect event
  1345. //in case both are signaled
  1346. result = WTBLOBJ_AddWaitableObject(
  1347. g_WaitObjMgr, NULL,
  1348. g_ClientIsconnectEvent,
  1349. HandleVCClientDisconnect
  1350. );
  1351. if (result != ERROR_SUCCESS) {
  1352. goto CLEANUPANDEXIT;
  1353. }
  1354. //
  1355. // Allocate space for the first VC read.
  1356. //
  1357. g_IncomingVCBuf.buffer = (PREMOTEDESKTOP_CHANNELBUFHEADER )ALLOCMEM(
  1358. VCBUFFER_RESIZE_DELTA
  1359. );
  1360. if (g_IncomingVCBuf.buffer != NULL) {
  1361. g_IncomingVCBuf.bufSize = VCBUFFER_RESIZE_DELTA;
  1362. g_IncomingVCBuf.offset = 0;
  1363. }
  1364. else {
  1365. TRC_ERR((TB, L"Can't allocate VC read buffer."));
  1366. result = ERROR_NOT_ENOUGH_MEMORY;
  1367. goto CLEANUPANDEXIT;
  1368. }
  1369. //
  1370. // Issue the first overlapped read on the VC.
  1371. //
  1372. result = IssueVCOverlappedRead(g_IncomingVCBuf, g_VCReadOverlapped);
  1373. if (result != ERROR_SUCCESS) {
  1374. goto CLEANUPANDEXIT;
  1375. }
  1376. //
  1377. // Notify the client that we are alive.
  1378. //
  1379. memcpy(msg.packetHeader.channelName, REMOTEDESKTOP_RC_CONTROL_CHANNEL,
  1380. sizeof(REMOTEDESKTOP_RC_CONTROL_CHANNEL));
  1381. msg.packetHeader.channelBufHeader.channelNameLen = REMOTEDESKTOP_RC_CHANNELNAME_LENGTH;
  1382. #ifdef USE_MAGICNO
  1383. msg.packetHeader.channelBufHeader.magicNo = CHANNELBUF_MAGICNO;
  1384. #endif
  1385. msg.packetHeader.channelBufHeader.dataLen =
  1386. sizeof(REMOTEDESKTOP_CTL_SERVERANNOUNCE_PACKET) -
  1387. sizeof(REMOTEDESKTOP_CTL_PACKETHEADER);
  1388. msg.msgHeader.msgType = REMOTEDESKTOP_CTL_SERVER_ANNOUNCE;
  1389. result = SendMsgToClient((PREMOTEDESKTOP_CHANNELBUFHEADER)&msg);
  1390. if (result != ERROR_SUCCESS) {
  1391. goto CLEANUPANDEXIT;
  1392. }
  1393. //
  1394. // Send the server protocol version information.
  1395. //
  1396. memcpy(versionInfoMsg.packetHeader.channelName, REMOTEDESKTOP_RC_CONTROL_CHANNEL,
  1397. sizeof(REMOTEDESKTOP_RC_CONTROL_CHANNEL));
  1398. versionInfoMsg.packetHeader.channelBufHeader.channelNameLen = REMOTEDESKTOP_RC_CHANNELNAME_LENGTH;
  1399. #ifdef USE_MAGICNO
  1400. versionInfoMsg.packetHeader.channelBufHeader.magicNo = CHANNELBUF_MAGICNO;
  1401. #endif
  1402. versionInfoMsg.packetHeader.channelBufHeader.dataLen =
  1403. sizeof(REMOTEDESKTOP_CTL_VERSIONINFO_PACKET) -
  1404. sizeof(REMOTEDESKTOP_CTL_PACKETHEADER);
  1405. versionInfoMsg.msgHeader.msgType = REMOTEDESKTOP_CTL_VERSIONINFO;
  1406. versionInfoMsg.versionMajor = REMOTEDESKTOP_VERSION_MAJOR;
  1407. versionInfoMsg.versionMinor = REMOTEDESKTOP_VERSION_MINOR;
  1408. result = SendMsgToClient((PREMOTEDESKTOP_CHANNELBUFHEADER)&versionInfoMsg);
  1409. if (result != ERROR_SUCCESS) {
  1410. goto CLEANUPANDEXIT;
  1411. }
  1412. CLEANUPANDEXIT:
  1413. DC_END_FN();
  1414. return result;
  1415. }
  1416. DWORD
  1417. ConnectClientSessionPipe()
  1418. /*++
  1419. Routine Description:
  1420. Connect to the client session TSRDP plug-in named pipe.
  1421. Arguments:
  1422. Return Value:
  1423. ERROR_SUCCESS on success. Otherwise, an error status is returned.
  1424. --*/
  1425. {
  1426. DC_BEGIN_FN("ConnectClientSessionPipe");
  1427. WCHAR pipePath[MAX_PATH+1];
  1428. DWORD result;
  1429. DWORD pipeMode = PIPE_READMODE_MESSAGE | PIPE_WAIT;
  1430. //
  1431. // Loop until we are connected or time out.
  1432. //
  1433. ASSERT(g_ClientSessionPipe == NULL);
  1434. while(g_ClientSessionPipe == NULL) {
  1435. wsprintf(pipePath, L"\\\\.\\pipe\\%s-%s",
  1436. TSRDPREMOTEDESKTOP_PIPENAME, g_HelpSessionID);
  1437. g_ClientSessionPipe = CreateFile(
  1438. pipePath,
  1439. GENERIC_READ |
  1440. GENERIC_WRITE,
  1441. 0,
  1442. NULL,
  1443. OPEN_EXISTING,
  1444. FILE_FLAG_OVERLAPPED, NULL
  1445. );
  1446. if (g_ClientSessionPipe != INVALID_HANDLE_VALUE) {
  1447. TRC_NRM((TB, L"Pipe successfully connected."));
  1448. result = ERROR_SUCCESS;
  1449. break;
  1450. }
  1451. else {
  1452. TRC_ALT((TB, L"Waiting for pipe availability: %08X.",
  1453. GetLastError()));
  1454. WaitNamedPipe(pipePath, CLIENTPIPE_CONNECTTIMEOUT);
  1455. result = GetLastError();
  1456. if (result != ERROR_SUCCESS) {
  1457. TRC_ERR((TB, L"WaitNamedPipe: %08X", result));
  1458. break;
  1459. }
  1460. }
  1461. }
  1462. //
  1463. // If we didn't get a valid connection, then bail out of
  1464. // this function and shut down.
  1465. //
  1466. if (g_ClientSessionPipe == INVALID_HANDLE_VALUE) {
  1467. ASSERT(result != ERROR_SUCCESS);
  1468. TRC_ERR((TB, L"Shutting down because of named pipe error."));
  1469. g_Shutdown = TRUE;
  1470. goto CLEANUPANDEXIT;
  1471. }
  1472. //
  1473. //set the options on the pipe to be the same as that of the server end to avoid problems
  1474. //fatal if we could not set it
  1475. //
  1476. if(!SetNamedPipeHandleState(g_ClientSessionPipe,
  1477. &pipeMode, // new pipe mode
  1478. NULL,
  1479. NULL
  1480. )) {
  1481. result = GetLastError();
  1482. TRC_ERR((TB, L"Shutting down, SetNamedPipeHandleState: %08X", result));
  1483. g_Shutdown = TRUE;
  1484. goto CLEANUPANDEXIT;
  1485. }
  1486. //
  1487. // Spin off the pipe read background thread.
  1488. //
  1489. g_NamedPipeReadThread = (HANDLE)_beginthread(NamedPipeReadThread, 0, NULL);
  1490. if ((uintptr_t)g_NamedPipeReadThread == -1) {
  1491. g_NamedPipeReadThread = NULL;
  1492. TRC_ERR((TB, L"Failed to create NamedPipeReadThread: %08X", GetLastError()));
  1493. g_Shutdown = TRUE;
  1494. result = errno;
  1495. goto CLEANUPANDEXIT;
  1496. }
  1497. CLEANUPANDEXIT:
  1498. DC_END_FN();
  1499. return result;
  1500. }
  1501. void __cdecl
  1502. NamedPipeReadThread(
  1503. void* ptr
  1504. )
  1505. /*++
  1506. Routine Description:
  1507. Named Pipe Input Thread
  1508. Arguments:
  1509. ptr - Ignored
  1510. Return Value:
  1511. NA
  1512. --*/
  1513. {
  1514. DC_BEGIN_FN("NamedPipeReadThread");
  1515. IOBUFFER incomingPipeBuf = { NULL, 0, 0 };
  1516. OVERLAPPED overlapped = { 0, 0, 0, 0, NULL };
  1517. DWORD waitResult;
  1518. DWORD ret;
  1519. HANDLE waitableObjects[2];
  1520. //
  1521. // Allocate the initial buffer for incoming named pipe data.
  1522. //
  1523. incomingPipeBuf.buffer = (PREMOTEDESKTOP_CHANNELBUFHEADER )
  1524. ALLOCMEM(sizeof(REMOTEDESKTOP_CHANNELBUFHEADER));
  1525. if (incomingPipeBuf.buffer != NULL) {
  1526. incomingPipeBuf.bufSize = sizeof(REMOTEDESKTOP_CHANNELBUFHEADER);
  1527. }
  1528. else {
  1529. TRC_ERR((TB, L"Can't allocate named pipe buf."));
  1530. g_Shutdown = TRUE;
  1531. goto CLEANUPANDEXIT;
  1532. }
  1533. //
  1534. // Create the overlapped pipe read event.
  1535. //
  1536. overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  1537. if (overlapped.hEvent == NULL) {
  1538. TRC_ERR((TB, L"CreateEvent: %08X", GetLastError()));
  1539. g_Shutdown = TRUE;
  1540. goto CLEANUPANDEXIT;
  1541. }
  1542. //
  1543. // Issue the read for the first message header.
  1544. //
  1545. ret = IssueNamedPipeOverlappedRead(
  1546. incomingPipeBuf,
  1547. overlapped,
  1548. sizeof(REMOTEDESKTOP_CHANNELBUFHEADER)
  1549. );
  1550. //
  1551. // If we can't connect, that's considered a fatal error because
  1552. // the client must no longer be in "listen" mode.
  1553. //
  1554. if (ret != ERROR_SUCCESS) {
  1555. TRC_ERR((TB, L"Shutting down because of named pipe error."));
  1556. g_Shutdown = TRUE;
  1557. }
  1558. //
  1559. // Loop until shut down.
  1560. //
  1561. waitableObjects[0] = overlapped.hEvent;
  1562. waitableObjects[1] = g_ShutdownEvent;
  1563. while (!g_Shutdown) {
  1564. //
  1565. // We will be signalled when the pipe closes or the read completes.
  1566. //
  1567. waitResult = WaitForMultipleObjects(
  1568. 2, waitableObjects,
  1569. FALSE,
  1570. INFINITE
  1571. );
  1572. if ((waitResult == WAIT_OBJECT_0) && !g_Shutdown) {
  1573. HandleNamedPipeReadComplete(overlapped, incomingPipeBuf);
  1574. }
  1575. else {
  1576. TRC_ERR((TB, L"WaitForMultipleObjects: %08X", GetLastError()));
  1577. g_Shutdown = TRUE;
  1578. }
  1579. }
  1580. CLEANUPANDEXIT:
  1581. //
  1582. // Make sure the foreground thread knows that we are shutting down.
  1583. //
  1584. if (g_WakeUpForegroundThreadEvent != NULL) {
  1585. SetEvent(g_WakeUpForegroundThreadEvent);
  1586. }
  1587. if (overlapped.hEvent != NULL) {
  1588. CloseHandle(overlapped.hEvent);
  1589. }
  1590. if (incomingPipeBuf.buffer != NULL) {
  1591. FREEMEM(incomingPipeBuf.buffer);
  1592. }
  1593. DC_END_FN();
  1594. _endthread();
  1595. }
  1596. VOID WakeUpFunc(
  1597. HANDLE waitableObject,
  1598. PVOID clientData
  1599. )
  1600. /*++
  1601. Routine Description:
  1602. Stub function, called when the background thread wants the foreground
  1603. thread to wake up because of a state change.
  1604. Arguments:
  1605. Return Value:
  1606. --*/
  1607. {
  1608. DC_BEGIN_FN("WakeUpFunc");
  1609. DC_END_FN();
  1610. }
  1611. VOID HandleHelpCenterExit(
  1612. HANDLE waitableObject,
  1613. PVOID clientData
  1614. )
  1615. /*++
  1616. Routine Description:
  1617. Woken up when Help Center exits as a fix for B2 Stopper: 342742
  1618. Arguments:
  1619. Return Value:
  1620. --*/
  1621. {
  1622. DC_BEGIN_FN("HandleHelpCenterExit");
  1623. g_Shutdown = TRUE;
  1624. DC_END_FN();
  1625. }
  1626. extern "C"
  1627. int
  1628. __cdecl
  1629. wmain( int argc, wchar_t *argv[])
  1630. {
  1631. DC_BEGIN_FN("Main");
  1632. DWORD result = ERROR_SUCCESS;
  1633. DWORD sz;
  1634. HRESULT hr;
  1635. LARGE_INTEGER liDueTime;
  1636. BOOL backgroundThreadFailedToExit = FALSE;
  1637. DWORD waitResult;
  1638. SetConsoleCtrlHandler( ControlHandler, TRUE );
  1639. //
  1640. // Expecting two parameters, first is HelpSession ID and second is
  1641. // HelpSession Password, we don't want to failed here just because
  1642. // number of argument mismatched, we will let authentication fail and
  1643. // return error code.
  1644. //
  1645. ASSERT( argc == 2 );
  1646. if( argc >= 2 ) {
  1647. g_bstrCmdLineHelpSessionId = argv[1];
  1648. TRC_ALT((TB, L"Input Parameters 1 : %ws ", argv[1]));
  1649. }
  1650. //
  1651. // Initialize COM.
  1652. //
  1653. hr = CoInitialize(NULL);
  1654. if (!SUCCEEDED(hr)) {
  1655. result = E_FAIL;
  1656. TRC_ERR((TB, L"CoInitialize: %08X", hr));
  1657. goto CLEANUPANDEXIT;
  1658. }
  1659. //
  1660. // Get our process.
  1661. //
  1662. g_ProcHandle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE,
  1663. GetCurrentProcessId());
  1664. if (g_ProcHandle == NULL) {
  1665. result = GetLastError();
  1666. TRC_ERR((TB, L"OpenProcess: %08X", result));
  1667. goto CLEANUPANDEXIT;
  1668. }
  1669. //
  1670. // Get our process token.
  1671. //
  1672. if (!OpenProcessToken(g_ProcHandle, TOKEN_READ, &g_ProcToken)) {
  1673. result = GetLastError();
  1674. TRC_ERR((TB, L"OpenProcessToken: %08X", result));
  1675. goto CLEANUPANDEXIT;
  1676. }
  1677. //
  1678. // Get our session ID.
  1679. //
  1680. if (!GetTokenInformation(g_ProcToken, TokenSessionId,
  1681. &g_SessionID, sizeof(g_SessionID), &sz)) {
  1682. result = GetLastError();
  1683. TRC_ERR((TB, L"GetTokenInformation: %08X", result));
  1684. goto CLEANUPANDEXIT;
  1685. }
  1686. //
  1687. // Initialize the waitable object manager.
  1688. //
  1689. g_WaitObjMgr = WTBLOBJ_CreateWaitableObjectMgr();
  1690. if (g_WaitObjMgr == NULL) {
  1691. result = E_FAIL;
  1692. goto CLEANUPANDEXIT;
  1693. }
  1694. //
  1695. //initialize the timer, get the timer interval from registry or use default
  1696. //used for finding if the client (expert) is still connected
  1697. //
  1698. g_PrevTimer = GetTickCount();
  1699. if(!GetDwordFromRegistry(&g_dwTimeOutInterval))
  1700. g_dwTimeOutInterval = RDS_CHECKCONN_TIMEOUT;
  1701. else
  1702. g_dwTimeOutInterval *= 1000; //we need this in millisec
  1703. liDueTime.QuadPart = -1 * g_dwTimeOutInterval * 1000 * 100; //in one hundred nanoseconds
  1704. //
  1705. // Initiate the VC channel connection.
  1706. //
  1707. result = ConnectVC();
  1708. if (result != ERROR_SUCCESS) {
  1709. goto CLEANUPANDEXIT;
  1710. }
  1711. //
  1712. // This is an event the background thread can use to wake up the
  1713. // foreground thread in order to check state.
  1714. //
  1715. g_WakeUpForegroundThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  1716. if (g_WakeUpForegroundThreadEvent == NULL) {
  1717. TRC_ERR((TB, L"CreateEvent: %08X", GetLastError()));
  1718. result = E_FAIL;
  1719. goto CLEANUPANDEXIT;
  1720. }
  1721. result = WTBLOBJ_AddWaitableObject(
  1722. g_WaitObjMgr, NULL,
  1723. g_WakeUpForegroundThreadEvent,
  1724. WakeUpFunc
  1725. );
  1726. if (result != ERROR_SUCCESS) {
  1727. goto CLEANUPANDEXIT;
  1728. }
  1729. //
  1730. // Create the named pipe write complete event.
  1731. //
  1732. g_NamedPipeWriteEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  1733. if (g_NamedPipeWriteEvent == NULL) {
  1734. result = GetLastError();
  1735. TRC_ERR((TB, L"CreateEvent: %08X", result));
  1736. goto CLEANUPANDEXIT;
  1737. }
  1738. //
  1739. //start the timer event, ignore error. 0 in the registry means don't send any pings
  1740. //worst case, we don't get disconnected which is fine
  1741. //
  1742. if(g_dwTimeOutInterval)
  1743. SetWaitableTimer( g_ClientIsconnectEvent, &liDueTime, g_dwTimeOutInterval, NULL, NULL, FALSE );
  1744. //
  1745. // Create the shutdown event.
  1746. //
  1747. g_ShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  1748. if (g_ShutdownEvent == NULL) {
  1749. result = GetLastError();
  1750. TRC_ERR((TB, L"CreateEvent: %08X", result));
  1751. goto CLEANUPANDEXIT;
  1752. }
  1753. //
  1754. // Handle IO events until the shut down flag is set.
  1755. //
  1756. while (!g_Shutdown) {
  1757. result = WTBLOBJ_PollWaitableObjects(g_WaitObjMgr);
  1758. if (result != ERROR_SUCCESS) {
  1759. g_Shutdown = TRUE;
  1760. }
  1761. }
  1762. //
  1763. // Notify the client that we have disconnected, in case it hasn't
  1764. // figured it out yet.
  1765. //
  1766. if (g_VCFileHandle != NULL) {
  1767. REMOTEDESKTOP_CTL_DISCONNECT_PACKET msg;
  1768. memcpy(msg.packetHeader.channelName, REMOTEDESKTOP_RC_CONTROL_CHANNEL,
  1769. sizeof(REMOTEDESKTOP_RC_CONTROL_CHANNEL));
  1770. msg.packetHeader.channelBufHeader.channelNameLen =
  1771. REMOTEDESKTOP_RC_CHANNELNAME_LENGTH;
  1772. #ifdef USE_MAGICNO
  1773. msg.packetHeader.channelBufHeader.magicNo = CHANNELBUF_MAGICNO;
  1774. #endif
  1775. msg.packetHeader.channelBufHeader.dataLen =
  1776. sizeof(REMOTEDESKTOP_CTL_DISCONNECT_PACKET) -
  1777. sizeof(REMOTEDESKTOP_CTL_PACKETHEADER);
  1778. msg.msgHeader.msgType = REMOTEDESKTOP_CTL_DISCONNECT;
  1779. SendMsgToClient((PREMOTEDESKTOP_CHANNELBUFHEADER)&msg);
  1780. }
  1781. CLEANUPANDEXIT:
  1782. //
  1783. // Signal the shutdown event.
  1784. //
  1785. if (g_ShutdownEvent != NULL) {
  1786. SetEvent(g_ShutdownEvent);
  1787. }
  1788. //
  1789. // Wait for the background threads to exit.
  1790. //
  1791. if (g_RemoteControlDesktopThread != NULL) {
  1792. waitResult = WaitForSingleObject(
  1793. g_RemoteControlDesktopThread,
  1794. THREADSHUTDOWN_WAITTIMEOUT
  1795. );
  1796. if (waitResult == WAIT_OBJECT_0) {
  1797. backgroundThreadFailedToExit = TRUE;
  1798. TRC_ERR((TB, L"WaitForSingleObject g_RemoteControlDesktopThread: %ld",
  1799. waitResult));
  1800. }
  1801. }
  1802. if (g_NamedPipeReadThread != NULL) {
  1803. waitResult = WaitForSingleObject(
  1804. g_NamedPipeReadThread,
  1805. THREADSHUTDOWN_WAITTIMEOUT
  1806. );
  1807. if (waitResult == WAIT_OBJECT_0) {
  1808. backgroundThreadFailedToExit = TRUE;
  1809. TRC_ERR((TB, L"WaitForSingleObject g_NamedPipeReadThread: %ld", waitResult));
  1810. }
  1811. }
  1812. if (g_hHelpCenterProcess) {
  1813. CloseHandle(g_hHelpCenterProcess);
  1814. }
  1815. if( g_HelpSessionManager != NULL ) {
  1816. g_HelpSessionManager.Release();
  1817. }
  1818. if (g_WaitObjMgr != NULL) {
  1819. WTBLOBJ_DeleteWaitableObjectMgr(g_WaitObjMgr);
  1820. }
  1821. if (g_ProcHandle != NULL) {
  1822. CloseHandle(g_ProcHandle);
  1823. }
  1824. if (g_ClientIsconnectEvent != NULL) {
  1825. CloseHandle(g_ClientIsconnectEvent);
  1826. }
  1827. if (g_VCReadOverlapped.hEvent != NULL) {
  1828. CloseHandle(g_VCReadOverlapped.hEvent);
  1829. }
  1830. if (g_ClientSessionPipe != NULL) {
  1831. CloseHandle(g_ClientSessionPipe);
  1832. }
  1833. if (g_IncomingVCBuf.buffer != NULL) {
  1834. FREEMEM(g_IncomingVCBuf.buffer);
  1835. }
  1836. if (g_ShutdownEvent != NULL) {
  1837. CloseHandle(g_ShutdownEvent);
  1838. g_ShutdownEvent = NULL;
  1839. }
  1840. if (g_NamedPipeWriteEvent != NULL) {
  1841. CloseHandle(g_NamedPipeWriteEvent);
  1842. }
  1843. CoUninitialize();
  1844. DC_END_FN();
  1845. //
  1846. // If any of the background threads failed to exit then terminate
  1847. // the process.
  1848. //
  1849. if (backgroundThreadFailedToExit) {
  1850. ExitProcess(0);
  1851. }
  1852. return result;
  1853. }
  1854. DWORD
  1855. SendNullDataToClient(
  1856. )
  1857. /*++
  1858. Routine Description:
  1859. sends a null data packet to client.
  1860. Only purpose is to find out if the client is still connected; if not we exit the process
  1861. REMOTEDESKTOP_RC_CONTROL_CHANNEL channel REMOTEDESKTOP_CTL_RESULT message.
  1862. Arguments:
  1863. Return Value:
  1864. ERROR_SUCCESS on success. Otherwise, an error code is returned.
  1865. --*/
  1866. {
  1867. DC_BEGIN_FN("SendNullDataToClient");
  1868. DWORD result;
  1869. DWORD bytesWritten = 0;
  1870. REMOTEDESKTOP_CTL_ISCONNECTED_PACKET msg;
  1871. memcpy(msg.packetHeader.channelName, REMOTEDESKTOP_RC_CONTROL_CHANNEL,
  1872. sizeof(REMOTEDESKTOP_RC_CONTROL_CHANNEL));
  1873. msg.packetHeader.channelBufHeader.channelNameLen = REMOTEDESKTOP_RC_CHANNELNAME_LENGTH;
  1874. #ifdef USE_MAGICNO
  1875. msg.packetHeader.channelBufHeader.magicNo = CHANNELBUF_MAGICNO;
  1876. #endif
  1877. msg.packetHeader.channelBufHeader.dataLen = sizeof(REMOTEDESKTOP_CTL_ISCONNECTED_PACKET) -
  1878. sizeof(REMOTEDESKTOP_CTL_PACKETHEADER);
  1879. msg.msgHeader.msgType = REMOTEDESKTOP_CTL_ISCONNECTED;
  1880. result = SendMsgToClient((PREMOTEDESKTOP_CHANNELBUFHEADER )&msg);
  1881. //if we couldn't write all data to the client
  1882. //if we could write some data, assume it is still connected
  1883. //client probably disconnected
  1884. if(result != ERROR_SUCCESS)
  1885. result = SAFERROR_SESSIONNOTCONNECTED;
  1886. DC_END_FN();
  1887. return result;
  1888. }
  1889. BOOL GetDwordFromRegistry(PDWORD pdwValue)
  1890. {
  1891. BOOL fSuccess = FALSE;
  1892. HKEY hKey = NULL;
  1893. if( NULL == pdwValue )
  1894. return FALSE;
  1895. if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  1896. REG_CONTROL_SALEM,
  1897. 0,
  1898. KEY_READ,
  1899. &hKey
  1900. ) == ERROR_SUCCESS ) {
  1901. DWORD dwSize = sizeof(DWORD);
  1902. DWORD dwType;
  1903. if((RegQueryValueEx(hKey,
  1904. RDC_CONNCHECK_ENTRY,
  1905. NULL,
  1906. &dwType,
  1907. (PBYTE) pdwValue,
  1908. &dwSize
  1909. ) == ERROR_SUCCESS) && dwType == REG_DWORD ) {
  1910. //
  1911. //fall back to default
  1912. //
  1913. fSuccess = TRUE;
  1914. }
  1915. }
  1916. CLEANUPANDEXIT:
  1917. if(NULL != hKey )
  1918. RegCloseKey(hKey);
  1919. return fSuccess;
  1920. }