Home | History | Annotate | Download | only in win
      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 
     19 namespace remoting {
     20 
     21 namespace {
     22 
     23 // 127.0.0.1 is explicitly blocked by the RDP ActiveX control, so we use
     24 // 127.0.0.2 instead.
     25 const unsigned char kRdpLoopbackAddress[] = { 127, 0, 0, 2 };
     26 
     27 const int kDefaultRdpPort = 3389;
     28 
     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";
     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.
     39 class 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 
     90 RdpClient::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 
    102 RdpClient::~RdpClient() {
    103   DCHECK(CalledOnValidThread());
    104 
    105   core_->Disconnect();
    106 }
    107 
    108 void RdpClient::InjectSas() {
    109   DCHECK(CalledOnValidThread());
    110 
    111   core_->InjectSas();
    112 }
    113 
    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) {
    121 }
    122 
    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));
    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 
    159 void 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 
    176 void 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 
    186 void RdpClient::Core::OnConnected() {
    187   DCHECK(ui_task_runner_->BelongsToCurrentThread());
    188   DCHECK(rdp_client_window_);
    189 
    190   NotifyConnected();
    191 }
    192 
    193 void 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 
    204 RdpClient::Core::~Core() {
    205   DCHECK(!event_handler_);
    206   DCHECK(!rdp_client_window_);
    207 }
    208 
    209 void 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 
    220 void 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