chromoting_host.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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/logging.h"
12#include "base/message_loop/message_loop_proxy.h"
13#include "build/build_config.h"
14#include "remoting/base/constants.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  if (!desktop_environment_factory_->SupportsAudioCapture()) {
90    protocol::CandidateSessionConfig::DisableAudioChannel(
91        protocol_config_.get());
92  }
93}
94
95ChromotingHost::~ChromotingHost() {
96  DCHECK(CalledOnValidThread());
97
98  // Disconnect all of the clients.
99  while (!clients_.empty()) {
100    clients_.front()->DisconnectSession();
101  }
102
103  // Destroy the session manager to make sure that |signal_strategy_| does not
104  // have any listeners registered.
105  session_manager_.reset();
106
107  // Notify observers.
108  if (started_)
109    FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, OnShutdown());
110}
111
112void ChromotingHost::Start(const std::string& xmpp_login) {
113  DCHECK(CalledOnValidThread());
114  DCHECK(!started_);
115
116  LOG(INFO) << "Starting host";
117  started_ = true;
118  FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, OnStart(xmpp_login));
119
120  // Start the SessionManager, supplying this ChromotingHost as the listener.
121  session_manager_->Init(signal_strategy_, this);
122}
123
124void ChromotingHost::AddStatusObserver(HostStatusObserver* observer) {
125  DCHECK(CalledOnValidThread());
126  status_observers_.AddObserver(observer);
127}
128
129void ChromotingHost::RemoveStatusObserver(HostStatusObserver* observer) {
130  DCHECK(CalledOnValidThread());
131  status_observers_.RemoveObserver(observer);
132}
133
134void ChromotingHost::RejectAuthenticatingClient() {
135  DCHECK(authenticating_client_);
136  reject_authenticating_client_ = true;
137}
138
139void ChromotingHost::SetAuthenticatorFactory(
140    scoped_ptr<protocol::AuthenticatorFactory> authenticator_factory) {
141  DCHECK(CalledOnValidThread());
142  session_manager_->set_authenticator_factory(authenticator_factory.Pass());
143}
144
145void ChromotingHost::SetEnableCurtaining(bool enable) {
146  DCHECK(network_task_runner_->BelongsToCurrentThread());
147
148  if (enable_curtaining_ == enable)
149    return;
150
151  enable_curtaining_ = enable;
152  desktop_environment_factory_->SetEnableCurtaining(enable_curtaining_);
153
154  // Disconnect all existing clients because they might be running not
155  // curtained.
156  // TODO(alexeypa): fix this such that the curtain is applied to the not
157  // curtained sessions or disconnect only the client connected to not
158  // curtained sessions.
159  if (enable_curtaining_)
160    DisconnectAllClients();
161}
162
163void ChromotingHost::SetMaximumSessionDuration(
164    const base::TimeDelta& max_session_duration) {
165  max_session_duration_ = max_session_duration;
166}
167
168////////////////////////////////////////////////////////////////////////////
169// protocol::ClientSession::EventHandler implementation.
170bool ChromotingHost::OnSessionAuthenticated(ClientSession* client) {
171  DCHECK(CalledOnValidThread());
172
173  login_backoff_.Reset();
174
175  // Disconnect all other clients. |it| should be advanced before Disconnect()
176  // is called to avoid it becoming invalid when the client is removed from
177  // the list.
178  ClientList::iterator it = clients_.begin();
179  while (it != clients_.end()) {
180    ClientSession* other_client = *it++;
181    if (other_client != client)
182      other_client->DisconnectSession();
183  }
184
185  // Disconnects above must have destroyed all other clients.
186  DCHECK_EQ(clients_.size(), 1U);
187
188  // Notify observers that there is at least one authenticated client.
189  const std::string& jid = client->client_jid();
190
191  reject_authenticating_client_ = false;
192
193  authenticating_client_ = true;
194  FOR_EACH_OBSERVER(HostStatusObserver, status_observers_,
195                    OnClientAuthenticated(jid));
196  authenticating_client_ = false;
197
198  return !reject_authenticating_client_;
199}
200
201void ChromotingHost::OnSessionChannelsConnected(ClientSession* client) {
202  DCHECK(CalledOnValidThread());
203
204  // Notify observers.
205  FOR_EACH_OBSERVER(HostStatusObserver, status_observers_,
206                    OnClientConnected(client->client_jid()));
207}
208
209void ChromotingHost::OnSessionAuthenticationFailed(ClientSession* client) {
210  DCHECK(CalledOnValidThread());
211
212  // Notify observers.
213  FOR_EACH_OBSERVER(HostStatusObserver, status_observers_,
214                    OnAccessDenied(client->client_jid()));
215}
216
217void ChromotingHost::OnSessionClosed(ClientSession* client) {
218  DCHECK(CalledOnValidThread());
219
220  ClientList::iterator it = std::find(clients_.begin(), clients_.end(), client);
221  CHECK(it != clients_.end());
222
223  if (client->is_authenticated()) {
224    FOR_EACH_OBSERVER(HostStatusObserver, status_observers_,
225                      OnClientDisconnected(client->client_jid()));
226  }
227
228  clients_.erase(it);
229  delete client;
230}
231
232void ChromotingHost::OnSessionSequenceNumber(ClientSession* session,
233                                             int64 sequence_number) {
234  DCHECK(CalledOnValidThread());
235}
236
237void ChromotingHost::OnSessionRouteChange(
238    ClientSession* session,
239    const std::string& channel_name,
240    const protocol::TransportRoute& route) {
241  DCHECK(CalledOnValidThread());
242  FOR_EACH_OBSERVER(HostStatusObserver, status_observers_,
243                    OnClientRouteChange(session->client_jid(), channel_name,
244                                        route));
245}
246
247void ChromotingHost::OnSessionManagerReady() {
248  DCHECK(CalledOnValidThread());
249  // Don't need to do anything here, just wait for incoming
250  // connections.
251}
252
253void ChromotingHost::OnIncomingSession(
254      protocol::Session* session,
255      protocol::SessionManager::IncomingSessionResponse* response) {
256  DCHECK(CalledOnValidThread());
257
258  if (!started_) {
259    *response = protocol::SessionManager::DECLINE;
260    return;
261  }
262
263  if (login_backoff_.ShouldRejectRequest()) {
264    *response = protocol::SessionManager::OVERLOAD;
265    return;
266  }
267
268  // We treat each incoming connection as a failure to authenticate,
269  // and clear the backoff when a connection successfully
270  // authenticates. This allows the backoff to protect from parallel
271  // connection attempts as well as sequential ones.
272  login_backoff_.InformOfRequest(false);
273
274  protocol::SessionConfig config;
275  if (!protocol_config_->Select(session->candidate_config(), &config)) {
276    LOG(WARNING) << "Rejecting connection from " << session->jid()
277                 << " because no compatible configuration has been found.";
278    *response = protocol::SessionManager::INCOMPATIBLE;
279    return;
280  }
281
282  session->set_config(config);
283
284  *response = protocol::SessionManager::ACCEPT;
285
286  LOG(INFO) << "Client connected: " << session->jid();
287
288  // Create a client object.
289  scoped_ptr<protocol::ConnectionToClient> connection(
290      new protocol::ConnectionToClient(session));
291  ClientSession* client = new ClientSession(
292      this,
293      audio_task_runner_,
294      input_task_runner_,
295      video_capture_task_runner_,
296      video_encode_task_runner_,
297      network_task_runner_,
298      ui_task_runner_,
299      connection.Pass(),
300      desktop_environment_factory_,
301      max_session_duration_,
302      pairing_registry_);
303  clients_.push_back(client);
304}
305
306void ChromotingHost::set_protocol_config(
307    scoped_ptr<protocol::CandidateSessionConfig> config) {
308  DCHECK(CalledOnValidThread());
309  DCHECK(config.get());
310  DCHECK(!started_);
311  protocol_config_ = config.Pass();
312}
313
314void ChromotingHost::DisconnectAllClients() {
315  DCHECK(CalledOnValidThread());
316
317  while (!clients_.empty()) {
318    size_t size = clients_.size();
319    clients_.front()->DisconnectSession();
320    CHECK_EQ(clients_.size(), size - 1);
321  }
322}
323
324}  // namespace remoting
325