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 
     18 namespace remoting {
     19 
     20 namespace {
     21 
     22 // 127.0.0.1 is explicitly blocked by the RDP ActiveX control, so we use
     23 // 127.0.0.2 instead.
     24 const unsigned char kRdpLoopbackAddress[] = { 127, 0, 0, 2 };
     25 
     26 const int kDefaultRdpPort = 3389;
     27 
     28 // The port number used by RDP is stored in the registry.
     29 const wchar_t kRdpPortKeyName[] = L"SYSTEM\\CurrentControlSet\\Control\\"
     30     L"Terminal Server\\WinStations\\RDP-Tcp";
     31 const wchar_t kRdpPortValueName[] = L"PortNumber";
     32 
     33 }  // namespace
     34 
     35 // The core of RdpClient is ref-counted since it services calls and notifies
     36 // events on the caller task runner, but runs the ActiveX control on the UI
     37 // task runner.
     38 class RdpClient::Core
     39     : public base::RefCountedThreadSafe<Core>,
     40       public RdpClientWindow::EventHandler {
     41  public:
     42   Core(
     43       scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
     44       scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
     45       RdpClient::EventHandler* event_handler);
     46 
     47   // Initiates a loopback RDP connection.
     48   void Connect(const SkISize& screen_size, const std::string& terminal_id);
     49 
     50   // Initiates a graceful shutdown of the RDP connection.
     51   void Disconnect();
     52 
     53   // Sends Secure Attention Sequence to the session.
     54   void InjectSas();
     55 
     56   // RdpClientWindow::EventHandler interface.
     57   virtual void OnConnected() OVERRIDE;
     58   virtual void OnDisconnected() OVERRIDE;
     59 
     60  private:
     61   friend class base::RefCountedThreadSafe<Core>;
     62   virtual ~Core();
     63 
     64   // Helpers for the event handler's methods that make sure that OnRdpClosed()
     65   // is the last notification delivered and is delevered only once.
     66   void NotifyConnected();
     67   void NotifyClosed();
     68 
     69   // Task runner on which the caller expects |event_handler_| to be notified.
     70   scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner_;
     71 
     72   // Task runner on which |rdp_client_window_| is running.
     73   scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
     74 
     75   // Event handler receiving notification about connection state. The pointer is
     76   // cleared when Disconnect() methods is called, stopping any further updates.
     77   RdpClient::EventHandler* event_handler_;
     78 
     79   // Hosts the RDP ActiveX control.
     80   scoped_ptr<RdpClientWindow> rdp_client_window_;
     81 
     82   // A self-reference to keep the object alive during connection shutdown.
     83   scoped_refptr<Core> self_;
     84 
     85   DISALLOW_COPY_AND_ASSIGN(Core);
     86 };
     87 
     88 RdpClient::RdpClient(
     89     scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
     90     scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
     91     const SkISize& screen_size,
     92     const std::string& terminal_id,
     93     EventHandler* event_handler) {
     94   DCHECK(caller_task_runner->BelongsToCurrentThread());
     95 
     96   core_ = new Core(caller_task_runner, ui_task_runner, event_handler);
     97   core_->Connect(screen_size, terminal_id);
     98 }
     99 
    100 RdpClient::~RdpClient() {
    101   DCHECK(CalledOnValidThread());
    102 
    103   core_->Disconnect();
    104 }
    105 
    106 void RdpClient::InjectSas() {
    107   DCHECK(CalledOnValidThread());
    108 
    109   core_->InjectSas();
    110 }
    111 
    112 RdpClient::Core::Core(
    113     scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
    114     scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
    115     RdpClient::EventHandler* event_handler)
    116     : caller_task_runner_(caller_task_runner),
    117       ui_task_runner_(ui_task_runner),
    118       event_handler_(event_handler) {
    119 }
    120 
    121 void RdpClient::Core::Connect(const SkISize& screen_size,
    122                               const std::string& terminal_id) {
    123   if (!ui_task_runner_->BelongsToCurrentThread()) {
    124     ui_task_runner_->PostTask(
    125         FROM_HERE, base::Bind(&Core::Connect, this, screen_size, terminal_id));
    126     return;
    127   }
    128 
    129   DCHECK_EQ(base::MessageLoop::current()->type(), base::MessageLoop::TYPE_UI);
    130   DCHECK(!rdp_client_window_);
    131   DCHECK(!self_);
    132 
    133   // Read the port number used by RDP.
    134   DWORD server_port;
    135   base::win::RegKey key(HKEY_LOCAL_MACHINE, kRdpPortKeyName, KEY_READ);
    136   if (!key.Valid() ||
    137       (key.ReadValueDW(kRdpPortValueName, &server_port) != ERROR_SUCCESS)) {
    138     server_port = kDefaultRdpPort;
    139   }
    140 
    141   net::IPAddressNumber server_address(
    142       kRdpLoopbackAddress,
    143       kRdpLoopbackAddress + arraysize(kRdpLoopbackAddress));
    144   net::IPEndPoint server_endpoint(server_address, server_port);
    145 
    146   // Create the ActiveX control window.
    147   rdp_client_window_.reset(new RdpClientWindow(server_endpoint, terminal_id,
    148                                                this));
    149   if (!rdp_client_window_->Connect(screen_size)) {
    150     rdp_client_window_.reset();
    151 
    152     // Notify the caller that connection attempt failed.
    153     NotifyClosed();
    154   }
    155 }
    156 
    157 void RdpClient::Core::Disconnect() {
    158   if (!ui_task_runner_->BelongsToCurrentThread()) {
    159     ui_task_runner_->PostTask(FROM_HERE, base::Bind(&Core::Disconnect, this));
    160     return;
    161   }
    162 
    163   // The caller does not expect any notifications to be delivered after this
    164   // point.
    165   event_handler_ = NULL;
    166 
    167   // Gracefully shutdown the RDP connection.
    168   if (rdp_client_window_) {
    169     self_ = this;
    170     rdp_client_window_->Disconnect();
    171   }
    172 }
    173 
    174 void RdpClient::Core::InjectSas() {
    175   if (!ui_task_runner_->BelongsToCurrentThread()) {
    176     ui_task_runner_->PostTask(FROM_HERE, base::Bind(&Core::InjectSas, this));
    177     return;
    178   }
    179 
    180   if (rdp_client_window_)
    181     rdp_client_window_->InjectSas();
    182 }
    183 
    184 void RdpClient::Core::OnConnected() {
    185   DCHECK(ui_task_runner_->BelongsToCurrentThread());
    186   DCHECK(rdp_client_window_);
    187 
    188   NotifyConnected();
    189 }
    190 
    191 void RdpClient::Core::OnDisconnected() {
    192   DCHECK(ui_task_runner_->BelongsToCurrentThread());
    193   DCHECK(rdp_client_window_);
    194 
    195   NotifyClosed();
    196 
    197   // Delay window destruction until no ActiveX control's code is on the stack.
    198   ui_task_runner_->DeleteSoon(FROM_HERE, rdp_client_window_.release());
    199   self_ = NULL;
    200 }
    201 
    202 RdpClient::Core::~Core() {
    203   DCHECK(!event_handler_);
    204   DCHECK(!rdp_client_window_);
    205 }
    206 
    207 void RdpClient::Core::NotifyConnected() {
    208   if (!caller_task_runner_->BelongsToCurrentThread()) {
    209     caller_task_runner_->PostTask(
    210         FROM_HERE, base::Bind(&Core::NotifyConnected, this));
    211     return;
    212   }
    213 
    214   if (event_handler_)
    215     event_handler_->OnRdpConnected();
    216 }
    217 
    218 void RdpClient::Core::NotifyClosed() {
    219   if (!caller_task_runner_->BelongsToCurrentThread()) {
    220     caller_task_runner_->PostTask(
    221         FROM_HERE, base::Bind(&Core::NotifyClosed, this));
    222     return;
    223   }
    224 
    225   if (event_handler_) {
    226     RdpClient::EventHandler* event_handler = event_handler_;
    227     event_handler_ = NULL;
    228     event_handler->OnRdpClosed();
    229   }
    230 }
    231 
    232 }  // namespace remoting
    233