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