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