• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
operator <<(std::ostream & os,const ScreenResolution & resolution)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 
~DaemonProcess()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 
OnConfigUpdated(const std::string & serialized_config)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 
OnConfigWatcherError()64 void DaemonProcess::OnConfigWatcherError() {
65   DCHECK(caller_task_runner()->BelongsToCurrentThread());
66 
67   Stop();
68 }
69 
AddStatusObserver(HostStatusObserver * observer)70 void DaemonProcess::AddStatusObserver(HostStatusObserver* observer) {
71   DCHECK(caller_task_runner()->BelongsToCurrentThread());
72 
73   status_observers_.AddObserver(observer);
74 }
75 
RemoveStatusObserver(HostStatusObserver * observer)76 void DaemonProcess::RemoveStatusObserver(HostStatusObserver* observer) {
77   DCHECK(caller_task_runner()->BelongsToCurrentThread());
78 
79   status_observers_.RemoveObserver(observer);
80 }
81 
OnChannelConnected(int32 peer_pid)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 
OnMessageReceived(const IPC::Message & message)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 
OnPermanentError(int exit_code)134 void DaemonProcess::OnPermanentError(int exit_code) {
135   DCHECK(caller_task_runner()->BelongsToCurrentThread());
136   Stop();
137 }
138 
CloseDesktopSession(int terminal_id)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 
DaemonProcess(scoped_refptr<AutoThreadTaskRunner> caller_task_runner,scoped_refptr<AutoThreadTaskRunner> io_task_runner,const base::Closure & stopped_callback)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 
CreateDesktopSession(int terminal_id,const ScreenResolution & resolution,bool virtual_terminal)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 
SetScreenResolution(int terminal_id,const ScreenResolution & resolution)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 
CrashNetworkProcess(const tracked_objects::Location & location)251 void DaemonProcess::CrashNetworkProcess(
252     const tracked_objects::Location& location) {
253   DCHECK(caller_task_runner()->BelongsToCurrentThread());
254 
255   DoCrashNetworkProcess(location);
256   DeleteAllDesktopSessions();
257 }
258 
Initialize()259 void DaemonProcess::Initialize() {
260   DCHECK(caller_task_runner()->BelongsToCurrentThread());
261 
262   const CommandLine* command_line = 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 
Stop()280 void 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 
WasTerminalIdAllocated(int terminal_id)290 bool DaemonProcess::WasTerminalIdAllocated(int terminal_id) {
291   return terminal_id < next_terminal_id_;
292 }
293 
OnAccessDenied(const std::string & jid)294 void DaemonProcess::OnAccessDenied(const std::string& jid) {
295   DCHECK(caller_task_runner()->BelongsToCurrentThread());
296 
297   FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, OnAccessDenied(jid));
298 }
299 
OnClientAuthenticated(const std::string & jid)300 void 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 
OnClientConnected(const std::string & jid)307 void 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 
OnClientDisconnected(const std::string & jid)314 void 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 
OnClientRouteChange(const std::string & jid,const std::string & channel_name,const SerializedTransportRoute & route)321 void 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 
OnHostStarted(const std::string & xmpp_login)360 void 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 
OnHostShutdown()366 void DaemonProcess::OnHostShutdown() {
367   DCHECK(caller_task_runner()->BelongsToCurrentThread());
368 
369   FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, OnShutdown());
370 }
371 
DeleteAllDesktopSessions()372 void DaemonProcess::DeleteAllDesktopSessions() {
373   while (!desktop_sessions_.empty()) {
374     delete desktop_sessions_.front();
375     desktop_sessions_.pop_front();
376   }
377 }
378 
379 }  // namespace remoting
380