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.

3065 lines
72 KiB

  1. /*++
  2. Copyright (c) 1999-2000 Microsoft Corporation
  3. Module Name:
  4. TSRDPRemoteDesktopClient
  5. Abstract:
  6. This is the TS/RDP implementation of the Remote Desktop Client class.
  7. The Remote Desktop Client class hierarchy provides a pluggable C++
  8. interface for remote desktop access, by abstracting the implementation
  9. specific details of remote desktop access for the client-side
  10. The TSRDPRemoteDesktopClass implements remote-desktopping
  11. with the help of an instance of the MSTSC ActiveX client control.
  12. Author:
  13. Tad Brockway 02/00
  14. Revision History:
  15. --*/
  16. #include "stdafx.h"
  17. #ifdef TRC_FILE
  18. #undef TRC_FILE
  19. #endif
  20. #define TRC_FILE "_tsrdpc"
  21. #include "RDCHost.h"
  22. #include "TSRDPRemoteDesktopClient.h"
  23. #include <RemoteDesktopChannels.h>
  24. #include <mstsax_i.c>
  25. #include <TSRDPRemoteDesktop.h>
  26. #include "pchannel.h"
  27. #include <tsremdsk.h>
  28. #include <sessmgr.h>
  29. #include <sessmgr_i.c>
  30. #include <regapi.h>
  31. #include "parseaddr.h"
  32. #include "icshelpapi.h"
  33. #include <tsperf.h>
  34. #include "base64.h"
  35. #define ISRCSTATUSCODE(code) ((code) > SAFERROR_SHADOWEND_BASE)
  36. //
  37. // Variable to manage WinSock and ICS library startup/shutdown
  38. //
  39. LONG CTSRDPRemoteDesktopClient::gm_ListeningLibraryRefCount = 0; // Number of time that WinSock is intialized
  40. HRESULT
  41. CTSRDPRemoteDesktopClient::InitListeningLibrary()
  42. /*++
  43. Description:
  44. Function to initialize WinSock and ICS library for StartListen(), function add
  45. reference count to library if WinSock/ICS library already initialized.
  46. Parameters:
  47. None.
  48. Returns:
  49. S_OK or error code.
  50. --*/
  51. {
  52. WSADATA wsaData;
  53. WORD versionRequested;
  54. INT intRC;
  55. DWORD dwStatus;
  56. HRESULT hr = S_OK;
  57. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::InitListeningLibrary");
  58. // Our COM object is apartment-threaded model, need a critical section if
  59. // we switch to multi-threaded
  60. if( gm_ListeningLibraryRefCount == 0 )
  61. {
  62. //
  63. // Initialize WinSock.
  64. //
  65. versionRequested = MAKEWORD(1, 1);
  66. intRC = WSAStartup(versionRequested, &wsaData);
  67. if( intRC != 0 )
  68. {
  69. intRC = WSAGetLastError();
  70. TRC_ERR((TB, _T("WSAStartup failed %d"), intRC));
  71. TRC_ASSERT( (intRC == 0), (TB, _T("WSAStartup failed...\n")) );
  72. hr = HRESULT_FROM_WIN32( intRC );
  73. goto CLEANUPANDEXIT;
  74. }
  75. /************************************************************************/
  76. /* Now confirm that this WinSock supports version 1.1. Note that if */
  77. /* the DLL supports versions greater than 1.1 in addition to 1.1 then */
  78. /* it will still return 1.1 in the version information as that is the */
  79. /* version requested. */
  80. /************************************************************************/
  81. if ((LOBYTE(wsaData.wVersion) != 1) ||
  82. (HIBYTE(wsaData.wVersion) != 1))
  83. {
  84. /********************************************************************/
  85. /* Oops - this WinSock doesn't support version 1.1. */
  86. /********************************************************************/
  87. TRC_ERR((TB, _T("WinSock doesn't support version 1.1")));
  88. WSACleanup();
  89. hr = HRESULT_FROM_WIN32( WSAVERNOTSUPPORTED );
  90. goto CLEANUPANDEXIT;
  91. }
  92. //
  93. // Initialize ICS library.
  94. //
  95. dwStatus = StartICSLib();
  96. if( ERROR_SUCCESS != dwStatus )
  97. {
  98. // Shutdown WinSock so that we have a matching WSAStatup() and StartICSLib().
  99. WSACleanup();
  100. hr = HRESULT_FROM_WIN32( dwStatus );
  101. TRC_ERR((TB, _T("StartICSLib() failed with %d"), dwStatus));
  102. TRC_ASSERT( (ERROR_SUCCESS == dwStatus), (TB, _T("StartICSLib() failed...\n")) );
  103. goto CLEANUPANDEXIT;
  104. }
  105. }
  106. InterlockedIncrement( &gm_ListeningLibraryRefCount );
  107. CLEANUPANDEXIT:
  108. DC_END_FN();
  109. return hr;
  110. }
  111. HRESULT
  112. CTSRDPRemoteDesktopClient::TerminateListeningLibrary()
  113. /*++
  114. Description:
  115. Function to shutdown ICS libaray and WinSock, decrement reference count
  116. if more than one object is referencing WinSock/ICS library.
  117. Parameters:
  118. None.
  119. Returns:
  120. S_OK or error code
  121. Note:
  122. Not multi-thread safe, need CRITICAL_SECTION if we switch to multi-threaded
  123. model.
  124. --*/
  125. {
  126. HRESULT hr = S_OK;
  127. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::TerminateListeningLibrary");
  128. ASSERT( gm_ListeningLibraryRefCount > 0 );
  129. if( gm_ListeningLibraryRefCount <= 0 )
  130. {
  131. TRC_ERR((TB, _T("TerminateListeningLibrary() called before InitListeningLibrary()")));
  132. hr = HRESULT_FROM_WIN32(WSANOTINITIALISED);
  133. goto CLEANUPANDEXIT;
  134. }
  135. if( 0 == InterlockedDecrement( &gm_ListeningLibraryRefCount ) )
  136. {
  137. // Stop ICS libray.
  138. StopICSLib();
  139. // Shutdown WinSock
  140. WSACleanup();
  141. }
  142. CLEANUPANDEXIT:
  143. DC_END_FN();
  144. return hr;
  145. }
  146. ///////////////////////////////////////////////////////
  147. //
  148. // CMSTSCClientEventSink Methods
  149. //
  150. CMSTSCClientEventSink::~CMSTSCClientEventSink()
  151. {
  152. DC_BEGIN_FN("CMSTSCClientEventSink::~CMSTSCClientEventSink");
  153. if (m_Obj) {
  154. ASSERT(m_Obj->IsValid());
  155. }
  156. DC_END_FN();
  157. }
  158. //
  159. // Event Sinks
  160. //
  161. HRESULT __stdcall
  162. CMSTSCClientEventSink::OnRDPConnected()
  163. {
  164. m_Obj->OnRDPConnected();
  165. return S_OK;
  166. }
  167. HRESULT __stdcall
  168. CMSTSCClientEventSink::OnLoginComplete()
  169. {
  170. m_Obj->OnLoginComplete();
  171. return S_OK;
  172. }
  173. HRESULT __stdcall
  174. CMSTSCClientEventSink::OnDisconnected(
  175. long disconReason
  176. )
  177. {
  178. m_Obj->OnDisconnected(disconReason);
  179. return S_OK;
  180. }
  181. void __stdcall CMSTSCClientEventSink::OnReceiveData(
  182. BSTR chanName,
  183. BSTR data
  184. )
  185. {
  186. m_Obj->OnMSTSCReceiveData(data);
  187. }
  188. void __stdcall CMSTSCClientEventSink::OnReceivedTSPublicKey(
  189. BSTR publicKey,
  190. VARIANT_BOOL* pfbContinueLogon
  191. )
  192. {
  193. m_Obj->OnReceivedTSPublicKey(publicKey, pfbContinueLogon);
  194. }
  195. ///////////////////////////////////////////////////////
  196. //
  197. // CCtlChannelEventSink Methods
  198. //
  199. CCtlChannelEventSink::~CCtlChannelEventSink()
  200. {
  201. DC_BEGIN_FN("CCtlChannelEventSink::~CCtlChannelEventSink");
  202. if (m_Obj) {
  203. ASSERT(m_Obj->IsValid());
  204. }
  205. DC_END_FN();
  206. }
  207. //
  208. // Event Sinks
  209. //
  210. void __stdcall
  211. CCtlChannelEventSink::DataReady(BSTR channelName)
  212. {
  213. m_Obj->HandleControlChannelMsg();
  214. }
  215. ///////////////////////////////////////////////////////
  216. //
  217. // CTSRDPRemoteDesktopClient Methods
  218. //
  219. HRESULT
  220. CTSRDPRemoteDesktopClient::FinalConstruct()
  221. {
  222. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::FinalConstruct");
  223. HRESULT hr = S_OK;
  224. if (!AtlAxWinInit()) {
  225. TRC_ERR((TB, L"AtlAxWinInit failed."));
  226. hr = E_FAIL;
  227. }
  228. DC_END_FN();
  229. return hr;
  230. }
  231. CTSRDPRemoteDesktopClient::~CTSRDPRemoteDesktopClient()
  232. /*++
  233. Routine Description:
  234. The Destructor
  235. Arguments:
  236. Return Value:
  237. --*/
  238. {
  239. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::~CTSRDPRemoteDesktopClient");
  240. if (m_ChannelMgr) {
  241. m_CtlChannelEventSink.DispEventUnadvise(m_CtlChannel);
  242. }
  243. if (m_TSClient != NULL) {
  244. m_TSClient->Release();
  245. m_TSClient = NULL;
  246. }
  247. if( m_TimerId > 0 ) {
  248. KillTimer( m_TimerId );
  249. }
  250. ListenConnectCleanup();
  251. if( m_InitListeningLibrary )
  252. {
  253. // Dereference listening library.
  254. TerminateListeningLibrary();
  255. }
  256. DC_END_FN();
  257. }
  258. HRESULT
  259. CTSRDPRemoteDesktopClient::Initialize(
  260. LPCREATESTRUCT pCreateStruct
  261. )
  262. /*++
  263. Routine Description:
  264. Final Initialization
  265. Arguments:
  266. pCreateStruct - WM_CREATE, create struct.
  267. Return Value:
  268. S_OK on success. Otherwise, an error code is returned.
  269. --*/
  270. {
  271. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::Initialize");
  272. RECT rcClient = { 0, 0, pCreateStruct->cx, pCreateStruct->cy };
  273. HRESULT hr;
  274. IUnknown *pUnk = NULL;
  275. DWORD result;
  276. IMsRdpClientAdvancedSettings2 *advancedSettings;
  277. CComBSTR bstr;
  278. HKEY hKey = NULL;
  279. HRESULT hrIgnore;
  280. ASSERT(!m_Initialized);
  281. //
  282. // Create the client Window.
  283. //
  284. m_TSClientWnd = m_TSClientAxView.Create(
  285. m_hWnd, rcClient, MSTSCAX_TEXTGUID,
  286. WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN, 0
  287. );
  288. if (m_TSClientWnd == NULL) {
  289. hr = HRESULT_FROM_WIN32(GetLastError());
  290. TRC_ERR((TB, L"Window Create: %08X", GetLastError()));
  291. goto CLEANUPANDEXIT;
  292. }
  293. //
  294. // Get IUnknown
  295. //
  296. hr = AtlAxGetControl(m_TSClientWnd, &pUnk);
  297. if (!SUCCEEDED(hr)) {
  298. TRC_ERR((TB, L"AtlAxGetControl: %08X", hr));
  299. pUnk = NULL;
  300. goto CLEANUPANDEXIT;
  301. }
  302. //
  303. // Initialize the event sink.
  304. //
  305. m_TSClientEventSink.m_Obj = this;
  306. //
  307. // Add the event sink.
  308. //
  309. hr = m_TSClientEventSink.DispEventAdvise(pUnk);
  310. if (!SUCCEEDED(hr)) {
  311. TRC_ERR((TB, L"DispEventAdvise: %08X", hr));
  312. goto CLEANUPANDEXIT;
  313. }
  314. //
  315. // Get the control.
  316. //
  317. hr = pUnk->QueryInterface(__uuidof(IMsRdpClient2), (void**)&m_TSClient);
  318. if (!SUCCEEDED(hr)) {
  319. TRC_ERR((TB, L"QueryInterface: %08X", hr));
  320. goto CLEANUPANDEXIT;
  321. }
  322. //
  323. // Specify that the MSTSC input handler window should accept background
  324. // events.
  325. //
  326. hr = m_TSClient->get_AdvancedSettings3(&advancedSettings);
  327. if (!SUCCEEDED(hr)) {
  328. TRC_ERR((TB, L"IMsTscAdvancedSettings: %08X", hr));
  329. goto CLEANUPANDEXIT;
  330. }
  331. hr = advancedSettings->put_allowBackgroundInput(1);
  332. //
  333. // Disable autoreconnect it doesn't apply to Salem
  334. //
  335. hr = advancedSettings->put_EnableAutoReconnect(VARIANT_FALSE);
  336. if (!SUCCEEDED(hr)) {
  337. TRC_ERR((TB, L"put_EnableAutoReconnect: %08X", hr));
  338. result = E_FAIL;
  339. goto CLEANUPANDEXIT;
  340. }
  341. //
  342. // Disable advanced desktop features for the help session.
  343. // An error here is not critical, so we ignore it.
  344. //
  345. LONG flags = TS_PERF_DISABLE_WALLPAPER | TS_PERF_DISABLE_THEMING;
  346. hrIgnore = advancedSettings->put_PerformanceFlags(flags);
  347. if (!SUCCEEDED(hrIgnore)) {
  348. TRC_ERR((TB, L"put_PerformanceFlags: %08X", hrIgnore));
  349. }
  350. //
  351. // Disable CTRL_ALT_BREAK, ignore error
  352. //
  353. hrIgnore = advancedSettings->put_HotKeyFullScreen(0);
  354. if (!SUCCEEDED(hrIgnore)) {
  355. TRC_ERR((TB, L"put_HotKeyFullScreen: %08X", hrIgnore));
  356. }
  357. //
  358. // Don't allow mstscax to grab input focus on connect. Ignore error
  359. // on failure.
  360. //
  361. hrIgnore = advancedSettings->put_GrabFocusOnConnect(FALSE);
  362. if (!SUCCEEDED(hrIgnore)) {
  363. TRC_ERR((TB, L"put_HotKeyFullScreen: %08X", hrIgnore));
  364. }
  365. advancedSettings->Release();
  366. if (!SUCCEEDED(hr)) {
  367. TRC_ERR((TB, L"put_allowBackgroundInput: %08X", hr));
  368. goto CLEANUPANDEXIT;
  369. }
  370. //
  371. // Create the "remote desktop" virtual channel with the TS Client.
  372. //
  373. bstr = TSRDPREMOTEDESKTOP_VC_CHANNEL;
  374. hr = m_TSClient->CreateVirtualChannels(bstr);
  375. if (!SUCCEEDED(hr)) {
  376. TRC_ERR((TB, L"CreateVirtualChannels: %08X", hr));
  377. result = E_FAIL;
  378. goto CLEANUPANDEXIT;
  379. }
  380. //
  381. // Set the Shadow Persistent option
  382. //
  383. hr = m_TSClient->SetVirtualChannelOptions(bstr, CHANNEL_OPTION_REMOTE_CONTROL_PERSISTENT);
  384. if (!SUCCEEDED(hr)) {
  385. TRC_ERR((TB, L"SetVirtualChannelOptions: %08X", hr));
  386. result = E_FAIL;
  387. goto CLEANUPANDEXIT;
  388. }
  389. //initialize timer-related stuff
  390. m_PrevTimer = GetTickCount();
  391. //
  392. //get the time interval for pings from the registry
  393. //
  394. if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  395. REG_CONTROL_SALEM,
  396. 0,
  397. KEY_READ,
  398. &hKey
  399. ) == ERROR_SUCCESS ) {
  400. DWORD dwSize = sizeof(DWORD);
  401. DWORD dwType;
  402. if((RegQueryValueEx(hKey,
  403. RDC_CONNCHECK_ENTRY,
  404. NULL,
  405. &dwType,
  406. (PBYTE) &m_RdcConnCheckTimeInterval,
  407. &dwSize
  408. ) == ERROR_SUCCESS) && dwType == REG_DWORD ) {
  409. m_RdcConnCheckTimeInterval *= 1000; //we need this in millisecs
  410. }
  411. else
  412. {
  413. //
  414. //fall back to default, if reg lookup failed
  415. //
  416. m_RdcConnCheckTimeInterval = RDC_CHECKCONN_TIMEOUT;
  417. }
  418. }
  419. CLEANUPANDEXIT:
  420. if(NULL != hKey )
  421. RegCloseKey(hKey);
  422. //
  423. // m_TSClient keeps our reference to the client object until
  424. // the destructor is called.
  425. //
  426. if (pUnk != NULL) {
  427. pUnk->Release();
  428. }
  429. SetValid(SUCCEEDED(hr));
  430. DC_END_FN();
  431. return hr;
  432. }
  433. STDMETHODIMP
  434. CTSRDPRemoteDesktopClient::SendData(
  435. BSTR data
  436. )
  437. /*++
  438. Routine Description:
  439. IDataChannelIO Data Channel Send Method
  440. Arguments:
  441. data - Data to send.
  442. Return Value:
  443. S_OK on success. Otherwise, an error code is returned.
  444. --*/
  445. {
  446. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::SendData");
  447. CComBSTR channelName;
  448. HRESULT hr;
  449. ASSERT(IsValid());
  450. channelName = TSRDPREMOTEDESKTOP_VC_CHANNEL;
  451. hr = m_TSClient->SendOnVirtualChannel(
  452. channelName,
  453. (BSTR)data
  454. );
  455. if (!SUCCEEDED(hr)) {
  456. TRC_ERR((TB, L"SendOnVirtualChannel: %08X", hr));
  457. }
  458. //
  459. //update timer
  460. //
  461. m_PrevTimer = GetTickCount();
  462. DC_END_FN();
  463. return hr;
  464. }
  465. STDMETHODIMP
  466. CTSRDPRemoteDesktopClient::put_EnableSmartSizing(
  467. BOOL val
  468. )
  469. /*++
  470. Routine Description:
  471. Enable/Disable Smart Sizing
  472. Arguments:
  473. val - TRUE for enable. FALSE, otherwise.
  474. Return Value:
  475. S_OK on success. Otherwise, an error code is returned.
  476. --*/
  477. {
  478. HRESULT hr;
  479. IMsRdpClientAdvancedSettings *pAdvSettings = NULL;
  480. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::put_EnableSmartSizing");
  481. if (!IsValid()) {
  482. ASSERT(FALSE);
  483. hr = E_FAIL;
  484. goto CLEANUPANDEXIT;
  485. }
  486. hr = m_TSClient->get_AdvancedSettings2(&pAdvSettings);
  487. if (hr != S_OK) {
  488. TRC_ERR((TB, L"get_AdvancedSettings2: %08X", hr));
  489. goto CLEANUPANDEXIT;
  490. }
  491. hr = pAdvSettings->put_SmartSizing(val ? VARIANT_TRUE : VARIANT_FALSE);
  492. pAdvSettings->Release();
  493. CLEANUPANDEXIT:
  494. DC_END_FN();
  495. return hr;
  496. }
  497. STDMETHODIMP
  498. CTSRDPRemoteDesktopClient::get_EnableSmartSizing(
  499. BOOL *pVal
  500. )
  501. /*++
  502. Routine Description:
  503. Enable/Disable Smart Sizing
  504. Arguments:
  505. val - TRUE for enable. FALSE, otherwise.
  506. Return Value:
  507. S_OK on success. Otherwise, an error code is returned.
  508. --*/
  509. {
  510. HRESULT hr;
  511. VARIANT_BOOL vb;
  512. IMsRdpClientAdvancedSettings *pAdvSettings = NULL;
  513. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::put_EnableSmartSizing");
  514. if (!IsValid()) {
  515. ASSERT(FALSE);
  516. hr = E_FAIL;
  517. goto CLEANUPANDEXIT;
  518. }
  519. hr = m_TSClient->get_AdvancedSettings2(&pAdvSettings);
  520. if (hr != S_OK) {
  521. TRC_ERR((TB, L"get_AdvancedSettings2: %08X", hr));
  522. goto CLEANUPANDEXIT;
  523. }
  524. hr = pAdvSettings->get_SmartSizing(&vb);
  525. *pVal = (vb != 0);
  526. pAdvSettings->Release();
  527. CLEANUPANDEXIT:
  528. DC_END_FN();
  529. return hr;
  530. }
  531. STDMETHODIMP
  532. CTSRDPRemoteDesktopClient::put_ChannelMgr(
  533. ISAFRemoteDesktopChannelMgr *newVal
  534. )
  535. /*++
  536. Routine Description:
  537. Assign the data channel manager interface.
  538. Arguments:
  539. newVal - Data Channel Manager
  540. Return Value:
  541. S_OK on success. Otherwise, an error code is returned.
  542. --*/
  543. {
  544. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::put_ChannelMgr");
  545. HRESULT hr = S_OK;
  546. //
  547. // We should get called one time.
  548. //
  549. ASSERT(m_ChannelMgr == NULL);
  550. m_ChannelMgr = newVal;
  551. //
  552. // Register the Remote Desktop control channel
  553. //
  554. hr = m_ChannelMgr->OpenDataChannel(
  555. REMOTEDESKTOP_RC_CONTROL_CHANNEL, &m_CtlChannel
  556. );
  557. if (!SUCCEEDED(hr)) {
  558. goto CLEANUPANDEXIT;
  559. }
  560. //
  561. // Register an event sink with the channel manager.
  562. //
  563. m_CtlChannelEventSink.m_Obj = this;
  564. //
  565. // Add the event sink.
  566. //
  567. hr = m_CtlChannelEventSink.DispEventAdvise(m_CtlChannel);
  568. if (!SUCCEEDED(hr)) {
  569. TRC_ERR((TB, L"DispEventAdvise: %08X", hr));
  570. }
  571. CLEANUPANDEXIT:
  572. return hr;
  573. }
  574. HRESULT
  575. CTSRDPRemoteDesktopClient::ConnectServerWithOpenedSocket()
  576. /*++
  577. Routine Description:
  578. Connects the client component to the server-side Remote Desktop Host COM
  579. Object with already opened socket.
  580. Arguments:
  581. None.
  582. Returns:
  583. S_OK or error code
  584. --*/
  585. {
  586. HRESULT hr = S_OK;
  587. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::ConnectServerWithSocket");
  588. IMsRdpClientAdvancedSettings* ptsAdvSettings = NULL;
  589. TRC_NRM((TB, L"ConnectServerWithOpenedSocket"));
  590. ASSERT( INVALID_SOCKET != m_TSConnectSocket );
  591. //
  592. // Direct the MSTSCAX control to connect.
  593. //
  594. hr = m_TSClient->put_Server( m_ConnectedServer );
  595. if (!SUCCEEDED(hr)) {
  596. TRC_ERR((TB, L"put_Server: %ld", hr));
  597. goto CLEANUPANDEXIT;
  598. }
  599. hr = m_TSClient->get_AdvancedSettings2( &ptsAdvSettings );
  600. if( SUCCEEDED(hr) && ptsAdvSettings ) {
  601. VARIANT var;
  602. VariantClear(&var);
  603. var.vt = VT_BYREF;
  604. var.byref = (PVOID)m_TSConnectSocket;
  605. hr = ptsAdvSettings->put_ConnectWithEndpoint( &var );
  606. if( FAILED(hr) ) {
  607. TRC_ERR((TB, _T("put_ConnectWithEndpoint failed - GLE:%x"), hr));
  608. }
  609. VariantClear(&var);
  610. ptsAdvSettings->Release();
  611. }
  612. if( FAILED(hr) ) {
  613. goto CLEANUPANDEXIT;
  614. }
  615. //
  616. // mstscax owns this socket and will close it
  617. //
  618. m_TSConnectSocket = INVALID_SOCKET;
  619. hr = m_TSClient->Connect();
  620. if( FAILED(hr) ) {
  621. TRC_ERR((TB, L"Connect: 0x%08x", hr));
  622. goto CLEANUPANDEXIT;
  623. }
  624. CLEANUPANDEXIT:
  625. DC_END_FN();
  626. return hr;
  627. }
  628. HRESULT
  629. CTSRDPRemoteDesktopClient::ConnectServerPort(
  630. BSTR bstrServer,
  631. LONG portNumber
  632. )
  633. /*++
  634. Routine Description:
  635. Connects the client component to the server-side Remote Desktop Host COM
  636. Object with specific port number
  637. Arguments:
  638. bstrServer : Name or IP address of server.
  639. portNumber : optional port number.
  640. Return Value:
  641. S_OK on success. Otherwise, an error code is returned.
  642. --*/
  643. {
  644. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::ConnectServerPort");
  645. HRESULT hr;
  646. IMsRdpClientAdvancedSettings* ptsAdvSettings = NULL;
  647. TRC_NRM((TB, L"ConnectServerPort %s %d", bstrServer, portNumber));
  648. //
  649. // Direct the MSTSCAX control to connect.
  650. //
  651. hr = m_TSClient->put_Server( bstrServer );
  652. if (!SUCCEEDED(hr)) {
  653. TRC_ERR((TB, L"put_Server: %ld", hr));
  654. goto CLEANUPANDEXIT;
  655. }
  656. hr = m_TSClient->get_AdvancedSettings2( &ptsAdvSettings );
  657. if( SUCCEEDED(hr) && ptsAdvSettings ) {
  658. //
  659. // Previous ConnectServerPort() might have set this port number
  660. // other than 3389
  661. //
  662. hr = ptsAdvSettings->put_RDPPort(
  663. (0 != portNumber) ? portNumber : TERMSRV_TCPPORT
  664. );
  665. if (FAILED(hr) ) {
  666. TRC_ERR((TB, L"put_RDPPort failed: 0x%08x", hr));
  667. }
  668. ptsAdvSettings->Release();
  669. }
  670. else {
  671. TRC_ERR((TB, L"get_AdvancedSettings2 failed: 0x%08x", hr));
  672. }
  673. //
  674. // Failed the connection if we can't set the port number
  675. //
  676. if( FAILED(hr) )
  677. {
  678. goto CLEANUPANDEXIT;
  679. }
  680. m_ConnectedServer = bstrServer;
  681. m_ConnectedPort = (0 != portNumber) ? portNumber : TERMSRV_TCPPORT;
  682. hr = m_TSClient->Connect();
  683. if( FAILED(hr) ) {
  684. TRC_ERR((TB, L"Connect: 0x%08x", hr));
  685. }
  686. CLEANUPANDEXIT:
  687. DC_END_FN();
  688. return hr;
  689. }
  690. HRESULT
  691. CTSRDPRemoteDesktopClient::SetupConnectionInfo(
  692. BOOL bListenConnectInfo,
  693. BSTR bstrExpertBlob
  694. )
  695. /*++
  696. Routine Description:
  697. Connects the client component to the server-side Remote Desktop Host COM
  698. Object.
  699. Arguments:
  700. bstrExpertBlob : Optional parameter to be transmitted to SAF resolver.
  701. Return Value:
  702. S_OK on success. Otherwise, an error code is returned.
  703. --*/
  704. {
  705. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::SetupConnectionInfo");
  706. HRESULT hr = S_OK;
  707. DWORD result;
  708. DWORD protocolType;
  709. IMsTscNonScriptable* ptsns = NULL;
  710. IMsRdpClientAdvancedSettings* ptsAdvSettings = NULL;
  711. IMsRdpClientSecuredSettings* ptsSecuredSettings = NULL;
  712. CComBSTR bstrAssistantAccount;
  713. CComBSTR bstrAccountDomainName;
  714. CComBSTR machineAddressList;
  715. VARIANT_BOOL bNotifyTSPublicKey;
  716. //
  717. // Parse the connection parameters.
  718. //
  719. result = ParseConnectParmsString(
  720. m_ConnectParms,
  721. &m_ConnectParmVersion,
  722. &protocolType,
  723. machineAddressList,
  724. bstrAssistantAccount,
  725. m_AssistantAccountPwd,
  726. m_HelpSessionID,
  727. m_HelpSessionName,
  728. m_HelpSessionPwd,
  729. m_TSSecurityBlob
  730. );
  731. if (result != ERROR_SUCCESS) {
  732. hr = HRESULT_FROM_WIN32(result);
  733. goto CLEANUPANDEXIT;
  734. }
  735. //
  736. // If the protocol type doesn't match, then fail.
  737. //
  738. if (protocolType != REMOTEDESKTOP_TSRDP_PROTOCOL) {
  739. TRC_ERR((TB, L"Invalid connection protocol %ld", protocolType));
  740. hr = HRESULT_FROM_WIN32(ERROR_INVALID_USER_BUFFER);
  741. goto CLEANUPANDEXIT;
  742. }
  743. if (bListenConnectInfo) {
  744. m_ServerAddressList.clear();
  745. }
  746. else {
  747. //
  748. // Parse address list in connect parm.
  749. //
  750. result = ParseAddressList( machineAddressList, m_ServerAddressList );
  751. if( ERROR_SUCCESS != result ) {
  752. TRC_ERR((TB, L"Invalid address list 0x%08x", result));
  753. hr = HRESULT_FROM_WIN32(result);
  754. goto CLEANUPANDEXIT;
  755. }
  756. if( 0 == m_ServerAddressList.size() ) {
  757. TRC_ERR((TB, L"Invalid connection address list"));
  758. hr = HRESULT_FROM_WIN32(ERROR_INVALID_USER_BUFFER);
  759. goto CLEANUPANDEXIT;
  760. }
  761. }
  762. hr = m_TSClient->put_UserName(SALEMHELPASSISTANTACCOUNT_NAME);
  763. if (!SUCCEEDED(hr)) {
  764. TRC_ERR((TB, L"put_UserName: %ld", hr));
  765. goto CLEANUPANDEXIT;
  766. }
  767. hr = m_TSClient->get_AdvancedSettings2( &ptsAdvSettings );
  768. if( SUCCEEDED(hr) && ptsAdvSettings ) {
  769. hr = ptsAdvSettings->put_DisableRdpdr( TRUE );
  770. if (FAILED(hr) ) {
  771. TRC_ERR((TB, L"put_DisableRdpdr failed: 0x%08x", hr));
  772. }
  773. // older version does not have security blob so don't notify us about
  774. // TS public key.
  775. bNotifyTSPublicKey = (VARIANT_BOOL)(m_ConnectParmVersion >= SALEM_CONNECTPARM_SECURITYBLOB_VERSION);
  776. // tell activeX control to notify us TS public key
  777. hr = ptsAdvSettings->put_NotifyTSPublicKey(bNotifyTSPublicKey);
  778. if (FAILED(hr) ) {
  779. TRC_ERR((TB, L"put_NotifyTSPublicKey failed: 0x%08x", hr));
  780. goto CLEANUPANDEXIT;
  781. }
  782. ptsAdvSettings->Release();
  783. }
  784. else {
  785. TRC_ERR((TB, L"QueryInterface IID_IMsRdpClientAdvancedSettings: %ld", hr));
  786. }
  787. //
  788. // Setting connection timeout, ICS might take sometime to routine
  789. // opened port to actual TS server, neither is critical error.
  790. //
  791. hr = ptsAdvSettings->put_singleConnectionTimeout( 60 * 2 ); // try two mins timeout
  792. if( FAILED(hr) ) {
  793. TRC_ERR((TB, L"put_singleConnectionTimeout : 0x%x", hr));
  794. }
  795. hr = ptsAdvSettings->put_overallConnectionTimeout( 60 * 2 );
  796. if( FAILED(hr) ) {
  797. TRC_ERR((TB, L"put_overallConnectionTimeout : 0x%x", hr));
  798. }
  799. // Password encryption is based on encyption cycle key + help session ID
  800. hr = m_TSClient->get_SecuredSettings2( &ptsSecuredSettings );
  801. if( FAILED(hr) || !ptsSecuredSettings ) {
  802. TRC_ERR((TB, L"get_IMsTscSecuredSettings : 0x%08x", hr));
  803. goto CLEANUPANDEXIT;
  804. }
  805. //
  806. // TermSrv invoke sessmgr to check if help session is valid
  807. // before kicking off rdsaddin.exe, we need to send over
  808. // help session ID and password, only place available and big
  809. // enough is on WorkDir and StartProgram property, TermSrv will
  810. // ignore these and fill appropriate value for it
  811. //
  812. hr = ptsSecuredSettings->put_WorkDir( m_HelpSessionID );
  813. if( FAILED(hr) ) {
  814. TRC_ERR((TB, L"put_WorkDir: 0x%08x", hr));
  815. goto CLEANUPANDEXIT;
  816. }
  817. hr = ptsSecuredSettings->put_StartProgram( m_HelpSessionPwd );
  818. if( FAILED(hr) ) {
  819. TRC_ERR((TB, L"put_StartProgram: 0x%08x", hr));
  820. goto CLEANUPANDEXIT;
  821. }
  822. ptsSecuredSettings->Release();
  823. // we only use this to disable redirection, not a critical
  824. // error, just ugly
  825. hr = m_TSClient->QueryInterface(IID_IMsTscNonScriptable,
  826. (void**)&ptsns);
  827. if(!SUCCEEDED(hr) || !ptsns){
  828. TRC_ERR((TB, L"QueryInterface IID_IMsTscNonScriptable: %ld", hr));
  829. goto CLEANUPANDEXIT;
  830. }
  831. // password in workdir, stuff something into password field
  832. hr = ptsns->put_ClearTextPassword( m_AssistantAccountPwd );
  833. if (!SUCCEEDED(hr)) {
  834. TRC_ERR((TB, L"put_ClearTextPassword: 0x%08x", hr));
  835. goto CLEANUPANDEXIT;
  836. }
  837. m_ExpertBlob = bstrExpertBlob;
  838. //
  839. // Instruct mstscax to connect with certain screen resolution,
  840. // mstscax will default to 200x20 (???), min. is VGA size.
  841. //
  842. {
  843. RECT rect;
  844. LONG cx;
  845. LONG cy;
  846. GetClientRect(&rect);
  847. cx = rect.right - rect.left;
  848. cy = rect.bottom - rect.top;
  849. if( cx < 640 || cy < 480 )
  850. {
  851. cx = 640;
  852. cy = 480;
  853. }
  854. m_TSClient->put_DesktopWidth(cx);
  855. m_TSClient->put_DesktopHeight(cy);
  856. }
  857. CLEANUPANDEXIT:
  858. if(ptsns)
  859. {
  860. ptsns->Release();
  861. ptsns = NULL;
  862. }
  863. DC_END_FN();
  864. return hr;
  865. }
  866. STDMETHODIMP
  867. CTSRDPRemoteDesktopClient::AcceptListenConnection(
  868. BSTR bstrExpertBlob
  869. )
  870. /*++
  871. Routine Description:
  872. Establish reverse connection with TS server, TS server must be connected
  873. wia reverse connection.
  874. Parameters:
  875. bstrExpertBlob : Same as ConnectToServer().
  876. Returns:
  877. S_OK or error code.
  878. --*/
  879. {
  880. HRESULT hr = S_OK;
  881. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::AcceptListenConnection");
  882. //
  883. // If we are already connected or not valid, then just
  884. // return.
  885. //
  886. if (!IsValid()) {
  887. ASSERT(FALSE);
  888. hr = E_FAIL;
  889. goto CLEANUPANDEXIT;
  890. }
  891. if (m_ConnectedToServer || m_ConnectionInProgress) {
  892. TRC_ERR((TB, L"Connection active"));
  893. hr = HRESULT_FROM_WIN32(ERROR_CONNECTION_ACTIVE);
  894. goto CLEANUPANDEXIT;
  895. }
  896. if( !ListenConnectInProgress() ) {
  897. TRC_ERR((TB, L"Connection in-active"));
  898. hr = HRESULT_FROM_WIN32(ERROR_CONNECTION_INVALID);
  899. goto CLEANUPANDEXIT;
  900. }
  901. if( INVALID_SOCKET == m_TSConnectSocket ) {
  902. TRC_ERR((TB, L"Socket is not connected"));
  903. hr = HRESULT_FROM_WIN32(ERROR_CONNECTION_INVALID);
  904. goto CLEANUPANDEXIT;
  905. }
  906. hr = SetupConnectionInfo(TRUE, bstrExpertBlob);
  907. if( FAILED(hr) ) {
  908. TRC_ERR((TB, L"SetupConnectionInfo() failed with 0x%08x", hr));
  909. goto CLEANUPANDEXIT;
  910. }
  911. hr = ConnectServerWithOpenedSocket();
  912. CLEANUPANDEXIT:
  913. m_ConnectionInProgress = SUCCEEDED(hr);
  914. DC_END_FN();
  915. return hr;
  916. }
  917. STDMETHODIMP
  918. CTSRDPRemoteDesktopClient::ConnectToServer(BSTR bstrExpertBlob)
  919. /*++
  920. Routine Description:
  921. Connects the client component to the server-side Remote Desktop Host COM
  922. Object.
  923. Arguments:
  924. bstrExpertBlob : Optional parameter to be transmitted to SAF resolver.
  925. Return Value:
  926. S_OK on success. Otherwise, an error code is returned.
  927. Params--*/
  928. {
  929. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::ConnectToServer");
  930. HRESULT hr = S_OK;
  931. ServerAddress address;
  932. //
  933. // If we are already connected or not valid, then just
  934. // return.
  935. //
  936. if (!IsValid()) {
  937. ASSERT(FALSE);
  938. hr = E_FAIL;
  939. goto CLEANUPANDEXIT;
  940. }
  941. if (m_ConnectedToServer || m_ConnectionInProgress) {
  942. TRC_ERR((TB, L"Connection active"));
  943. hr = HRESULT_FROM_WIN32(ERROR_CONNECTION_ACTIVE);
  944. goto CLEANUPANDEXIT;
  945. }
  946. hr = SetupConnectionInfo(FALSE, bstrExpertBlob);
  947. address = m_ServerAddressList.front();
  948. m_ServerAddressList.pop_front();
  949. hr = ConnectServerPort(address.ServerName, address.portNumber);
  950. if (FAILED(hr)) {
  951. TRC_ERR((TB, L"ConnectServerPort: %08X", hr));
  952. }
  953. CLEANUPANDEXIT:
  954. //
  955. // If we succeeded, remember that we are in a state of connecting.
  956. //
  957. m_ConnectionInProgress = SUCCEEDED(hr);
  958. DC_END_FN();
  959. return hr;
  960. }
  961. STDMETHODIMP
  962. CTSRDPRemoteDesktopClient::DisconnectFromServer()
  963. /*++
  964. Routine Description:
  965. Disconnects the client from the server to which we are currently
  966. connected.
  967. Arguments:
  968. Return Value:
  969. S_OK on success. Otherwise, an error code is returned.
  970. --*/
  971. {
  972. return DisconnectFromServerInternal(
  973. SAFERROR_LOCALNOTERROR
  974. );
  975. }
  976. STDMETHODIMP
  977. CTSRDPRemoteDesktopClient::DisconnectFromServerInternal(
  978. LONG errorCode
  979. )
  980. /*++
  981. Routine Description:
  982. Disconnects the client from the server to which we are currently
  983. connected.
  984. Arguments:
  985. reason - Reason for disconnect.
  986. Return Value:
  987. S_OK on success. Otherwise, an error code is returned.
  988. --*/
  989. {
  990. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::DisconnectFromServerInternal");
  991. HRESULT hr;
  992. //
  993. // Make sure our window is hidden.
  994. //
  995. //ShowWindow(SW_HIDE);
  996. ListenConnectCleanup();
  997. if (m_ConnectedToServer || m_ConnectionInProgress) {
  998. hr = m_TSClient->Disconnect();
  999. if (SUCCEEDED(hr)) {
  1000. m_ConnectionInProgress = FALSE;
  1001. m_ConnectedToServer = FALSE;
  1002. if (m_RemoteControlRequestInProgress) {
  1003. m_RemoteControlRequestInProgress = FALSE;
  1004. Fire_RemoteControlRequestComplete(SAFERROR_SHADOWEND_UNKNOWN);
  1005. }
  1006. //
  1007. // Fire the server disconnect event.
  1008. //
  1009. Fire_Disconnected(errorCode);
  1010. }
  1011. }
  1012. else {
  1013. TRC_NRM((TB, L"Not connected."));
  1014. hr = S_OK;
  1015. }
  1016. DC_END_FN();
  1017. return hr;
  1018. }
  1019. STDMETHODIMP
  1020. CTSRDPRemoteDesktopClient::ConnectRemoteDesktop()
  1021. /*++
  1022. Routine Description:
  1023. Once "remote desktop mode" has been enabled for the server-side Remote
  1024. Desktop Host COM Object and we are connected to the server, the
  1025. ConnectRemoteDesktop method can be invoked to take control of the remote
  1026. user's desktop.
  1027. Arguments:
  1028. Return Value:
  1029. S_OK on success. Otherwise, an error code is returned.
  1030. --*/
  1031. {
  1032. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::ConnectRemoteDesktop");
  1033. HRESULT hr = S_OK;
  1034. DWORD result;
  1035. BSTR rcRequest = NULL;
  1036. //
  1037. // Fail if we are not valid or not connected to the server.
  1038. //
  1039. if (!IsValid()) {
  1040. ASSERT(FALSE);
  1041. hr = E_FAIL;
  1042. goto CLEANUPANDEXIT;
  1043. }
  1044. if (!m_ConnectedToServer) {
  1045. hr = HRESULT_FROM_WIN32(ERROR_DEVICE_NOT_CONNECTED);
  1046. goto CLEANUPANDEXIT;
  1047. }
  1048. //
  1049. // Succeed if a remote control request is already in progress.
  1050. //
  1051. if (m_RemoteControlRequestInProgress) {
  1052. hr = S_OK;
  1053. goto CLEANUPANDEXIT;
  1054. }
  1055. //
  1056. // Generate the remote control connect request message.
  1057. //
  1058. hr = GenerateRCRequest(&rcRequest);
  1059. if (!SUCCEEDED(hr)) {
  1060. goto CLEANUPANDEXIT;
  1061. }
  1062. //
  1063. // Send it.
  1064. //
  1065. hr = m_CtlChannel->SendChannelData(rcRequest);
  1066. if (!SUCCEEDED(hr)) {
  1067. goto CLEANUPANDEXIT;
  1068. }
  1069. //
  1070. // A request is in progress, if we successfully sent the request.
  1071. //
  1072. m_RemoteControlRequestInProgress = TRUE;
  1073. CLEANUPANDEXIT:
  1074. if (rcRequest != NULL) {
  1075. SysFreeString(rcRequest);
  1076. }
  1077. DC_END_FN();
  1078. return hr;
  1079. }
  1080. STDMETHODIMP
  1081. CTSRDPRemoteDesktopClient::DisconnectRemoteDesktop()
  1082. /*++
  1083. Routine Description:
  1084. Once "remote desktop mode" has been enabled for the server-side Remote
  1085. Desktop Host COM Object and we are connected to the server, the
  1086. ConnectRemoteDesktop method can be invoked to take control of the remote
  1087. user's desktop.
  1088. Arguments:
  1089. Return Value:
  1090. S_OK on success. Otherwise, an error code is returned.
  1091. --*/
  1092. {
  1093. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::DisconnectRemoteDesktop");
  1094. HRESULT hr = S_OK;
  1095. CComBSTR rcRequest;
  1096. //
  1097. // Fail if we are not valid or not connected.
  1098. //
  1099. if (!IsValid()) {
  1100. ASSERT(FALSE);
  1101. hr = E_FAIL;
  1102. goto CLEANUPANDEXIT;
  1103. }
  1104. if (!m_ConnectedToServer) {
  1105. hr = HRESULT_FROM_WIN32(ERROR_DEVICE_NOT_CONNECTED);
  1106. goto CLEANUPANDEXIT;
  1107. }
  1108. //
  1109. // Generate the terminate remote control key sequence and sent it to the
  1110. // server.
  1111. //
  1112. if (m_RemoteControlRequestInProgress) {
  1113. hr = SendTerminateRCKeysToServer();
  1114. }
  1115. CLEANUPANDEXIT:
  1116. DC_END_FN();
  1117. return hr;
  1118. }
  1119. //
  1120. // ISAFRemoteDesktopTestExtension
  1121. //
  1122. STDMETHODIMP
  1123. CTSRDPRemoteDesktopClient::put_TestExtDllName(/*[in]*/ BSTR newVal)
  1124. {
  1125. HRESULT hr = E_NOTIMPL;
  1126. IMsTscAdvancedSettings *pMstscAdvSettings = NULL;
  1127. IMsTscDebug *pMstscDebug = NULL;
  1128. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::put_TestExtDllName" );
  1129. if ( NULL == m_TSClient )
  1130. {
  1131. TRC_ERR((TB, L"m_TSClient is NULL" ));
  1132. hr = E_NOINTERFACE;
  1133. goto CLEANUPANDEXIT;
  1134. }
  1135. hr = m_TSClient->get_AdvancedSettings( &pMstscAdvSettings );
  1136. if (FAILED( hr ))
  1137. {
  1138. TRC_ERR((TB, L"m_TSClient->get_AdvancedSettings failed %08X", hr ));
  1139. goto CLEANUPANDEXIT;
  1140. }
  1141. hr = m_TSClient->get_Debugger( &pMstscDebug );
  1142. if ( FAILED( hr ))
  1143. {
  1144. TRC_ERR((TB, L"m_TSClient->get_Debugger failed %08X", hr ));
  1145. goto CLEANUPANDEXIT;
  1146. }
  1147. hr = pMstscAdvSettings->put_allowBackgroundInput( 1 );
  1148. if (FAILED( hr ))
  1149. {
  1150. TRC_ERR((TB, L"put_allowBackgroundInput failed %08X", hr ));
  1151. }
  1152. pMstscDebug->put_CLXDll( newVal );
  1153. CLEANUPANDEXIT:
  1154. if ( NULL != pMstscAdvSettings )
  1155. pMstscAdvSettings->Release();
  1156. if ( NULL != pMstscDebug )
  1157. pMstscDebug->Release();
  1158. DC_END_FN();
  1159. return hr;
  1160. }
  1161. STDMETHODIMP
  1162. CTSRDPRemoteDesktopClient::put_TestExtParams(/*[in]*/ BSTR newVal)
  1163. {
  1164. HRESULT hr = E_NOTIMPL;
  1165. IMsTscDebug *pMstscDebug = NULL;
  1166. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::put_TestExtParams" );
  1167. if ( NULL == m_TSClient )
  1168. {
  1169. TRC_ERR((TB, L"m_TSClient is NULL" ));
  1170. hr = E_NOINTERFACE;
  1171. goto CLEANUPANDEXIT;
  1172. }
  1173. hr = m_TSClient->get_Debugger( &pMstscDebug );
  1174. if (FAILED( hr ))
  1175. {
  1176. TRC_ERR((TB, L"m_TSClient->get_Debugger failed %08X", hr ));
  1177. goto CLEANUPANDEXIT;
  1178. }
  1179. hr = pMstscDebug->put_CLXCmdLine( newVal );
  1180. CLEANUPANDEXIT:
  1181. if ( NULL != pMstscDebug )
  1182. pMstscDebug->Release();
  1183. DC_END_FN();
  1184. return hr;
  1185. }
  1186. VOID
  1187. CTSRDPRemoteDesktopClient::OnMSTSCReceiveData(
  1188. BSTR data
  1189. )
  1190. /*++
  1191. Routine Description:
  1192. Handle Remote Control Control Channel messages.
  1193. Arguments:
  1194. Return Value:
  1195. --*/
  1196. {
  1197. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::OnMSTSCReceiveData");
  1198. //
  1199. //we got some data, so we must be connected, update timer
  1200. //
  1201. m_PrevTimer = GetTickCount();
  1202. //
  1203. // Fire the data ready event.
  1204. //
  1205. Fire_DataReady(data);
  1206. DC_END_FN();
  1207. }
  1208. VOID
  1209. CTSRDPRemoteDesktopClient::HandleControlChannelMsg()
  1210. /*++
  1211. Routine Description:
  1212. Handle Remote Control Control Channel messages.
  1213. Arguments:
  1214. Return Value:
  1215. --*/
  1216. {
  1217. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::HandleControlChannelMsg");
  1218. PREMOTEDESKTOP_CTL_BUFHEADER msgHeader;
  1219. BSTR msg = NULL;
  1220. LONG *pResult;
  1221. BSTR authenticateReq = NULL;
  1222. BSTR versionInfoPacket = NULL;
  1223. HRESULT hr;
  1224. DWORD result;
  1225. ASSERT(IsValid());
  1226. //
  1227. // Read the next message.
  1228. //
  1229. result = m_CtlChannel->ReceiveChannelData(&msg);
  1230. if (result != ERROR_SUCCESS) {
  1231. goto CLEANUPANDEXIT;
  1232. }
  1233. //
  1234. // Dispatch, based on the message type.
  1235. //
  1236. msgHeader = (PREMOTEDESKTOP_CTL_BUFHEADER)msg;
  1237. //
  1238. // If the server-side of the VC link is alive.
  1239. //
  1240. //
  1241. if ((msgHeader->msgType == REMOTEDESKTOP_CTL_SERVER_ANNOUNCE) &&
  1242. m_ConnectionInProgress) {
  1243. //
  1244. // Send version information to the server.
  1245. //
  1246. hr = GenerateVersionInfoPacket(
  1247. &versionInfoPacket
  1248. );
  1249. if (!SUCCEEDED(hr)) {
  1250. goto CLEANUPANDEXIT;
  1251. }
  1252. hr = m_CtlChannel->SendChannelData(versionInfoPacket);
  1253. if (!SUCCEEDED(hr)) {
  1254. goto CLEANUPANDEXIT;
  1255. }
  1256. //
  1257. // Request client authentication.
  1258. //
  1259. hr = GenerateClientAuthenticateRequest(
  1260. &authenticateReq
  1261. );
  1262. if (!SUCCEEDED(hr)) {
  1263. goto CLEANUPANDEXIT;
  1264. }
  1265. hr = m_CtlChannel->SendChannelData(authenticateReq);
  1266. }
  1267. //
  1268. // If the message is from the server, indicating that it is
  1269. // disconnecting. This can happen if the RDSRemoteDesktopServer
  1270. // is directed to exit listening mode.
  1271. //
  1272. else if (msgHeader->msgType == REMOTEDESKTOP_CTL_DISCONNECT) {
  1273. TRC_NRM((TB, L"Server indicated a disconnect."));
  1274. DisconnectFromServerInternal(SAFERROR_BYSERVER);
  1275. }
  1276. //
  1277. // If the message is a message result.
  1278. //
  1279. else if (msgHeader->msgType == REMOTEDESKTOP_CTL_RESULT) {
  1280. pResult = (LONG *)(msgHeader+1);
  1281. //
  1282. // If a remote control request is in progress, then we should check
  1283. // for a remote control complete status.
  1284. //
  1285. if (m_RemoteControlRequestInProgress && ISRCSTATUSCODE(*pResult)) {
  1286. TRC_ERR((TB, L"Received RC terminate status code."));
  1287. m_RemoteControlRequestInProgress = FALSE;
  1288. Fire_RemoteControlRequestComplete(*pResult);
  1289. }
  1290. //
  1291. // Otherwise, if a connection is in progress, then the client
  1292. // authentication request must have succeeded.
  1293. //
  1294. else if (m_ConnectionInProgress) {
  1295. //
  1296. // Should not be getting a remote control status here.
  1297. //
  1298. ASSERT(!ISRCSTATUSCODE(*pResult));
  1299. //
  1300. // Fire connect request succeeded message.
  1301. //
  1302. if (*pResult == SAFERROR_NOERROR ) {
  1303. m_ConnectedToServer = TRUE;
  1304. m_ConnectionInProgress = FALSE;
  1305. //
  1306. //set the timer to check if the user is still connected
  1307. //ignore errors, worst case - the ui is up even after the user disconnects
  1308. //
  1309. if( m_RdcConnCheckTimeInterval )
  1310. m_TimerId = SetTimer(WM_CONNECTCHECK_TIMER, m_RdcConnCheckTimeInterval);
  1311. //
  1312. // Not in progress once connected
  1313. //
  1314. m_ListenConnectInProgress = FALSE;
  1315. m_TSConnectSocket = INVALID_SOCKET;
  1316. Fire_Connected();
  1317. }
  1318. //
  1319. // Otherwise, fire a disconnected event.
  1320. //
  1321. else {
  1322. DisconnectFromServerInternal(*pResult);
  1323. m_ConnectionInProgress = FALSE;
  1324. }
  1325. }
  1326. }
  1327. //
  1328. // We will ignore other packets to support forward compatibility.
  1329. //
  1330. CLEANUPANDEXIT:
  1331. //
  1332. // Release the message.
  1333. //
  1334. if (msg != NULL) {
  1335. SysFreeString(msg);
  1336. }
  1337. if (versionInfoPacket != NULL) {
  1338. SysFreeString(versionInfoPacket);
  1339. }
  1340. if (authenticateReq != NULL) {
  1341. SysFreeString(authenticateReq);
  1342. }
  1343. DC_END_FN();
  1344. }
  1345. HRESULT
  1346. CTSRDPRemoteDesktopClient::GenerateRCRequest(
  1347. BSTR *rcRequest
  1348. )
  1349. /*++
  1350. Routine Description:
  1351. Generate a remote control request message for the
  1352. server.
  1353. TODO: We might need to be able to push this up
  1354. to the parent class, if it makes sense for
  1355. NetMeeting.
  1356. Arguments:
  1357. rcRequest - Returned request message.
  1358. Return Value:
  1359. S_OK on success. Otherwise, an error code is returned.
  1360. --*/
  1361. {
  1362. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::GenerateRCRequest");
  1363. PREMOTEDESKTOP_CTL_BUFHEADER msgHeader;
  1364. PBYTE ptr;
  1365. HRESULT hr;
  1366. DWORD len;
  1367. len = sizeof(REMOTEDESKTOP_CTL_BUFHEADER) + ((m_ConnectParms.Length()+1) * sizeof(WCHAR));
  1368. msgHeader = (PREMOTEDESKTOP_CTL_BUFHEADER)SysAllocStringByteLen(NULL, len);
  1369. if (msgHeader != NULL) {
  1370. msgHeader->msgType = REMOTEDESKTOP_CTL_REMOTE_CONTROL_DESKTOP;
  1371. ptr = (PBYTE)(msgHeader + 1);
  1372. memcpy(ptr, (BSTR)m_ConnectParms,
  1373. ((m_ConnectParms.Length()+1) * sizeof(WCHAR)));
  1374. *rcRequest = (BSTR)msgHeader;
  1375. hr = S_OK;
  1376. }
  1377. else {
  1378. TRC_ERR((TB, L"SysAllocStringByteLen failed for %ld bytes", len));
  1379. hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  1380. }
  1381. DC_END_FN();
  1382. return hr;
  1383. }
  1384. HRESULT
  1385. CTSRDPRemoteDesktopClient::GenerateClientAuthenticateRequest(
  1386. BSTR *authenticateReq
  1387. )
  1388. /*++
  1389. Routine Description:
  1390. Generate a 'client authenticate' request.
  1391. TODO: We might need to be able to push this up
  1392. to the parent class, if it makes sense for
  1393. NetMeeting.
  1394. Arguments:
  1395. rcRequest - Returned request message.
  1396. Return Value:
  1397. S_OK on success. Otherwise, an error code is returned.
  1398. --*/
  1399. {
  1400. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::GenerateClientAuthenticateRequest");
  1401. PREMOTEDESKTOP_CTL_BUFHEADER msgHeader;
  1402. PBYTE ptr;
  1403. HRESULT hr;
  1404. DWORD len;
  1405. len = sizeof(REMOTEDESKTOP_CTL_BUFHEADER) + ((m_ConnectParms.Length()+1) * sizeof(WCHAR));
  1406. #if FEATURE_USERBLOBS
  1407. if( m_ExpertBlob.Length() > 0 ) {
  1408. len += ((m_ExpertBlob.Length() + 1) * sizeof(WCHAR));
  1409. }
  1410. #endif
  1411. msgHeader = (PREMOTEDESKTOP_CTL_BUFHEADER)SysAllocStringByteLen(NULL, len);
  1412. if (msgHeader != NULL) {
  1413. msgHeader->msgType = REMOTEDESKTOP_CTL_AUTHENTICATE;
  1414. ptr = (PBYTE)(msgHeader + 1);
  1415. memcpy(ptr, (BSTR)m_ConnectParms,
  1416. ((m_ConnectParms.Length()+1) * sizeof(WCHAR)));
  1417. #if FEATURE_USERBLOBS
  1418. if( m_ExpertBlob.Length() > 0 ) {
  1419. ptr += ((m_ConnectParms.Length()+1) * sizeof(WCHAR));
  1420. memcpy(ptr, (BSTR)m_ExpertBlob,
  1421. ((m_ExpertBlob.Length()+1) * sizeof(WCHAR)));
  1422. }
  1423. #endif
  1424. *authenticateReq = (BSTR)msgHeader;
  1425. hr = S_OK;
  1426. }
  1427. else {
  1428. TRC_ERR((TB, L"SysAllocStringByteLen failed for %ld bytes", len));
  1429. hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  1430. }
  1431. DC_END_FN();
  1432. return hr;
  1433. }
  1434. HRESULT
  1435. CTSRDPRemoteDesktopClient::GenerateVersionInfoPacket(
  1436. BSTR *versionInfoPacket
  1437. )
  1438. /*++
  1439. Routine Description:
  1440. Generate a version information packet.
  1441. Arguments:
  1442. versionInfoPacket - Version Information Returned Packet
  1443. Return Value:
  1444. S_OK on success. Otherwise, an error code is returned.
  1445. --*/
  1446. {
  1447. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::GenerateVersionInfoPacket");
  1448. PREMOTEDESKTOP_CTL_BUFHEADER msgHeader;
  1449. PDWORD ptr;
  1450. HRESULT hr;
  1451. DWORD len;
  1452. len = sizeof(REMOTEDESKTOP_CTL_BUFHEADER) + (sizeof(DWORD) * 2);
  1453. msgHeader = (PREMOTEDESKTOP_CTL_BUFHEADER)SysAllocStringByteLen(NULL, len);
  1454. if (msgHeader != NULL) {
  1455. msgHeader->msgType = REMOTEDESKTOP_CTL_VERSIONINFO;
  1456. ptr = (PDWORD)(msgHeader + 1);
  1457. *ptr = REMOTEDESKTOP_VERSION_MAJOR; ptr++;
  1458. *ptr = REMOTEDESKTOP_VERSION_MINOR;
  1459. *versionInfoPacket = (BSTR)msgHeader;
  1460. hr = S_OK;
  1461. }
  1462. else {
  1463. TRC_ERR((TB, L"SysAllocStringByteLen failed for %ld bytes", len));
  1464. hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  1465. }
  1466. DC_END_FN();
  1467. return hr;
  1468. }
  1469. VOID
  1470. CTSRDPRemoteDesktopClient::OnReceivedTSPublicKey(BSTR bstrPublicKey, VARIANT_BOOL* pfContinue)
  1471. {
  1472. DWORD dwStatus;
  1473. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::OnReceivedPublicKey");
  1474. CComBSTR bstrTSPublicKey;
  1475. if( m_ConnectParmVersion >= SALEM_CONNECTPARM_SECURITYBLOB_VERSION ) {
  1476. //
  1477. // hash TS public key send from client activeX control, reverse
  1478. // hashing from what we got in connect parm might not give us
  1479. // back the original value.
  1480. //
  1481. dwStatus = HashSecurityData(
  1482. (PBYTE) bstrPublicKey,
  1483. ::SysStringByteLen(bstrPublicKey),
  1484. bstrTSPublicKey
  1485. );
  1486. if( ERROR_SUCCESS != dwStatus )
  1487. {
  1488. TRC_ERR((TB, L"Hashed Public Key Send from TS %s", bstrPublicKey));
  1489. TRC_ERR((TB, L"Hashed public Key in parm %s", m_TSSecurityBlob));
  1490. TRC_ERR((TB, L"HashSecurityData() failed with %d", dwStatus));
  1491. *pfContinue = FALSE;
  1492. }
  1493. else if( !(bstrTSPublicKey == m_TSSecurityBlob) )
  1494. {
  1495. TRC_ERR((TB, L"Hashed Public Key Send from TS %s", bstrPublicKey));
  1496. TRC_ERR((TB, L"Hashed public Key in parm %s", m_TSSecurityBlob));
  1497. *pfContinue = FALSE;
  1498. }
  1499. else
  1500. {
  1501. *pfContinue = TRUE;
  1502. }
  1503. }
  1504. else {
  1505. *pfContinue = TRUE;
  1506. }
  1507. DC_END_FN();
  1508. }
  1509. VOID
  1510. CTSRDPRemoteDesktopClient::OnRDPConnected()
  1511. {
  1512. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::OnRDPConnected");
  1513. Fire_BeginConnect();
  1514. DC_END_FN();
  1515. }
  1516. VOID
  1517. CTSRDPRemoteDesktopClient::OnLoginComplete()
  1518. /*++
  1519. Routine Description:
  1520. Arguments:
  1521. Return Value:
  1522. --*/
  1523. {
  1524. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::OnLoginComplete");
  1525. //
  1526. // Clear server address list
  1527. //
  1528. m_ServerAddressList.clear();
  1529. //
  1530. // We got some event from the mstsc, so we must be connected, update timer
  1531. //
  1532. m_PrevTimer = GetTickCount();
  1533. CLEANUPANDEXIT:
  1534. DC_END_FN();
  1535. }
  1536. LONG
  1537. CTSRDPRemoteDesktopClient::TranslateMSTSCDisconnectCode(
  1538. DisconnectReasonCode disconReason,
  1539. ExtendedDisconnectReasonCode extendedReasonCode
  1540. )
  1541. /*++
  1542. Routine Description:
  1543. Translate an MSTSC disconnect code into a Salem disconnect
  1544. code.
  1545. Arguments:
  1546. disconReason - Disconnect Reason
  1547. extendedReasonCode - MSTSCAX Extended Reason Code
  1548. Return Value:
  1549. Salem disconnect code.
  1550. --*/
  1551. {
  1552. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::TranslateMSTSCDisconnectCode");
  1553. LONG ret;
  1554. BOOL handled;
  1555. //
  1556. // First check the extended error information.
  1557. // TODO: Need to keep track of additional values added by NadimA
  1558. // and company, here, before we ship.
  1559. //
  1560. if (extendedReasonCode != exDiscReasonNoInfo) {
  1561. //
  1562. // Record the extended error code, if given. Note that this may be
  1563. // overridden below if we have better information.
  1564. //
  1565. m_LastExtendedErrorInfo = extendedReasonCode;
  1566. //
  1567. // Check for a protocol error.
  1568. //
  1569. if ((extendedReasonCode >= exDiscReasonProtocolRangeStart) &&
  1570. (extendedReasonCode <= exDiscReasonProtocolRangeEnd)) {
  1571. ret = SAFERROR_RCPROTOCOLERROR;
  1572. goto CLEANUPANDEXIT;
  1573. }
  1574. }
  1575. //
  1576. // If the extended error information didn't help us.
  1577. //
  1578. switch(disconReason)
  1579. {
  1580. case disconnectReasonNoInfo : ret = SAFERROR_NOINFO;
  1581. break;
  1582. case 0xb08: // UI_ERR_NORMAL_DISCONNECT not defined in mstsax.idl
  1583. case disconnectReasonLocalNotError : ret = SAFERROR_LOCALNOTERROR;
  1584. break;
  1585. case disconnectReasonRemoteByUser : ret = SAFERROR_REMOTEBYUSER;
  1586. break;
  1587. case disconnectReasonByServer : ret = SAFERROR_BYSERVER;
  1588. break;
  1589. case disconnectReasonDNSLookupFailed2 : m_LastExtendedErrorInfo = disconReason;
  1590. case disconnectReasonDNSLookupFailed : ret = SAFERROR_DNSLOOKUPFAILED;
  1591. break;
  1592. case disconnectReasonOutOfMemory3 :
  1593. case disconnectReasonOutOfMemory2 : m_LastExtendedErrorInfo = disconReason;
  1594. case disconnectReasonOutOfMemory : ret = SAFERROR_OUTOFMEMORY;
  1595. break;
  1596. case disconnectReasonConnectionTimedOut : ret = SAFERROR_CONNECTIONTIMEDOUT;
  1597. break;
  1598. case disconnectReasonSocketConnectFailed :
  1599. case 0x904 : ret = SAFERROR_SOCKETCONNECTFAILED; // NadimA is adding to
  1600. // MSTSCAX IDL. This is a
  1601. // NL_ERR_TDFDCLOSE error.
  1602. break;
  1603. case disconnectReasonHostNotFound : ret = SAFERROR_HOSTNOTFOUND;
  1604. break;
  1605. case disconnectReasonWinsockSendFailed : ret = SAFERROR_WINSOCKSENDFAILED;
  1606. break;
  1607. case disconnectReasonInvalidIP : m_LastExtendedErrorInfo = disconReason;
  1608. case disconnectReasonInvalidIPAddr : ret = SAFERROR_INVALIDIPADDR;
  1609. break;
  1610. case disconnectReasonSocketRecvFailed : ret = SAFERROR_SOCKETRECVFAILED;
  1611. break;
  1612. case disconnectReasonInvalidEncryption : ret = SAFERROR_INVALIDENCRYPTION;
  1613. break;
  1614. case disconnectReasonGetHostByNameFailed : ret = SAFERROR_GETHOSTBYNAMEFAILED;
  1615. break;
  1616. case disconnectReasonLicensingFailed : m_LastExtendedErrorInfo = disconReason;
  1617. case disconnectReasonLicensingTimeout : ret = SAFERROR_LICENSINGFAILED;
  1618. break;
  1619. case disconnectReasonDecryptionError : ret = SAFERROR_DECRYPTIONERROR;
  1620. break;
  1621. case disconnectReasonServerCertificateUnpackErr : ret = SAFERROR_MISMATCHPARMS;
  1622. break;
  1623. default: ret = SAFERROR_RCUNKNOWNERROR;
  1624. m_LastExtendedErrorInfo = disconReason;
  1625. ASSERT(FALSE);
  1626. }
  1627. CLEANUPANDEXIT:
  1628. DC_END_FN();
  1629. return ret;
  1630. }
  1631. VOID
  1632. CTSRDPRemoteDesktopClient::OnDisconnected(
  1633. long disconReason
  1634. )
  1635. /*++
  1636. Routine Description:
  1637. Arguments:
  1638. Return Value:
  1639. --*/
  1640. {
  1641. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::OnDisconnected");
  1642. HRESULT hr = E_HANDLE; // initialize an error code.
  1643. long clientReturnCode;
  1644. ExtendedDisconnectReasonCode extendedClientCode;
  1645. TRC_ERR((TB, L"Disconnected because %ld", disconReason));
  1646. m_TSClient->get_ExtendedDisconnectReason(&extendedClientCode);
  1647. clientReturnCode = TranslateMSTSCDisconnectCode(
  1648. (DisconnectReasonCode)disconReason,
  1649. extendedClientCode
  1650. );
  1651. // Go thru all remaining server:port, mstscax might return some
  1652. // error code that we don't understand.
  1653. if( m_ServerAddressList.size() > 0 ) {
  1654. ServerAddress address;
  1655. address = m_ServerAddressList.front();
  1656. m_ServerAddressList.pop_front();
  1657. hr = ConnectServerPort( address.ServerName, address.portNumber );
  1658. if (FAILED(hr)) {
  1659. TRC_ERR((TB, L"ConnectServerPort: %08X", hr));
  1660. }
  1661. }
  1662. //
  1663. // Return the error code from connecting to 'last' server to client
  1664. //
  1665. if( FAILED(hr) ) {
  1666. m_ServerAddressList.clear();
  1667. //
  1668. // Fire the server disconnect event, if we are really connected or
  1669. // we have a connection in progress.
  1670. //
  1671. if (m_ConnectedToServer || m_ConnectionInProgress) {
  1672. Fire_Disconnected(clientReturnCode);
  1673. }
  1674. m_ConnectedToServer = FALSE;
  1675. m_ConnectionInProgress = FALSE;
  1676. ListenConnectCleanup();
  1677. //
  1678. // Fire the remote control request failure event, if appropriate.
  1679. //
  1680. if (m_RemoteControlRequestInProgress) {
  1681. ASSERT(clientReturnCode != SAFERROR_NOERROR);
  1682. Fire_RemoteControlRequestComplete(SAFERROR_SHADOWEND_UNKNOWN);
  1683. m_RemoteControlRequestInProgress = FALSE;
  1684. }
  1685. }
  1686. DC_END_FN();
  1687. }
  1688. HRESULT
  1689. CTSRDPRemoteDesktopClient::SendTerminateRCKeysToServer()
  1690. /*++
  1691. Routine Description:
  1692. Send the terminate shadowing key sequence to the server.
  1693. Arguments:
  1694. Return Value:
  1695. S_OK on success. Otherwise, an error status is returned.
  1696. --*/
  1697. {
  1698. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::SendTerminateRCKeysToServer");
  1699. HRESULT hr = S_OK;
  1700. IMsRdpClientNonScriptable* pTscNonScript = NULL;
  1701. VARIANT_BOOL keyUp[] = {
  1702. VARIANT_FALSE, VARIANT_FALSE, VARIANT_TRUE, VARIANT_TRUE
  1703. };
  1704. LONG keyData[] = {
  1705. MapVirtualKey(VK_CONTROL, 0), // these are SCANCODES.
  1706. MapVirtualKey(VK_MULTIPLY, 0),
  1707. MapVirtualKey(VK_MULTIPLY, 0),
  1708. MapVirtualKey(VK_CONTROL, 0),
  1709. };
  1710. //
  1711. // Send the terminate keys to the server.
  1712. //
  1713. hr = m_TSClient->QueryInterface(
  1714. IID_IMsRdpClientNonScriptable,
  1715. (void**)&pTscNonScript
  1716. );
  1717. if (hr != S_OK) {
  1718. TRC_ERR((TB, L"QI: %08X", hr));
  1719. goto CLEANUPANDEXIT;
  1720. }
  1721. pTscNonScript->NotifyRedirectDeviceChange(0, 0);
  1722. pTscNonScript->SendKeys(4, keyUp, keyData);
  1723. if (hr != S_OK) {
  1724. TRC_ERR((TB, L"SendKeys, QI: %08X", hr));
  1725. }
  1726. pTscNonScript->Release();
  1727. CLEANUPANDEXIT:
  1728. DC_END_FN();
  1729. return hr;
  1730. }
  1731. HWND CTSRDPRemoteDesktopClient::SearchForWindow(
  1732. HWND hwndParent,
  1733. LPTSTR srchCaption,
  1734. LPTSTR srchClass
  1735. )
  1736. /*++
  1737. Routine Description:
  1738. Search for a child window of the specified parent window.
  1739. Arguments:
  1740. srchCaption - Window caption for which to search. NULL is
  1741. considered a wildcard.
  1742. srchClass - Window class for which to search. NULL is
  1743. considred a wildcard.
  1744. Return Value:
  1745. S_OK on success. Otherwise, an error status is returned.
  1746. --*/
  1747. {
  1748. WINSEARCH srch;
  1749. srch.foundWindow = NULL;
  1750. srch.srchCaption = srchCaption;
  1751. srch.srchClass = srchClass;
  1752. BOOL result = EnumChildWindows(
  1753. hwndParent,
  1754. (WNDENUMPROC)_WindowSrchProc,
  1755. (LPARAM)&srch
  1756. );
  1757. return srch.foundWindow;
  1758. }
  1759. BOOL CALLBACK
  1760. CTSRDPRemoteDesktopClient::_WindowSrchProc(HWND hwnd, PWINSEARCH srch)
  1761. {
  1762. TCHAR classname[128];
  1763. TCHAR caption[128];
  1764. if (srch->srchClass && !GetClassName(hwnd, classname, sizeof(classname) / sizeof(TCHAR)))
  1765. {
  1766. return TRUE;
  1767. }
  1768. if (srch->srchCaption && !::GetWindowText(hwnd, caption, sizeof(caption)/sizeof(TCHAR)))
  1769. {
  1770. return TRUE;
  1771. }
  1772. if ((!srch->srchClass || !_tcscmp(classname, srch->srchClass)
  1773. &&
  1774. (!srch->srchCaption || !_tcscmp(caption, srch->srchCaption)))
  1775. )
  1776. {
  1777. srch->foundWindow = hwnd;
  1778. return FALSE;
  1779. }
  1780. return TRUE;
  1781. }
  1782. HRESULT
  1783. CTSRDPRemoteDesktopClient::GenerateNullData( BSTR* pbstrData )
  1784. {
  1785. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::GenerateNullData");
  1786. HRESULT hr;
  1787. DWORD len;
  1788. PREMOTEDESKTOP_CTL_BUFHEADER msgHeader = NULL;
  1789. len = sizeof( REMOTEDESKTOP_CTL_BUFHEADER );
  1790. msgHeader = (PREMOTEDESKTOP_CTL_BUFHEADER)SysAllocStringByteLen(NULL, len);
  1791. if (msgHeader != NULL) {
  1792. msgHeader->msgType = REMOTEDESKTOP_CTL_ISCONNECTED;
  1793. //nothing else other than the message
  1794. *pbstrData = (BSTR)msgHeader;
  1795. hr = S_OK;
  1796. }
  1797. else {
  1798. TRC_ERR((TB, L"SysAllocStringByteLen failed for %ld bytes", len));
  1799. hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  1800. }
  1801. DC_END_FN();
  1802. return hr;
  1803. }
  1804. LRESULT CTSRDPRemoteDesktopClient::OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  1805. {
  1806. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::OnCheckConnectTimer");
  1807. BSTR bstrMsg = NULL;
  1808. if( WM_LISTENTIMEOUT_TIMER == wParam ) {
  1809. bHandled = TRUE;
  1810. if( TRUE == ListenConnectInProgress() ) {
  1811. StopListen();
  1812. Fire_ListenConnect( SAFERROR_CONNECTIONTIMEDOUT );
  1813. }
  1814. }
  1815. else if ( WM_CONNECTCHECK_TIMER == wParam ) {
  1816. DWORD dwCurTimer = GetTickCount();
  1817. bHandled = TRUE;
  1818. if(m_ConnectedToServer) {
  1819. //
  1820. //see if the timer wrapped around to zero (does so if the system was up 49.7 days or something)
  1821. //if so reset it
  1822. //
  1823. if( dwCurTimer > m_PrevTimer && ( dwCurTimer - m_PrevTimer >= m_RdcConnCheckTimeInterval )) {
  1824. //
  1825. //time to send a null data
  1826. //
  1827. if(SUCCEEDED(GenerateNullData(&bstrMsg))) {
  1828. if(!SUCCEEDED(m_CtlChannel->SendChannelData(bstrMsg))) {
  1829. //
  1830. //could not send data, assume disconnected
  1831. //
  1832. DisconnectFromServer();
  1833. //
  1834. //don't need the timer anymore, kill it
  1835. //
  1836. KillTimer( m_TimerId );
  1837. m_TimerId = 0;
  1838. }
  1839. }
  1840. }
  1841. } //m_ConnectedToServer
  1842. //
  1843. //update the timer
  1844. //
  1845. m_PrevTimer = dwCurTimer;
  1846. if( NULL != bstrMsg ) {
  1847. SysFreeString(bstrMsg);
  1848. }
  1849. }
  1850. DC_END_FN();
  1851. return 0;
  1852. }
  1853. STDMETHODIMP
  1854. CTSRDPRemoteDesktopClient::CreateListenEndpoint(
  1855. IN LONG port,
  1856. OUT BSTR* pConnectParm
  1857. )
  1858. /*++
  1859. Description:
  1860. Routine to create a listening socket and return connection parameter to this 'listen' socket.
  1861. Parameters:
  1862. port : Port that socket should listen on.
  1863. pConnectParm : Return connection parameter to this listening socket.
  1864. returns:
  1865. S_OK or error code.
  1866. Notes:
  1867. Function is async, return code, if error, is for listening thread set up, caller is notified of
  1868. successful or error in network connection via ListenConnect event.
  1869. --*/
  1870. {
  1871. HRESULT hr = S_OK;
  1872. SOCKET hListenSocket = INVALID_SOCKET;
  1873. IMsRdpClientAdvancedSettings* pAdvSettings;
  1874. LONG rdpPort = 0;
  1875. int intRC;
  1876. int lastError;
  1877. SOCKADDR_IN sockAddr;
  1878. int sockAddrSize;
  1879. int optvalue;
  1880. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::CreateListenEndpoint");
  1881. if( NULL == pConnectParm )
  1882. {
  1883. hr = E_POINTER;
  1884. return hr;
  1885. }
  1886. if (!IsValid()) {
  1887. ASSERT(FALSE);
  1888. hr = E_FAIL;
  1889. return hr;
  1890. }
  1891. //
  1892. // Return error if we are in progress of connect or connected.
  1893. //
  1894. if( TRUE == ListenConnectInProgress() || // Listen already started.
  1895. TRUE == m_ConnectionInProgress || // Connection already in progress
  1896. TRUE == m_ConnectedToServer ) { // Already connected to server
  1897. hr = HRESULT_FROM_WIN32( ERROR_SHARING_VIOLATION );
  1898. TRC_ERR((TB, L"StartListen() already in listen"));
  1899. goto CLEANUPANDEXIT;
  1900. }
  1901. //
  1902. // Initialize Winsock and ICS library if not yet initialized.
  1903. // InitListeningLibrary() will only add ref. count
  1904. // if library already initialized by other instance.
  1905. //
  1906. if( FALSE == m_InitListeningLibrary ) {
  1907. hr = InitListeningLibrary();
  1908. if( FAILED(hr) ) {
  1909. TRC_ERR((TB, L"InitListeningLibrary() failed : %08X", hr));
  1910. goto CLEANUPANDEXIT;
  1911. }
  1912. m_InitListeningLibrary = TRUE;
  1913. }
  1914. //
  1915. // mstscax will close the socket once connection is ended.
  1916. //
  1917. m_TSConnectSocket = INVALID_SOCKET;
  1918. //
  1919. // Create a listening socket
  1920. //
  1921. m_ListenSocket = socket(AF_INET, SOCK_STREAM, 0);
  1922. if( INVALID_SOCKET == m_ListenSocket ) {
  1923. intRC = WSAGetLastError();
  1924. TRC_ERR((TB, _T("socket failed %d"), intRC));
  1925. hr = HRESULT_FROM_WIN32(intRC);
  1926. goto CLEANUPANDEXIT;
  1927. }
  1928. //
  1929. // Disable NAGLE algorithm and enable don't linger option.
  1930. //
  1931. optvalue = 1;
  1932. setsockopt( m_ListenSocket, IPPROTO_TCP, TCP_NODELAY, (char *)&optvalue, sizeof(optvalue) );
  1933. optvalue = 1;
  1934. setsockopt( m_ListenSocket, SOL_SOCKET, SO_DONTLINGER, (char *)&optvalue, sizeof(optvalue) );
  1935. //
  1936. // Request async notifications to send to our window
  1937. //
  1938. intRC = WSAAsyncSelect(
  1939. m_ListenSocket,
  1940. m_hWnd,
  1941. WM_TSCONNECT,
  1942. FD_ACCEPT
  1943. );
  1944. if(SOCKET_ERROR == intRC) {
  1945. intRC = WSAGetLastError();
  1946. TRC_ERR((TB, _T("WSAAsyncSelect failed %d"), intRC));
  1947. hr = HRESULT_FROM_WIN32(intRC);
  1948. goto CLEANUPANDEXIT;
  1949. }
  1950. sockAddr.sin_family = AF_INET;
  1951. sockAddr.sin_port = htons(port);
  1952. sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);;
  1953. intRC = bind( m_ListenSocket, (struct sockaddr *) &sockAddr, sizeof(sockAddr) );
  1954. if( SOCKET_ERROR == intRC ) {
  1955. lastError = WSAGetLastError();
  1956. TRC_ERR((TB, _T("bind failed - %d"), lastError));
  1957. hr = HRESULT_FROM_WIN32( lastError );
  1958. goto CLEANUPANDEXIT;
  1959. }
  1960. if( 0 == port ) {
  1961. //
  1962. // Retrieve which port we are listening
  1963. //
  1964. sockAddrSize = sizeof( sockAddr );
  1965. intRC = getsockname(
  1966. m_ListenSocket,
  1967. (struct sockaddr *)&sockAddr,
  1968. &sockAddrSize
  1969. );
  1970. if( SOCKET_ERROR == intRC )
  1971. {
  1972. lastError = WSAGetLastError();
  1973. TRC_ERR((TB, _T("getsockname failed - GLE:%d"),lastError));
  1974. hr = HRESULT_FROM_WIN32( lastError );
  1975. goto CLEANUPANDEXIT;
  1976. }
  1977. m_ConnectedPort = ntohs(sockAddr.sin_port);
  1978. }
  1979. else {
  1980. m_ConnectedPort = port;
  1981. }
  1982. TRC_ERR((TB, _T("Listenin on port %d"),m_ConnectedPort));
  1983. //
  1984. // Tell ICS library to punch a hole thru ICS, no-op
  1985. // if not ICS configuration.
  1986. //
  1987. m_ICSPort = OpenPort( m_ConnectedPort );
  1988. //
  1989. // Retrieve connection parameters for this client (expert).
  1990. //
  1991. hr = RetrieveUserConnectParm( pConnectParm );
  1992. if( FAILED(hr) ) {
  1993. TRC_ERR((TB, _T("RetrieveUserConnectParm failed - 0x%08x"),hr));
  1994. }
  1995. CLEANUPANDEXIT:
  1996. if( FAILED(hr) ) {
  1997. StopListen();
  1998. }
  1999. DC_END_FN();
  2000. return hr;
  2001. }
  2002. STDMETHODIMP
  2003. CTSRDPRemoteDesktopClient::StopListen()
  2004. /*++
  2005. Description:
  2006. Stop listening waiting for TS server (helpee, user) to connect.
  2007. Parameters:
  2008. None.
  2009. Returns:
  2010. S_OK or error code.
  2011. --*/
  2012. {
  2013. HRESULT hr = S_OK;
  2014. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::StopListen");
  2015. if (!IsValid()) {
  2016. ASSERT(FALSE);
  2017. hr = E_FAIL;
  2018. return hr;
  2019. }
  2020. // End listening, either we are actually issued a listen() to socket
  2021. // or we just created the listen socket but not yet start listening
  2022. if( TRUE == ListenConnectInProgress() || INVALID_SOCKET != m_ListenSocket ) {
  2023. ListenConnectCleanup();
  2024. Fire_ListenConnect( SAFERROR_STOPLISTENBYUSER );
  2025. }
  2026. else {
  2027. TRC_ERR((TB, _T("StopListen called while not in listen mode")));
  2028. hr = HRESULT_FROM_WIN32( WSANOTINITIALISED );
  2029. }
  2030. DC_END_FN();
  2031. return hr;
  2032. }
  2033. LRESULT
  2034. CTSRDPRemoteDesktopClient::OnTSConnect(
  2035. UINT uMsg,
  2036. WPARAM wParam,
  2037. LPARAM lParam,
  2038. BOOL& bHandled
  2039. )
  2040. /*++
  2041. Routine Description:
  2042. Window Message Handler FD_ACCEPT from async. winsock.
  2043. Parameters:
  2044. Refer to async. winsock FD_ACCEPT.
  2045. Returns:
  2046. --*/
  2047. {
  2048. WORD eventWSA;
  2049. WORD errorWSA;
  2050. HRESULT hr = S_OK;
  2051. SOCKADDR_IN inSockAddr;
  2052. int inSockAddrSize;
  2053. SOCKET s;
  2054. DWORD dwStatus;
  2055. DWORD SafErrorCode = SAFERROR_NOERROR;
  2056. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::OnTSConnect");
  2057. eventWSA = WSAGETSELECTEVENT(lParam);
  2058. errorWSA = WSAGETSELECTERROR(lParam);
  2059. //
  2060. // MSDN : Message might already in our queue before we stop listen.
  2061. //
  2062. if( INVALID_SOCKET == m_ListenSocket || FALSE == ListenConnectInProgress() ) {
  2063. bHandled = TRUE;
  2064. return 0;
  2065. }
  2066. //
  2067. // we are not expecting event other than FD_CONNECT
  2068. //
  2069. if( eventWSA != FD_ACCEPT ) {
  2070. TRC_ERR((TB, _T("Expecting event %d got got %d"), FD_CONNECT, eventWSA));
  2071. return 0;
  2072. }
  2073. //
  2074. // Make sure we don't do anything other than our own socket
  2075. //
  2076. if( (SOCKET)wParam != m_ListenSocket ) {
  2077. TRC_ERR((TB, _T("Expecting listening socket %d got %d"), m_ListenSocket, wParam));
  2078. return 0;
  2079. }
  2080. //
  2081. // We handle the message
  2082. //
  2083. bHandled = TRUE;
  2084. //
  2085. // Error occurred, fire a error event.
  2086. //
  2087. if( 0 != errorWSA ) {
  2088. TRC_ERR((TB, _T("WSA socket listen failed : %d"), errorWSA));
  2089. hr = HRESULT_FROM_WIN32( errorWSA );
  2090. SafErrorCode = SAFERROR_SOCKETCONNECTFAILED;
  2091. goto CLEANUPANDEXIT;
  2092. }
  2093. inSockAddrSize = sizeof(inSockAddr);
  2094. m_TSConnectSocket = accept( m_ListenSocket,
  2095. (struct sockaddr DCPTR)&inSockAddr,
  2096. &inSockAddrSize
  2097. );
  2098. if( INVALID_SOCKET == m_TSConnectSocket ) {
  2099. dwStatus = WSAGetLastError();
  2100. hr = HRESULT_FROM_WIN32(dwStatus);
  2101. TRC_ERR((TB, _T("accept failed : %d"), dwStatus));
  2102. SafErrorCode = SAFERROR_SOCKETCONNECTFAILED;
  2103. goto CLEANUPANDEXIT;
  2104. }
  2105. //
  2106. // Cached connecting TS server IP address.
  2107. // m_ConnectPort is set at the time we bind socket
  2108. //
  2109. m_ConnectedServer = inet_ntoa(inSockAddr.sin_addr);
  2110. //
  2111. // Stop async. event notification now, accepted socket
  2112. // has same properties as original listening socket.
  2113. //
  2114. dwStatus = WSAAsyncSelect(
  2115. m_TSConnectSocket,
  2116. m_hWnd,
  2117. 0,
  2118. 0
  2119. );
  2120. //
  2121. // Not critical,
  2122. // listening socket.
  2123. //
  2124. if((DWORD)SOCKET_ERROR == dwStatus) {
  2125. TRC_ERR((TB, _T("WSAAsyncSelect resetting notification failed : %d"), dwStatus));
  2126. }
  2127. CLEANUPANDEXIT:
  2128. //
  2129. // Close listening socket and kill timer.
  2130. //
  2131. if( (UINT_PTR)0 != m_ListenTimeoutTimerID )
  2132. {
  2133. KillTimer( m_ListenTimeoutTimerID );
  2134. m_ListenTimeoutTimerID = (UINT_PTR)0;
  2135. }
  2136. if( INVALID_SOCKET != m_ListenSocket )
  2137. {
  2138. closesocket( m_ListenSocket );
  2139. m_ListenSocket = INVALID_SOCKET;
  2140. }
  2141. //
  2142. // Successfully established connection, terminate listening socket
  2143. //
  2144. Fire_ListenConnect( SafErrorCode );
  2145. DC_END_FN();
  2146. return 0;
  2147. }
  2148. STDMETHODIMP
  2149. CTSRDPRemoteDesktopClient::StartListen(
  2150. /*[in]*/ LONG timeout
  2151. )
  2152. /*++
  2153. Routine Description:
  2154. Put client into listen mode with optionally timeout.
  2155. Parameters:
  2156. timeout : Listen wait timeout, 0 for infinite.
  2157. Returns:
  2158. S_OK or error code.
  2159. --*/
  2160. {
  2161. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::OnTSConnect");
  2162. HRESULT hr = S_OK;
  2163. int intRC;
  2164. int lastError;
  2165. if( FALSE == IsValid() ) {
  2166. ASSERT(FALSE);
  2167. hr = E_FAIL;
  2168. goto CLEANUPANDEXIT;
  2169. }
  2170. if( INVALID_SOCKET == m_ListenSocket ) {
  2171. ASSERT(FALSE);
  2172. hr = E_FAIL;
  2173. goto CLEANUPANDEXIT;
  2174. }
  2175. //
  2176. // Start listening on the port
  2177. //
  2178. intRC = listen( m_ListenSocket, SOMAXCONN );
  2179. if( SOCKET_ERROR == intRC )
  2180. {
  2181. lastError = WSAGetLastError();
  2182. TRC_ERR((TB, _T("listen failed - GLE:%d"), lastError));
  2183. hr = HRESULT_FROM_WIN32( lastError );
  2184. goto CLEANUPANDEXIT;
  2185. }
  2186. //
  2187. // we are in listening now.
  2188. //
  2189. m_ListenConnectInProgress = TRUE;
  2190. //
  2191. // Start listening timer
  2192. //
  2193. if( 0 != timeout )
  2194. {
  2195. m_ListenTimeoutTimerID = SetTimer( (UINT_PTR)WM_LISTENTIMEOUT_TIMER, (UINT)(timeout * 1000) );
  2196. if( (UINT_PTR)0 == m_ListenTimeoutTimerID )
  2197. {
  2198. DWORD dwStatus;
  2199. // Failed to create a timer
  2200. dwStatus = GetLastError();
  2201. TRC_ERR((TB, _T("SetTimer failed - %d"),dwStatus));
  2202. hr = HRESULT_FROM_WIN32( dwStatus );
  2203. }
  2204. }
  2205. else
  2206. {
  2207. m_ListenTimeoutTimerID = (UINT_PTR)0;
  2208. }
  2209. CLEANUPANDEXIT:
  2210. if( FAILED(hr) ) {
  2211. StopListen();
  2212. }
  2213. DC_END_FN();
  2214. return hr;
  2215. }
  2216. HRESULT
  2217. CTSRDPRemoteDesktopClient::RetrieveUserConnectParm(
  2218. BSTR* pConnectParm
  2219. )
  2220. /*++
  2221. Routine Description:
  2222. Retrieve Salem connection parameter to this expert.
  2223. Parameters:
  2224. pConnectParm : Pointer to BSTR to receive connect parm.
  2225. Returns:
  2226. S_OK or error code.
  2227. --*/
  2228. {
  2229. LPTSTR pszAddress = NULL;
  2230. int BufSize = 0;
  2231. CComBSTR bstrConnParm;
  2232. DWORD dwRetry;
  2233. HRESULT hRes;
  2234. DWORD dwBufferRequire;
  2235. DWORD dwNumChars;
  2236. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::RetrieveUserConnectParm");
  2237. if( NULL == pConnectParm )
  2238. {
  2239. hRes = E_POINTER;
  2240. goto CLEANUPANDEXIT;
  2241. }
  2242. //
  2243. // Address might have change which might require bigger buffer, retry
  2244. //
  2245. //
  2246. for(dwRetry=0; dwRetry < MAX_FETCHIPADDRESSRETRY; dwRetry++) {
  2247. if( NULL != pszAddress ) {
  2248. LocalFree( pszAddress );
  2249. }
  2250. //
  2251. // Fetch all address on local machine.
  2252. //
  2253. dwBufferRequire = FetchAllAddresses( NULL, 0 );
  2254. if( 0 == dwBufferRequire ) {
  2255. hRes = E_UNEXPECTED;
  2256. ASSERT(FALSE);
  2257. goto CLEANUPANDEXIT;
  2258. }
  2259. pszAddress = (LPTSTR) LocalAlloc( LPTR, sizeof(TCHAR)*(dwBufferRequire+1) );
  2260. if( NULL == pszAddress ) {
  2261. hRes = E_OUTOFMEMORY;
  2262. goto CLEANUPANDEXIT;
  2263. }
  2264. dwNumChars = FetchAllAddresses( pszAddress, dwBufferRequire );
  2265. ASSERT( dwNumChars <= dwBufferRequire );
  2266. if( dwNumChars <= dwBufferRequire ) {
  2267. break;
  2268. }
  2269. }
  2270. if( NULL == pszAddress ) {
  2271. hRes = E_UNEXPECTED;
  2272. goto CLEANUPANDEXIT;
  2273. }
  2274. bstrConnParm = pszAddress;
  2275. *pConnectParm = bstrConnParm.Copy();
  2276. if( NULL == *pConnectParm ) {
  2277. hRes = E_OUTOFMEMORY;
  2278. }
  2279. CLEANUPANDEXIT:
  2280. if( NULL != pszAddress ) {
  2281. LocalFree(pszAddress);
  2282. }
  2283. DC_END_FN();
  2284. return hRes;
  2285. }
  2286. STDMETHODIMP
  2287. CTSRDPRemoteDesktopClient::put_ColorDepth(
  2288. LONG val
  2289. )
  2290. /*++
  2291. Routine Description:
  2292. Set Color depth
  2293. Arguments:
  2294. val - Value in bits perpel to set
  2295. Return Value:
  2296. S_OK on success. Otherwise, an error code is returned.
  2297. --*/
  2298. {
  2299. HRESULT hr;
  2300. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::put_ColorDepth");
  2301. if (!IsValid()) {
  2302. ASSERT(FALSE);
  2303. hr = E_FAIL;
  2304. goto CLEANUPANDEXIT;
  2305. }
  2306. hr = m_TSClient->put_ColorDepth(val);
  2307. if (hr != S_OK) {
  2308. TRC_ERR((TB, L"put_ColorDepth: %08X", hr));
  2309. goto CLEANUPANDEXIT;
  2310. }
  2311. CLEANUPANDEXIT:
  2312. DC_END_FN();
  2313. return hr;
  2314. }
  2315. STDMETHODIMP
  2316. CTSRDPRemoteDesktopClient::get_ColorDepth(
  2317. LONG *pVal
  2318. )
  2319. /*++
  2320. Routine Description:
  2321. Get Color depth
  2322. Arguments:
  2323. pVal - address to place the colordepth value in
  2324. Return Value:
  2325. S_OK on success. Otherwise, an error code is returned.
  2326. --*/
  2327. {
  2328. HRESULT hr;
  2329. IMsRdpClientAdvancedSettings *pAdvSettings = NULL;
  2330. DC_BEGIN_FN("CTSRDPRemoteDesktopClient::get_ColorDepth");
  2331. if (!IsValid()) {
  2332. ASSERT(FALSE);
  2333. hr = E_FAIL;
  2334. goto CLEANUPANDEXIT;
  2335. }
  2336. hr = m_TSClient->get_ColorDepth(pVal);
  2337. if (hr != S_OK) {
  2338. TRC_ERR((TB, L"get_ColorDepth: %08X", hr));
  2339. goto CLEANUPANDEXIT;
  2340. }
  2341. CLEANUPANDEXIT:
  2342. DC_END_FN();
  2343. return hr;
  2344. }