1// Copyright (c) 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "remoting/host/win/rdp_client.h" 6 7#include <windows.h> 8 9#include "base/bind.h" 10#include "base/bind_helpers.h" 11#include "base/logging.h" 12#include "base/single_thread_task_runner.h" 13#include "base/win/registry.h" 14#include "net/base/ip_endpoint.h" 15#include "remoting/base/typed_buffer.h" 16#include "remoting/host/win/rdp_client_window.h" 17 18namespace remoting { 19 20namespace { 21 22// 127.0.0.1 is explicitly blocked by the RDP ActiveX control, so we use 23// 127.0.0.2 instead. 24const unsigned char kRdpLoopbackAddress[] = { 127, 0, 0, 2 }; 25 26const int kDefaultRdpPort = 3389; 27 28// The port number used by RDP is stored in the registry. 29const wchar_t kRdpPortKeyName[] = L"SYSTEM\\CurrentControlSet\\Control\\" 30 L"Terminal Server\\WinStations\\RDP-Tcp"; 31const wchar_t kRdpPortValueName[] = L"PortNumber"; 32 33} // namespace 34 35// The core of RdpClient is ref-counted since it services calls and notifies 36// events on the caller task runner, but runs the ActiveX control on the UI 37// task runner. 38class RdpClient::Core 39 : public base::RefCountedThreadSafe<Core>, 40 public RdpClientWindow::EventHandler { 41 public: 42 Core( 43 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner, 44 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, 45 RdpClient::EventHandler* event_handler); 46 47 // Initiates a loopback RDP connection. 48 void Connect(const SkISize& screen_size, const std::string& terminal_id); 49 50 // Initiates a graceful shutdown of the RDP connection. 51 void Disconnect(); 52 53 // Sends Secure Attention Sequence to the session. 54 void InjectSas(); 55 56 // RdpClientWindow::EventHandler interface. 57 virtual void OnConnected() OVERRIDE; 58 virtual void OnDisconnected() OVERRIDE; 59 60 private: 61 friend class base::RefCountedThreadSafe<Core>; 62 virtual ~Core(); 63 64 // Helpers for the event handler's methods that make sure that OnRdpClosed() 65 // is the last notification delivered and is delevered only once. 66 void NotifyConnected(); 67 void NotifyClosed(); 68 69 // Task runner on which the caller expects |event_handler_| to be notified. 70 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner_; 71 72 // Task runner on which |rdp_client_window_| is running. 73 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_; 74 75 // Event handler receiving notification about connection state. The pointer is 76 // cleared when Disconnect() methods is called, stopping any further updates. 77 RdpClient::EventHandler* event_handler_; 78 79 // Hosts the RDP ActiveX control. 80 scoped_ptr<RdpClientWindow> rdp_client_window_; 81 82 // A self-reference to keep the object alive during connection shutdown. 83 scoped_refptr<Core> self_; 84 85 DISALLOW_COPY_AND_ASSIGN(Core); 86}; 87 88RdpClient::RdpClient( 89 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner, 90 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, 91 const SkISize& screen_size, 92 const std::string& terminal_id, 93 EventHandler* event_handler) { 94 DCHECK(caller_task_runner->BelongsToCurrentThread()); 95 96 core_ = new Core(caller_task_runner, ui_task_runner, event_handler); 97 core_->Connect(screen_size, terminal_id); 98} 99 100RdpClient::~RdpClient() { 101 DCHECK(CalledOnValidThread()); 102 103 core_->Disconnect(); 104} 105 106void RdpClient::InjectSas() { 107 DCHECK(CalledOnValidThread()); 108 109 core_->InjectSas(); 110} 111 112RdpClient::Core::Core( 113 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner, 114 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, 115 RdpClient::EventHandler* event_handler) 116 : caller_task_runner_(caller_task_runner), 117 ui_task_runner_(ui_task_runner), 118 event_handler_(event_handler) { 119} 120 121void RdpClient::Core::Connect(const SkISize& screen_size, 122 const std::string& terminal_id) { 123 if (!ui_task_runner_->BelongsToCurrentThread()) { 124 ui_task_runner_->PostTask( 125 FROM_HERE, base::Bind(&Core::Connect, this, screen_size, terminal_id)); 126 return; 127 } 128 129 DCHECK_EQ(base::MessageLoop::current()->type(), base::MessageLoop::TYPE_UI); 130 DCHECK(!rdp_client_window_); 131 DCHECK(!self_); 132 133 // Read the port number used by RDP. 134 DWORD server_port; 135 base::win::RegKey key(HKEY_LOCAL_MACHINE, kRdpPortKeyName, KEY_READ); 136 if (!key.Valid() || 137 (key.ReadValueDW(kRdpPortValueName, &server_port) != ERROR_SUCCESS)) { 138 server_port = kDefaultRdpPort; 139 } 140 141 net::IPAddressNumber server_address( 142 kRdpLoopbackAddress, 143 kRdpLoopbackAddress + arraysize(kRdpLoopbackAddress)); 144 net::IPEndPoint server_endpoint(server_address, server_port); 145 146 // Create the ActiveX control window. 147 rdp_client_window_.reset(new RdpClientWindow(server_endpoint, terminal_id, 148 this)); 149 if (!rdp_client_window_->Connect(screen_size)) { 150 rdp_client_window_.reset(); 151 152 // Notify the caller that connection attempt failed. 153 NotifyClosed(); 154 } 155} 156 157void RdpClient::Core::Disconnect() { 158 if (!ui_task_runner_->BelongsToCurrentThread()) { 159 ui_task_runner_->PostTask(FROM_HERE, base::Bind(&Core::Disconnect, this)); 160 return; 161 } 162 163 // The caller does not expect any notifications to be delivered after this 164 // point. 165 event_handler_ = NULL; 166 167 // Gracefully shutdown the RDP connection. 168 if (rdp_client_window_) { 169 self_ = this; 170 rdp_client_window_->Disconnect(); 171 } 172} 173 174void RdpClient::Core::InjectSas() { 175 if (!ui_task_runner_->BelongsToCurrentThread()) { 176 ui_task_runner_->PostTask(FROM_HERE, base::Bind(&Core::InjectSas, this)); 177 return; 178 } 179 180 if (rdp_client_window_) 181 rdp_client_window_->InjectSas(); 182} 183 184void RdpClient::Core::OnConnected() { 185 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 186 DCHECK(rdp_client_window_); 187 188 NotifyConnected(); 189} 190 191void RdpClient::Core::OnDisconnected() { 192 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 193 DCHECK(rdp_client_window_); 194 195 NotifyClosed(); 196 197 // Delay window destruction until no ActiveX control's code is on the stack. 198 ui_task_runner_->DeleteSoon(FROM_HERE, rdp_client_window_.release()); 199 self_ = NULL; 200} 201 202RdpClient::Core::~Core() { 203 DCHECK(!event_handler_); 204 DCHECK(!rdp_client_window_); 205} 206 207void RdpClient::Core::NotifyConnected() { 208 if (!caller_task_runner_->BelongsToCurrentThread()) { 209 caller_task_runner_->PostTask( 210 FROM_HERE, base::Bind(&Core::NotifyConnected, this)); 211 return; 212 } 213 214 if (event_handler_) 215 event_handler_->OnRdpConnected(); 216} 217 218void RdpClient::Core::NotifyClosed() { 219 if (!caller_task_runner_->BelongsToCurrentThread()) { 220 caller_task_runner_->PostTask( 221 FROM_HERE, base::Bind(&Core::NotifyClosed, this)); 222 return; 223 } 224 225 if (event_handler_) { 226 RdpClient::EventHandler* event_handler = event_handler_; 227 event_handler_ = NULL; 228 event_handler->OnRdpClosed(); 229 } 230} 231 232} // namespace remoting 233