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"
17 #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
23 // 127.0.0.1 is explicitly blocked by the RDP ActiveX control, so we use
25 const unsigned char kRdpLoopbackAddress
[] = { 127, 0, 0, 2 };
27 const int kDefaultRdpPort
= 3389;
29 // The port number used by RDP is stored in the registry.
30 const wchar_t kRdpPortKeyName
[] = L
"SYSTEM\\CurrentControlSet\\Control\\"
31 L
"Terminal Server\\WinStations\\RDP-Tcp";
32 const wchar_t kRdpPortValueName
[] = L
"PortNumber";
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
40 : public base::RefCountedThreadSafe
<Core
>,
41 public RdpClientWindow::EventHandler
{
44 scoped_refptr
<base::SingleThreadTaskRunner
> caller_task_runner
,
45 scoped_refptr
<base::SingleThreadTaskRunner
> ui_task_runner
,
46 RdpClient::EventHandler
* event_handler
);
48 // Initiates a loopback RDP connection.
49 void Connect(const webrtc::DesktopSize
& screen_size
,
50 const std::string
& terminal_id
);
52 // Initiates a graceful shutdown of the RDP connection.
55 // Sends Secure Attention Sequence to the session.
58 // RdpClientWindow::EventHandler interface.
59 void OnConnected() override
;
60 void OnDisconnected() override
;
63 friend class base::RefCountedThreadSafe
<Core
>;
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();
71 // Task runner on which the caller expects |event_handler_| to be notified.
72 scoped_refptr
<base::SingleThreadTaskRunner
> caller_task_runner_
;
74 // Task runner on which |rdp_client_window_| is running.
75 scoped_refptr
<base::SingleThreadTaskRunner
> ui_task_runner_
;
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_
;
81 // Hosts the RDP ActiveX control.
82 scoped_ptr
<RdpClientWindow
> rdp_client_window_
;
84 // A self-reference to keep the object alive during connection shutdown.
85 scoped_refptr
<Core
> self_
;
87 DISALLOW_COPY_AND_ASSIGN(Core
);
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());
98 core_
= new Core(caller_task_runner
, ui_task_runner
, event_handler
);
99 core_
->Connect(screen_size
, terminal_id
);
102 RdpClient::~RdpClient() {
103 DCHECK(CalledOnValidThread());
108 void RdpClient::InjectSas() {
109 DCHECK(CalledOnValidThread());
114 RdpClient::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
) {
123 void 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
));
131 DCHECK(base::MessageLoopForUI::IsCurrent());
132 DCHECK(!rdp_client_window_
);
133 DCHECK(!self_
.get());
135 // Read the port number used by RDP.
137 base::win::RegKey
key(HKEY_LOCAL_MACHINE
, kRdpPortKeyName
, KEY_READ
);
139 (key
.ReadValueDW(kRdpPortValueName
, &server_port
) != ERROR_SUCCESS
) ||
140 server_port
> 65535) {
141 server_port
= kDefaultRdpPort
;
144 net::IPAddressNumber
server_address(
146 kRdpLoopbackAddress
+ arraysize(kRdpLoopbackAddress
));
147 net::IPEndPoint
server_endpoint(server_address
,
148 static_cast<uint16
>(server_port
));
150 // Create the ActiveX control window.
151 rdp_client_window_
.reset(new RdpClientWindow(server_endpoint
, terminal_id
,
153 if (!rdp_client_window_
->Connect(screen_size
)) {
154 rdp_client_window_
.reset();
156 // Notify the caller that connection attempt failed.
161 void RdpClient::Core::Disconnect() {
162 if (!ui_task_runner_
->BelongsToCurrentThread()) {
163 ui_task_runner_
->PostTask(FROM_HERE
, base::Bind(&Core::Disconnect
, this));
167 // The caller does not expect any notifications to be delivered after this
169 event_handler_
= nullptr;
171 // Gracefully shutdown the RDP connection.
172 if (rdp_client_window_
) {
174 rdp_client_window_
->Disconnect();
178 void RdpClient::Core::InjectSas() {
179 if (!ui_task_runner_
->BelongsToCurrentThread()) {
180 ui_task_runner_
->PostTask(FROM_HERE
, base::Bind(&Core::InjectSas
, this));
184 if (rdp_client_window_
)
185 rdp_client_window_
->InjectSas();
188 void RdpClient::Core::OnConnected() {
189 DCHECK(ui_task_runner_
->BelongsToCurrentThread());
190 DCHECK(rdp_client_window_
);
195 void RdpClient::Core::OnDisconnected() {
196 DCHECK(ui_task_runner_
->BelongsToCurrentThread());
197 DCHECK(rdp_client_window_
);
201 // Delay window destruction until no ActiveX control's code is on the stack.
202 ui_task_runner_
->DeleteSoon(FROM_HERE
, rdp_client_window_
.release());
206 RdpClient::Core::~Core() {
207 DCHECK(!event_handler_
);
208 DCHECK(!rdp_client_window_
);
211 void RdpClient::Core::NotifyConnected() {
212 if (!caller_task_runner_
->BelongsToCurrentThread()) {
213 caller_task_runner_
->PostTask(
214 FROM_HERE
, base::Bind(&Core::NotifyConnected
, this));
219 event_handler_
->OnRdpConnected();
222 void RdpClient::Core::NotifyClosed() {
223 if (!caller_task_runner_
->BelongsToCurrentThread()) {
224 caller_task_runner_
->PostTask(
225 FROM_HERE
, base::Bind(&Core::NotifyClosed
, this));
229 if (event_handler_
) {
230 RdpClient::EventHandler
* event_handler
= event_handler_
;
231 event_handler_
= nullptr;
232 event_handler
->OnRdpClosed();
236 } // namespace remoting