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 <cstdint>
10
11 #include "base/bind.h"
12 #include "base/callback_helpers.h"
13 #include "base/check_op.h"
14 #include "base/macros.h"
15 #include "base/numerics/safe_conversions.h"
16 #include "base/single_thread_task_runner.h"
17 #include "base/task/current_thread.h"
18 #include "net/base/ip_address.h"
19 #include "net/base/ip_endpoint.h"
20 #include "remoting/base/typed_buffer.h"
21 #include "remoting/host/screen_resolution.h"
22 #include "remoting/host/win/rdp_client_window.h"
23
24 namespace remoting {
25
26 namespace {
27
28 // 127.0.0.1 is explicitly blocked by the RDP ActiveX control, so we use
29 // 127.0.0.2 instead.
30 const unsigned char kRdpLoopbackAddress[] = { 127, 0, 0, 2 };
31
32 } // namespace
33
34 // The core of RdpClient is ref-counted since it services calls and notifies
35 // events on the caller task runner, but runs the ActiveX control on the UI
36 // task runner.
37 class RdpClient::Core
38 : public base::RefCountedThreadSafe<Core>,
39 public RdpClientWindow::EventHandler {
40 public:
41 Core(
42 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
43 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
44 RdpClient::EventHandler* event_handler);
45
46 // Initiates a loopback RDP connection.
47 void Connect(const ScreenResolution& resolution,
48 const std::string& terminal_id,
49 DWORD port_number);
50
51 // Initiates a graceful shutdown of the RDP connection.
52 void Disconnect();
53
54 // Sends Secure Attention Sequence to the session.
55 void InjectSas();
56
57 // Change the resolution of the desktop.
58 void ChangeResolution(const ScreenResolution& resolution);
59
60 // RdpClientWindow::EventHandler interface.
61 void OnConnected() override;
62 void OnDisconnected() override;
63
64 private:
65 friend class base::RefCountedThreadSafe<Core>;
66 ~Core() override;
67
68 // Helpers for the event handler's methods that make sure that OnRdpClosed()
69 // is the last notification delivered and is delevered only once.
70 void NotifyConnected();
71 void NotifyClosed();
72
73 // Task runner on which the caller expects |event_handler_| to be notified.
74 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner_;
75
76 // Task runner on which |rdp_client_window_| is running.
77 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
78
79 // Event handler receiving notification about connection state. The pointer is
80 // cleared when Disconnect() methods is called, stopping any further updates.
81 RdpClient::EventHandler* event_handler_;
82
83 // Hosts the RDP ActiveX control.
84 std::unique_ptr<RdpClientWindow> rdp_client_window_;
85
86 // A self-reference to keep the object alive during connection shutdown.
87 scoped_refptr<Core> self_;
88
89 DISALLOW_COPY_AND_ASSIGN(Core);
90 };
91
RdpClient(scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,const ScreenResolution & resolution,const std::string & terminal_id,DWORD port_number,EventHandler * event_handler)92 RdpClient::RdpClient(
93 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
94 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
95 const ScreenResolution& resolution,
96 const std::string& terminal_id,
97 DWORD port_number,
98 EventHandler* event_handler) {
99 DCHECK(caller_task_runner->BelongsToCurrentThread());
100
101 core_ = new Core(caller_task_runner, ui_task_runner, event_handler);
102 core_->Connect(resolution, terminal_id, port_number);
103 }
104
~RdpClient()105 RdpClient::~RdpClient() {
106 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
107
108 core_->Disconnect();
109 }
110
InjectSas()111 void RdpClient::InjectSas() {
112 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
113
114 core_->InjectSas();
115 }
116
ChangeResolution(const ScreenResolution & resolution)117 void RdpClient::ChangeResolution(const ScreenResolution& resolution) {
118 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
119
120 core_->ChangeResolution(resolution);
121 }
122
Core(scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,RdpClient::EventHandler * event_handler)123 RdpClient::Core::Core(
124 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
125 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
126 RdpClient::EventHandler* event_handler)
127 : caller_task_runner_(caller_task_runner),
128 ui_task_runner_(ui_task_runner),
129 event_handler_(event_handler) {
130 }
131
Connect(const ScreenResolution & resolution,const std::string & terminal_id,DWORD port_number)132 void RdpClient::Core::Connect(const ScreenResolution& resolution,
133 const std::string& terminal_id,
134 DWORD port_number) {
135 if (!ui_task_runner_->BelongsToCurrentThread()) {
136 ui_task_runner_->PostTask(
137 FROM_HERE, base::BindOnce(&Core::Connect, this, resolution, terminal_id,
138 port_number));
139 return;
140 }
141
142 DCHECK(base::CurrentUIThread::IsSet());
143 DCHECK(!rdp_client_window_);
144 DCHECK(!self_.get());
145
146 net::IPEndPoint server_endpoint(net::IPAddress(kRdpLoopbackAddress),
147 base::checked_cast<uint16_t>(port_number));
148
149 // Create the ActiveX control window.
150 rdp_client_window_.reset(new RdpClientWindow(server_endpoint, terminal_id,
151 this));
152 if (!rdp_client_window_->Connect(resolution)) {
153 rdp_client_window_.reset();
154
155 // Notify the caller that connection attempt failed.
156 NotifyClosed();
157 }
158 }
159
Disconnect()160 void RdpClient::Core::Disconnect() {
161 if (!ui_task_runner_->BelongsToCurrentThread()) {
162 ui_task_runner_->PostTask(FROM_HERE,
163 base::BindOnce(&Core::Disconnect, this));
164 return;
165 }
166
167 // The caller does not expect any notifications to be delivered after this
168 // point.
169 event_handler_ = nullptr;
170
171 // Gracefully shutdown the RDP connection.
172 if (rdp_client_window_) {
173 self_ = this;
174 rdp_client_window_->Disconnect();
175 }
176 }
177
InjectSas()178 void RdpClient::Core::InjectSas() {
179 if (!ui_task_runner_->BelongsToCurrentThread()) {
180 ui_task_runner_->PostTask(FROM_HERE,
181 base::BindOnce(&Core::InjectSas, this));
182 return;
183 }
184
185 if (rdp_client_window_) {
186 rdp_client_window_->InjectSas();
187 }
188 }
189
ChangeResolution(const ScreenResolution & resolution)190 void RdpClient::Core::ChangeResolution(const ScreenResolution& resolution) {
191 if (!ui_task_runner_->BelongsToCurrentThread()) {
192 ui_task_runner_->PostTask(
193 FROM_HERE, base::BindOnce(&Core::ChangeResolution, this, resolution));
194 return;
195 }
196
197 if (rdp_client_window_) {
198 rdp_client_window_->ChangeResolution(resolution);
199 }
200 }
201
OnConnected()202 void RdpClient::Core::OnConnected() {
203 DCHECK(ui_task_runner_->BelongsToCurrentThread());
204 DCHECK(rdp_client_window_);
205
206 NotifyConnected();
207 }
208
OnDisconnected()209 void RdpClient::Core::OnDisconnected() {
210 DCHECK(ui_task_runner_->BelongsToCurrentThread());
211 DCHECK(rdp_client_window_);
212
213 NotifyClosed();
214
215 // Delay window destruction until no ActiveX control's code is on the stack.
216 ui_task_runner_->DeleteSoon(FROM_HERE, rdp_client_window_.release());
217 self_ = nullptr;
218 }
219
~Core()220 RdpClient::Core::~Core() {
221 DCHECK(!event_handler_);
222 DCHECK(!rdp_client_window_);
223 }
224
NotifyConnected()225 void RdpClient::Core::NotifyConnected() {
226 if (!caller_task_runner_->BelongsToCurrentThread()) {
227 caller_task_runner_->PostTask(FROM_HERE,
228 base::BindOnce(&Core::NotifyConnected, this));
229 return;
230 }
231
232 if (event_handler_)
233 event_handler_->OnRdpConnected();
234 }
235
NotifyClosed()236 void RdpClient::Core::NotifyClosed() {
237 if (!caller_task_runner_->BelongsToCurrentThread()) {
238 caller_task_runner_->PostTask(FROM_HERE,
239 base::BindOnce(&Core::NotifyClosed, this));
240 return;
241 }
242
243 if (event_handler_) {
244 RdpClient::EventHandler* event_handler = event_handler_;
245 event_handler_ = nullptr;
246 event_handler->OnRdpClosed();
247 }
248 }
249
250 } // namespace remoting
251