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
19namespace remoting {
20
21namespace {
22
23// 127.0.0.1 is explicitly blocked by the RDP ActiveX control, so we use
24// 127.0.0.2 instead.
25const unsigned char kRdpLoopbackAddress[] = { 127, 0, 0, 2 };
26
27const int kDefaultRdpPort = 3389;
28
29// The port number used by RDP is stored in the registry.
30const wchar_t kRdpPortKeyName[] = L"SYSTEM\\CurrentControlSet\\Control\\"
31    L"Terminal Server\\WinStations\\RDP-Tcp";
32const 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.
39class 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
90RdpClient::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
102RdpClient::~RdpClient() {
103  DCHECK(CalledOnValidThread());
104
105  core_->Disconnect();
106}
107
108void RdpClient::InjectSas() {
109  DCHECK(CalledOnValidThread());
110
111  core_->InjectSas();
112}
113
114RdpClient::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
123void 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(base::MessageLoopForUI::IsCurrent());
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
159void 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
176void 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
186void RdpClient::Core::OnConnected() {
187  DCHECK(ui_task_runner_->BelongsToCurrentThread());
188  DCHECK(rdp_client_window_);
189
190  NotifyConnected();
191}
192
193void 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
204RdpClient::Core::~Core() {
205  DCHECK(!event_handler_);
206  DCHECK(!rdp_client_window_);
207}
208
209void 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
220void 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