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 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 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 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 63 void DaemonProcess::OnConfigWatcherError() { 64 DCHECK(caller_task_runner()->BelongsToCurrentThread()); 65 66 Stop(); 67 } 68 69 void DaemonProcess::AddStatusObserver(HostStatusObserver* observer) { 70 DCHECK(caller_task_runner()->BelongsToCurrentThread()); 71 72 status_observers_.AddObserver(observer); 73 } 74 75 void DaemonProcess::RemoveStatusObserver(HostStatusObserver* observer) { 76 DCHECK(caller_task_runner()->BelongsToCurrentThread()); 77 78 status_observers_.RemoveObserver(observer); 79 } 80 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 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 133 void DaemonProcess::OnPermanentError(int exit_code) { 134 DCHECK(caller_task_runner()->BelongsToCurrentThread()); 135 Stop(); 136 } 137 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 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 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 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 250 void DaemonProcess::CrashNetworkProcess( 251 const tracked_objects::Location& location) { 252 DCHECK(caller_task_runner()->BelongsToCurrentThread()); 253 254 DoCrashNetworkProcess(location); 255 DeleteAllDesktopSessions(); 256 } 257 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 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