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 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 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 290 bool DaemonProcess::WasTerminalIdAllocated(int terminal_id) { 291 return terminal_id < next_terminal_id_; 292 } 293 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 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 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 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 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 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 366 void DaemonProcess::OnHostShutdown() { 367 DCHECK(caller_task_runner()->BelongsToCurrentThread()); 368 369 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, OnShutdown()); 370 } 371 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