Home | History | Annotate | Download | only in host
      1 // Copyright (c) 2012 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/daemon_process.h"
      6 
      7 #include <algorithm>
      8 #include <string>
      9 
     10 #include "base/bind.h"
     11 #include "base/bind_helpers.h"
     12 #include "base/command_line.h"
     13 #include "base/file_util.h"
     14 #include "base/files/file_path.h"
     15 #include "base/location.h"
     16 #include "base/single_thread_task_runner.h"
     17 #include "net/base/net_util.h"
     18 #include "remoting/base/auto_thread_task_runner.h"
     19 #include "remoting/base/url_request_context.h"
     20 #include "remoting/host/branding.h"
     21 #include "remoting/host/chromoting_messages.h"
     22 #include "remoting/host/config_file_watcher.h"
     23 #include "remoting/host/desktop_session.h"
     24 #include "remoting/host/host_event_logger.h"
     25 #include "remoting/host/host_status_observer.h"
     26 #include "remoting/host/screen_resolution.h"
     27 #include "remoting/protocol/transport.h"
     28 
     29 namespace remoting {
     30 
     31 namespace {
     32 
     33 // This is used for tagging system event logs.
     34 const char kApplicationName[] = "chromoting";
     35 
     36 std::ostream& operator<<(std::ostream& os, const ScreenResolution& resolution) {
     37   return os << resolution.dimensions().width() << "x"
     38             << resolution.dimensions().height() << " at "
     39             << resolution.dpi().x() << "x" << resolution.dpi().y() << " DPI";
     40 }
     41 
     42 }  // namespace
     43 
     44 DaemonProcess::~DaemonProcess() {
     45   DCHECK(caller_task_runner()->BelongsToCurrentThread());
     46 
     47   host_event_logger_.reset();
     48   weak_factory_.InvalidateWeakPtrs();
     49 
     50   config_watcher_.reset();
     51   DeleteAllDesktopSessions();
     52 }
     53 
     54 void DaemonProcess::OnConfigUpdated(const std::string& serialized_config) {
     55   DCHECK(caller_task_runner()->BelongsToCurrentThread());
     56 
     57   if (serialized_config_ != serialized_config) {
     58     serialized_config_ = serialized_config;
     59     SendToNetwork(
     60         new ChromotingDaemonNetworkMsg_Configuration(serialized_config_));
     61   }
     62 }
     63 
     64 void DaemonProcess::OnConfigWatcherError() {
     65   DCHECK(caller_task_runner()->BelongsToCurrentThread());
     66 
     67   Stop();
     68 }
     69 
     70 void DaemonProcess::AddStatusObserver(HostStatusObserver* observer) {
     71   DCHECK(caller_task_runner()->BelongsToCurrentThread());
     72 
     73   status_observers_.AddObserver(observer);
     74 }
     75 
     76 void DaemonProcess::RemoveStatusObserver(HostStatusObserver* observer) {
     77   DCHECK(caller_task_runner()->BelongsToCurrentThread());
     78 
     79   status_observers_.RemoveObserver(observer);
     80 }
     81 
     82 void DaemonProcess::OnChannelConnected(int32 peer_pid) {
     83   DCHECK(caller_task_runner()->BelongsToCurrentThread());
     84 
     85   VLOG(1) << "IPC: daemon <- network (" << peer_pid << ")";
     86 
     87   DeleteAllDesktopSessions();
     88 
     89   // Reset the last known terminal ID because no IDs have been allocated
     90   // by the the newly started process yet.
     91   next_terminal_id_ = 0;
     92 
     93   // Send the configuration to the network process.
     94   SendToNetwork(
     95       new ChromotingDaemonNetworkMsg_Configuration(serialized_config_));
     96 }
     97 
     98 bool DaemonProcess::OnMessageReceived(const IPC::Message& message) {
     99   DCHECK(caller_task_runner()->BelongsToCurrentThread());
    100 
    101   bool handled = true;
    102   IPC_BEGIN_MESSAGE_MAP(DaemonProcess, message)
    103     IPC_MESSAGE_HANDLER(ChromotingNetworkHostMsg_ConnectTerminal,
    104                         CreateDesktopSession)
    105     IPC_MESSAGE_HANDLER(ChromotingNetworkHostMsg_DisconnectTerminal,
    106                         CloseDesktopSession)
    107     IPC_MESSAGE_HANDLER(ChromotingNetworkDaemonMsg_SetScreenResolution,
    108                         SetScreenResolution)
    109     IPC_MESSAGE_HANDLER(ChromotingNetworkDaemonMsg_AccessDenied,
    110                         OnAccessDenied)
    111     IPC_MESSAGE_HANDLER(ChromotingNetworkDaemonMsg_ClientAuthenticated,
    112                         OnClientAuthenticated)
    113     IPC_MESSAGE_HANDLER(ChromotingNetworkDaemonMsg_ClientConnected,
    114                         OnClientConnected)
    115     IPC_MESSAGE_HANDLER(ChromotingNetworkDaemonMsg_ClientDisconnected,
    116                         OnClientDisconnected)
    117     IPC_MESSAGE_HANDLER(ChromotingNetworkDaemonMsg_ClientRouteChange,
    118                         OnClientRouteChange)
    119     IPC_MESSAGE_HANDLER(ChromotingNetworkDaemonMsg_HostStarted,
    120                         OnHostStarted)
    121     IPC_MESSAGE_HANDLER(ChromotingNetworkDaemonMsg_HostShutdown,
    122                         OnHostShutdown)
    123     IPC_MESSAGE_UNHANDLED(handled = false)
    124   IPC_END_MESSAGE_MAP()
    125 
    126   if (!handled) {
    127     LOG(ERROR) << "Received unexpected IPC type: " << message.type();
    128     CrashNetworkProcess(FROM_HERE);
    129   }
    130 
    131   return handled;
    132 }
    133 
    134 void DaemonProcess::OnPermanentError(int exit_code) {
    135   DCHECK(caller_task_runner()->BelongsToCurrentThread());
    136   Stop();
    137 }
    138 
    139 void DaemonProcess::CloseDesktopSession(int terminal_id) {
    140   DCHECK(caller_task_runner()->BelongsToCurrentThread());
    141 
    142   // Validate the supplied terminal ID. An attempt to use a desktop session ID
    143   // that couldn't possibly have been allocated is considered a protocol error
    144   // and the network process will be restarted.
    145   if (!WasTerminalIdAllocated(terminal_id)) {
    146     LOG(ERROR) << "Invalid terminal ID: " << terminal_id;
    147     CrashNetworkProcess(FROM_HERE);
    148     return;
    149   }
    150 
    151   DesktopSessionList::iterator i;
    152   for (i = desktop_sessions_.begin(); i != desktop_sessions_.end(); ++i) {
    153     if ((*i)->id() == terminal_id) {
    154       break;
    155     }
    156   }
    157 
    158   // It is OK if the terminal ID wasn't found. There is a race between
    159   // the network and daemon processes. Each frees its own recources first and
    160   // notifies the other party if there was something to clean up.
    161   if (i == desktop_sessions_.end())
    162     return;
    163 
    164   delete *i;
    165   desktop_sessions_.erase(i);
    166 
    167   VLOG(1) << "Daemon: closed desktop session " << terminal_id;
    168   SendToNetwork(
    169       new ChromotingDaemonNetworkMsg_TerminalDisconnected(terminal_id));
    170 }
    171 
    172 DaemonProcess::DaemonProcess(
    173     scoped_refptr<AutoThreadTaskRunner> caller_task_runner,
    174     scoped_refptr<AutoThreadTaskRunner> io_task_runner,
    175     const base::Closure& stopped_callback)
    176     : caller_task_runner_(caller_task_runner),
    177       io_task_runner_(io_task_runner),
    178       next_terminal_id_(0),
    179       stopped_callback_(stopped_callback),
    180       weak_factory_(this) {
    181   DCHECK(caller_task_runner->BelongsToCurrentThread());
    182 }
    183 
    184 void DaemonProcess::CreateDesktopSession(int terminal_id,
    185                                          const ScreenResolution& resolution,
    186                                          bool virtual_terminal) {
    187   DCHECK(caller_task_runner()->BelongsToCurrentThread());
    188 
    189   // Validate the supplied terminal ID. An attempt to create a desktop session
    190   // with an ID that could possibly have been allocated already is considered
    191   // a protocol error and the network process will be restarted.
    192   if (WasTerminalIdAllocated(terminal_id)) {
    193     LOG(ERROR) << "Invalid terminal ID: " << terminal_id;
    194     CrashNetworkProcess(FROM_HERE);
    195     return;
    196   }
    197 
    198   // Terminal IDs cannot be reused. Update the expected next terminal ID.
    199   next_terminal_id_ = std::max(next_terminal_id_, terminal_id + 1);
    200 
    201   // Create the desktop session.
    202   scoped_ptr<DesktopSession> session = DoCreateDesktopSession(
    203       terminal_id, resolution, virtual_terminal);
    204   if (!session) {
    205     LOG(ERROR) << "Failed to create a desktop session.";
    206     SendToNetwork(
    207         new ChromotingDaemonNetworkMsg_TerminalDisconnected(terminal_id));
    208     return;
    209   }
    210 
    211   VLOG(1) << "Daemon: opened desktop session " << terminal_id;
    212   desktop_sessions_.push_back(session.release());
    213 }
    214 
    215 void DaemonProcess::SetScreenResolution(int terminal_id,
    216                                         const ScreenResolution& resolution) {
    217   DCHECK(caller_task_runner()->BelongsToCurrentThread());
    218 
    219   // Validate the supplied terminal ID. An attempt to use a desktop session ID
    220   // that couldn't possibly have been allocated is considered a protocol error
    221   // and the network process will be restarted.
    222   if (!WasTerminalIdAllocated(terminal_id)) {
    223     LOG(ERROR) << "Invalid terminal ID: " << terminal_id;
    224     CrashNetworkProcess(FROM_HERE);
    225     return;
    226   }
    227 
    228   // Validate |resolution| and restart the sender if it is not valid.
    229   if (resolution.IsEmpty()) {
    230     LOG(ERROR) << "Invalid resolution specified: " << resolution;
    231     CrashNetworkProcess(FROM_HERE);
    232     return;
    233   }
    234 
    235   DesktopSessionList::iterator i;
    236   for (i = desktop_sessions_.begin(); i != desktop_sessions_.end(); ++i) {
    237     if ((*i)->id() == terminal_id) {
    238       break;
    239     }
    240   }
    241 
    242   // It is OK if the terminal ID wasn't found. There is a race between
    243   // the network and daemon processes. Each frees its own resources first and
    244   // notifies the other party if there was something to clean up.
    245   if (i == desktop_sessions_.end())
    246     return;
    247 
    248   (*i)->SetScreenResolution(resolution);
    249 }
    250 
    251 void DaemonProcess::CrashNetworkProcess(
    252     const tracked_objects::Location& location) {
    253   DCHECK(caller_task_runner()->BelongsToCurrentThread());
    254 
    255   DoCrashNetworkProcess(location);
    256   DeleteAllDesktopSessions();
    257 }
    258 
    259 void DaemonProcess::Initialize() {
    260   DCHECK(caller_task_runner()->BelongsToCurrentThread());
    261 
    262   const base::CommandLine* command_line =
    263       base::CommandLine::ForCurrentProcess();
    264   // Get the name of the host configuration file.
    265   base::FilePath default_config_dir = remoting::GetConfigDir();
    266   base::FilePath config_path = default_config_dir.Append(
    267       kDefaultHostConfigFile);
    268   if (command_line->HasSwitch(kHostConfigSwitchName)) {
    269     config_path = command_line->GetSwitchValuePath(kHostConfigSwitchName);
    270   }
    271   config_watcher_.reset(new ConfigFileWatcher(
    272       caller_task_runner(), io_task_runner(), config_path));
    273   config_watcher_->Watch(this);
    274   host_event_logger_ =
    275       HostEventLogger::Create(weak_factory_.GetWeakPtr(), kApplicationName);
    276 
    277   // Launch the process.
    278   LaunchNetworkProcess();
    279 }
    280 
    281 void DaemonProcess::Stop() {
    282   DCHECK(caller_task_runner()->BelongsToCurrentThread());
    283 
    284   if (!stopped_callback_.is_null()) {
    285     base::Closure stopped_callback = stopped_callback_;
    286     stopped_callback_.Reset();
    287     stopped_callback.Run();
    288   }
    289 }
    290 
    291 bool DaemonProcess::WasTerminalIdAllocated(int terminal_id) {
    292   return terminal_id < next_terminal_id_;
    293 }
    294 
    295 void DaemonProcess::OnAccessDenied(const std::string& jid) {
    296   DCHECK(caller_task_runner()->BelongsToCurrentThread());
    297 
    298   FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, OnAccessDenied(jid));
    299 }
    300 
    301 void DaemonProcess::OnClientAuthenticated(const std::string& jid) {
    302   DCHECK(caller_task_runner()->BelongsToCurrentThread());
    303 
    304   FOR_EACH_OBSERVER(HostStatusObserver, status_observers_,
    305                     OnClientAuthenticated(jid));
    306 }
    307 
    308 void DaemonProcess::OnClientConnected(const std::string& jid) {
    309   DCHECK(caller_task_runner()->BelongsToCurrentThread());
    310 
    311   FOR_EACH_OBSERVER(HostStatusObserver, status_observers_,
    312                     OnClientConnected(jid));
    313 }
    314 
    315 void DaemonProcess::OnClientDisconnected(const std::string& jid) {
    316   DCHECK(caller_task_runner()->BelongsToCurrentThread());
    317 
    318   FOR_EACH_OBSERVER(HostStatusObserver, status_observers_,
    319                     OnClientDisconnected(jid));
    320 }
    321 
    322 void DaemonProcess::OnClientRouteChange(const std::string& jid,
    323                                         const std::string& channel_name,
    324                                         const SerializedTransportRoute& route) {
    325   DCHECK(caller_task_runner()->BelongsToCurrentThread());
    326 
    327   // Validate |route|.
    328   if (route.type != protocol::TransportRoute::DIRECT &&
    329       route.type != protocol::TransportRoute::STUN &&
    330       route.type != protocol::TransportRoute::RELAY) {
    331     LOG(ERROR) << "An invalid RouteType " << route.type << " passed.";
    332     CrashNetworkProcess(FROM_HERE);
    333     return;
    334   }
    335   if (route.remote_address.size() != net::kIPv4AddressSize &&
    336       route.remote_address.size() != net::kIPv6AddressSize) {
    337     LOG(ERROR) << "An invalid net::IPAddressNumber size "
    338                << route.remote_address.size() << " passed.";
    339     CrashNetworkProcess(FROM_HERE);
    340     return;
    341   }
    342   if (route.local_address.size() != net::kIPv4AddressSize &&
    343       route.local_address.size() != net::kIPv6AddressSize) {
    344     LOG(ERROR) << "An invalid net::IPAddressNumber size "
    345                << route.local_address.size() << " passed.";
    346     CrashNetworkProcess(FROM_HERE);
    347     return;
    348   }
    349 
    350   protocol::TransportRoute parsed_route;
    351   parsed_route.type =
    352       static_cast<protocol::TransportRoute::RouteType>(route.type);
    353   parsed_route.remote_address =
    354       net::IPEndPoint(route.remote_address, route.remote_port);
    355   parsed_route.local_address =
    356       net::IPEndPoint(route.local_address, route.local_port);
    357   FOR_EACH_OBSERVER(HostStatusObserver, status_observers_,
    358                     OnClientRouteChange(jid, channel_name, parsed_route));
    359 }
    360 
    361 void DaemonProcess::OnHostStarted(const std::string& xmpp_login) {
    362   DCHECK(caller_task_runner()->BelongsToCurrentThread());
    363 
    364   FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, OnStart(xmpp_login));
    365 }
    366 
    367 void DaemonProcess::OnHostShutdown() {
    368   DCHECK(caller_task_runner()->BelongsToCurrentThread());
    369 
    370   FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, OnShutdown());
    371 }
    372 
    373 void DaemonProcess::DeleteAllDesktopSessions() {
    374   while (!desktop_sessions_.empty()) {
    375     delete desktop_sessions_.front();
    376     desktop_sessions_.pop_front();
    377   }
    378 }
    379 
    380 }  // namespace remoting
    381