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