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