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