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
28 namespace remoting {
29
30 namespace {
31
32 // This is used for tagging system event logs.
33 const char kApplicationName[] = "chromoting";
34
operator <<(std::ostream & os,const ScreenResolution & resolution)35 std::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
~DaemonProcess()43 DaemonProcess::~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
OnConfigUpdated(const std::string & serialized_config)53 void 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
OnConfigWatcherError()63 void DaemonProcess::OnConfigWatcherError() {
64 DCHECK(caller_task_runner()->BelongsToCurrentThread());
65
66 Stop();
67 }
68
AddStatusObserver(HostStatusObserver * observer)69 void DaemonProcess::AddStatusObserver(HostStatusObserver* observer) {
70 DCHECK(caller_task_runner()->BelongsToCurrentThread());
71
72 status_observers_.AddObserver(observer);
73 }
74
RemoveStatusObserver(HostStatusObserver * observer)75 void DaemonProcess::RemoveStatusObserver(HostStatusObserver* observer) {
76 DCHECK(caller_task_runner()->BelongsToCurrentThread());
77
78 status_observers_.RemoveObserver(observer);
79 }
80
OnChannelConnected(int32 peer_pid)81 void 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
OnMessageReceived(const IPC::Message & message)97 bool 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
OnPermanentError(int exit_code)133 void DaemonProcess::OnPermanentError(int exit_code) {
134 DCHECK(caller_task_runner()->BelongsToCurrentThread());
135 Stop();
136 }
137
CloseDesktopSession(int terminal_id)138 void 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
DaemonProcess(scoped_refptr<AutoThreadTaskRunner> caller_task_runner,scoped_refptr<AutoThreadTaskRunner> io_task_runner,const base::Closure & stopped_callback)171 DaemonProcess::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
CreateDesktopSession(int terminal_id,const ScreenResolution & resolution,bool virtual_terminal)183 void 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
SetScreenResolution(int terminal_id,const ScreenResolution & resolution)214 void 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
CrashNetworkProcess(const tracked_objects::Location & location)250 void DaemonProcess::CrashNetworkProcess(
251 const tracked_objects::Location& location) {
252 DCHECK(caller_task_runner()->BelongsToCurrentThread());
253
254 DoCrashNetworkProcess(location);
255 DeleteAllDesktopSessions();
256 }
257
Initialize()258 void 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
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