chromoting_host.cc revision c5cede9ae108bb15f6b7a8aea21c7e1fefa2834c
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/chromoting_host.h" 6 7#include <algorithm> 8 9#include "base/bind.h" 10#include "base/callback.h" 11#include "base/message_loop/message_loop_proxy.h" 12#include "build/build_config.h" 13#include "remoting/base/constants.h" 14#include "remoting/base/logging.h" 15#include "remoting/host/chromoting_host_context.h" 16#include "remoting/host/desktop_environment.h" 17#include "remoting/host/host_config.h" 18#include "remoting/host/input_injector.h" 19#include "remoting/protocol/connection_to_client.h" 20#include "remoting/protocol/client_stub.h" 21#include "remoting/protocol/host_stub.h" 22#include "remoting/protocol/input_stub.h" 23#include "remoting/protocol/session_config.h" 24 25using remoting::protocol::ConnectionToClient; 26using remoting::protocol::InputStub; 27 28namespace remoting { 29 30namespace { 31 32const net::BackoffEntry::Policy kDefaultBackoffPolicy = { 33 // Number of initial errors (in sequence) to ignore before applying 34 // exponential back-off rules. 35 0, 36 37 // Initial delay for exponential back-off in ms. 38 2000, 39 40 // Factor by which the waiting time will be multiplied. 41 2, 42 43 // Fuzzing percentage. ex: 10% will spread requests randomly 44 // between 90%-100% of the calculated time. 45 0, 46 47 // Maximum amount of time we are willing to delay our request in ms. 48 -1, 49 50 // Time to keep an entry from being discarded even when it 51 // has no significant state, -1 to never discard. 52 -1, 53 54 // Don't use initial delay unless the last request was an error. 55 false, 56}; 57 58} // namespace 59 60ChromotingHost::ChromotingHost( 61 SignalStrategy* signal_strategy, 62 DesktopEnvironmentFactory* desktop_environment_factory, 63 scoped_ptr<protocol::SessionManager> session_manager, 64 scoped_refptr<base::SingleThreadTaskRunner> audio_task_runner, 65 scoped_refptr<base::SingleThreadTaskRunner> input_task_runner, 66 scoped_refptr<base::SingleThreadTaskRunner> video_capture_task_runner, 67 scoped_refptr<base::SingleThreadTaskRunner> video_encode_task_runner, 68 scoped_refptr<base::SingleThreadTaskRunner> network_task_runner, 69 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) 70 : desktop_environment_factory_(desktop_environment_factory), 71 session_manager_(session_manager.Pass()), 72 audio_task_runner_(audio_task_runner), 73 input_task_runner_(input_task_runner), 74 video_capture_task_runner_(video_capture_task_runner), 75 video_encode_task_runner_(video_encode_task_runner), 76 network_task_runner_(network_task_runner), 77 ui_task_runner_(ui_task_runner), 78 signal_strategy_(signal_strategy), 79 started_(false), 80 protocol_config_(protocol::CandidateSessionConfig::CreateDefault()), 81 login_backoff_(&kDefaultBackoffPolicy), 82 authenticating_client_(false), 83 reject_authenticating_client_(false), 84 enable_curtaining_(false), 85 weak_factory_(this) { 86 DCHECK(network_task_runner_->BelongsToCurrentThread()); 87 DCHECK(signal_strategy); 88 89 // VP9 encode is not yet supported. 90 protocol::CandidateSessionConfig::DisableVideoCodec( 91 protocol_config_.get(), protocol::ChannelConfig::CODEC_VP9); 92 93 if (!desktop_environment_factory_->SupportsAudioCapture()) { 94 protocol::CandidateSessionConfig::DisableAudioChannel( 95 protocol_config_.get()); 96 } 97} 98 99ChromotingHost::~ChromotingHost() { 100 DCHECK(CalledOnValidThread()); 101 102 // Disconnect all of the clients. 103 while (!clients_.empty()) { 104 clients_.front()->DisconnectSession(); 105 } 106 107 // Destroy the session manager to make sure that |signal_strategy_| does not 108 // have any listeners registered. 109 session_manager_.reset(); 110 111 // Notify observers. 112 if (started_) 113 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, OnShutdown()); 114} 115 116void ChromotingHost::Start(const std::string& host_owner) { 117 DCHECK(CalledOnValidThread()); 118 DCHECK(!started_); 119 120 HOST_LOG << "Starting host"; 121 started_ = true; 122 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, OnStart(host_owner)); 123 124 // Start the SessionManager, supplying this ChromotingHost as the listener. 125 session_manager_->Init(signal_strategy_, this); 126} 127 128void ChromotingHost::AddStatusObserver(HostStatusObserver* observer) { 129 DCHECK(CalledOnValidThread()); 130 status_observers_.AddObserver(observer); 131} 132 133void ChromotingHost::RemoveStatusObserver(HostStatusObserver* observer) { 134 DCHECK(CalledOnValidThread()); 135 status_observers_.RemoveObserver(observer); 136} 137 138void ChromotingHost::RejectAuthenticatingClient() { 139 DCHECK(authenticating_client_); 140 reject_authenticating_client_ = true; 141} 142 143void ChromotingHost::SetAuthenticatorFactory( 144 scoped_ptr<protocol::AuthenticatorFactory> authenticator_factory) { 145 DCHECK(CalledOnValidThread()); 146 session_manager_->set_authenticator_factory(authenticator_factory.Pass()); 147} 148 149void ChromotingHost::SetEnableCurtaining(bool enable) { 150 DCHECK(network_task_runner_->BelongsToCurrentThread()); 151 152 if (enable_curtaining_ == enable) 153 return; 154 155 enable_curtaining_ = enable; 156 desktop_environment_factory_->SetEnableCurtaining(enable_curtaining_); 157 158 // Disconnect all existing clients because they might be running not 159 // curtained. 160 // TODO(alexeypa): fix this such that the curtain is applied to the not 161 // curtained sessions or disconnect only the client connected to not 162 // curtained sessions. 163 if (enable_curtaining_) 164 DisconnectAllClients(); 165} 166 167void ChromotingHost::SetMaximumSessionDuration( 168 const base::TimeDelta& max_session_duration) { 169 max_session_duration_ = max_session_duration; 170} 171 172//////////////////////////////////////////////////////////////////////////// 173// protocol::ClientSession::EventHandler implementation. 174void ChromotingHost::OnSessionAuthenticating(ClientSession* client) { 175 // We treat each incoming connection as a failure to authenticate, 176 // and clear the backoff when a connection successfully 177 // authenticates. This allows the backoff to protect from parallel 178 // connection attempts as well as sequential ones. 179 if (login_backoff_.ShouldRejectRequest()) { 180 LOG(WARNING) << "Disconnecting client " << client->client_jid() << " due to" 181 " an overload of failed login attempts."; 182 client->DisconnectSession(); 183 return; 184 } 185 login_backoff_.InformOfRequest(false); 186} 187 188bool ChromotingHost::OnSessionAuthenticated(ClientSession* client) { 189 DCHECK(CalledOnValidThread()); 190 191 login_backoff_.Reset(); 192 193 // Disconnect all other clients. |it| should be advanced before Disconnect() 194 // is called to avoid it becoming invalid when the client is removed from 195 // the list. 196 ClientList::iterator it = clients_.begin(); 197 while (it != clients_.end()) { 198 ClientSession* other_client = *it++; 199 if (other_client != client) 200 other_client->DisconnectSession(); 201 } 202 203 // Disconnects above must have destroyed all other clients. 204 DCHECK_EQ(clients_.size(), 1U); 205 206 // Notify observers that there is at least one authenticated client. 207 const std::string& jid = client->client_jid(); 208 209 reject_authenticating_client_ = false; 210 211 authenticating_client_ = true; 212 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, 213 OnClientAuthenticated(jid)); 214 authenticating_client_ = false; 215 216 return !reject_authenticating_client_; 217} 218 219void ChromotingHost::OnSessionChannelsConnected(ClientSession* client) { 220 DCHECK(CalledOnValidThread()); 221 222 // Notify observers. 223 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, 224 OnClientConnected(client->client_jid())); 225} 226 227void ChromotingHost::OnSessionAuthenticationFailed(ClientSession* client) { 228 DCHECK(CalledOnValidThread()); 229 230 // Notify observers. 231 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, 232 OnAccessDenied(client->client_jid())); 233} 234 235void ChromotingHost::OnSessionClosed(ClientSession* client) { 236 DCHECK(CalledOnValidThread()); 237 238 ClientList::iterator it = std::find(clients_.begin(), clients_.end(), client); 239 CHECK(it != clients_.end()); 240 241 if (client->is_authenticated()) { 242 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, 243 OnClientDisconnected(client->client_jid())); 244 } 245 246 clients_.erase(it); 247 delete client; 248} 249 250void ChromotingHost::OnSessionSequenceNumber(ClientSession* session, 251 int64 sequence_number) { 252 DCHECK(CalledOnValidThread()); 253} 254 255void ChromotingHost::OnSessionRouteChange( 256 ClientSession* session, 257 const std::string& channel_name, 258 const protocol::TransportRoute& route) { 259 DCHECK(CalledOnValidThread()); 260 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, 261 OnClientRouteChange(session->client_jid(), channel_name, 262 route)); 263} 264 265void ChromotingHost::OnSessionManagerReady() { 266 DCHECK(CalledOnValidThread()); 267 // Don't need to do anything here, just wait for incoming 268 // connections. 269} 270 271void ChromotingHost::OnIncomingSession( 272 protocol::Session* session, 273 protocol::SessionManager::IncomingSessionResponse* response) { 274 DCHECK(CalledOnValidThread()); 275 276 if (!started_) { 277 *response = protocol::SessionManager::DECLINE; 278 return; 279 } 280 281 if (login_backoff_.ShouldRejectRequest()) { 282 LOG(WARNING) << "Rejecting connection due to" 283 " an overload of failed login attempts."; 284 *response = protocol::SessionManager::OVERLOAD; 285 return; 286 } 287 288 protocol::SessionConfig config; 289 if (!protocol_config_->Select(session->candidate_config(), &config)) { 290 LOG(WARNING) << "Rejecting connection from " << session->jid() 291 << " because no compatible configuration has been found."; 292 *response = protocol::SessionManager::INCOMPATIBLE; 293 return; 294 } 295 296 session->set_config(config); 297 298 *response = protocol::SessionManager::ACCEPT; 299 300 HOST_LOG << "Client connected: " << session->jid(); 301 302 // Create a client object. 303 scoped_ptr<protocol::ConnectionToClient> connection( 304 new protocol::ConnectionToClient(session)); 305 ClientSession* client = new ClientSession( 306 this, 307 audio_task_runner_, 308 input_task_runner_, 309 video_capture_task_runner_, 310 video_encode_task_runner_, 311 network_task_runner_, 312 ui_task_runner_, 313 connection.Pass(), 314 desktop_environment_factory_, 315 max_session_duration_, 316 pairing_registry_); 317 clients_.push_back(client); 318} 319 320void ChromotingHost::set_protocol_config( 321 scoped_ptr<protocol::CandidateSessionConfig> config) { 322 DCHECK(CalledOnValidThread()); 323 DCHECK(config.get()); 324 DCHECK(!started_); 325 protocol_config_ = config.Pass(); 326} 327 328void ChromotingHost::DisconnectAllClients() { 329 DCHECK(CalledOnValidThread()); 330 331 while (!clients_.empty()) { 332 size_t size = clients_.size(); 333 clients_.front()->DisconnectSession(); 334 CHECK_EQ(clients_.size(), size - 1); 335 } 336} 337 338} // namespace remoting 339