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