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