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.
5 #include "remoting/host/win/rdp_client.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"
22 // 127.0.0.1 is explicitly blocked by the RDP ActiveX control, so we use
24 const unsigned char kRdpLoopbackAddress[] = { 127, 0, 0, 2 };
26 const int kDefaultRdpPort = 3389;
28 // The port number used by RDP is stored in the registry.
29 const wchar_t kRdpPortKeyName[] = L"SYSTEM\\CurrentControlSet\\Control\\"
30 L"Terminal Server\\WinStations\\RDP-Tcp";
31 const wchar_t kRdpPortValueName[] = L"PortNumber";
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
39 : public base::RefCountedThreadSafe<Core>,
40 public RdpClientWindow::EventHandler {
43 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
44 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
45 RdpClient::EventHandler* event_handler);
47 // Initiates a loopback RDP connection.
48 void Connect(const SkISize& screen_size, const std::string& terminal_id);
50 // Initiates a graceful shutdown of the RDP connection.
53 // Sends Secure Attention Sequence to the session.
56 // RdpClientWindow::EventHandler interface.
57 virtual void OnConnected() OVERRIDE;
58 virtual void OnDisconnected() OVERRIDE;
61 friend class base::RefCountedThreadSafe<Core>;
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();
69 // Task runner on which the caller expects |event_handler_| to be notified.
70 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner_;
72 // Task runner on which |rdp_client_window_| is running.
73 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
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_;
79 // Hosts the RDP ActiveX control.
80 scoped_ptr<RdpClientWindow> rdp_client_window_;
82 // A self-reference to keep the object alive during connection shutdown.
83 scoped_refptr<Core> self_;
85 DISALLOW_COPY_AND_ASSIGN(Core);
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());
96 core_ = new Core(caller_task_runner, ui_task_runner, event_handler);
97 core_->Connect(screen_size, terminal_id);
100 RdpClient::~RdpClient() {
101 DCHECK(CalledOnValidThread());
106 void RdpClient::InjectSas() {
107 DCHECK(CalledOnValidThread());
112 RdpClient::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) {
121 void 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));
129 DCHECK_EQ(base::MessageLoop::current()->type(), base::MessageLoop::TYPE_UI);
130 DCHECK(!rdp_client_window_);
133 // Read the port number used by RDP.
135 base::win::RegKey key(HKEY_LOCAL_MACHINE, kRdpPortKeyName, KEY_READ);
137 (key.ReadValueDW(kRdpPortValueName, &server_port) != ERROR_SUCCESS)) {
138 server_port = kDefaultRdpPort;
141 net::IPAddressNumber server_address(
143 kRdpLoopbackAddress + arraysize(kRdpLoopbackAddress));
144 net::IPEndPoint server_endpoint(server_address, server_port);
146 // Create the ActiveX control window.
147 rdp_client_window_.reset(new RdpClientWindow(server_endpoint, terminal_id,
149 if (!rdp_client_window_->Connect(screen_size)) {
150 rdp_client_window_.reset();
152 // Notify the caller that connection attempt failed.
157 void RdpClient::Core::Disconnect() {
158 if (!ui_task_runner_->BelongsToCurrentThread()) {
159 ui_task_runner_->PostTask(FROM_HERE, base::Bind(&Core::Disconnect, this));
163 // The caller does not expect any notifications to be delivered after this
165 event_handler_ = NULL;
167 // Gracefully shutdown the RDP connection.
168 if (rdp_client_window_) {
170 rdp_client_window_->Disconnect();
174 void RdpClient::Core::InjectSas() {
175 if (!ui_task_runner_->BelongsToCurrentThread()) {
176 ui_task_runner_->PostTask(FROM_HERE, base::Bind(&Core::InjectSas, this));
180 if (rdp_client_window_)
181 rdp_client_window_->InjectSas();
184 void RdpClient::Core::OnConnected() {
185 DCHECK(ui_task_runner_->BelongsToCurrentThread());
186 DCHECK(rdp_client_window_);
191 void RdpClient::Core::OnDisconnected() {
192 DCHECK(ui_task_runner_->BelongsToCurrentThread());
193 DCHECK(rdp_client_window_);
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());
202 RdpClient::Core::~Core() {
203 DCHECK(!event_handler_);
204 DCHECK(!rdp_client_window_);
207 void RdpClient::Core::NotifyConnected() {
208 if (!caller_task_runner_->BelongsToCurrentThread()) {
209 caller_task_runner_->PostTask(
210 FROM_HERE, base::Bind(&Core::NotifyConnected, this));
215 event_handler_->OnRdpConnected();
218 void RdpClient::Core::NotifyClosed() {
219 if (!caller_task_runner_->BelongsToCurrentThread()) {
220 caller_task_runner_->PostTask(
221 FROM_HERE, base::Bind(&Core::NotifyClosed, this));
225 if (event_handler_) {
226 RdpClient::EventHandler* event_handler = event_handler_;
227 event_handler_ = NULL;
228 event_handler->OnRdpClosed();
232 } // namespace remoting