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
28using remoting::protocol::ConnectionToClient;
29using remoting::protocol::InputStub;
30
31namespace remoting {
32
33namespace {
34
35const 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
63ChromotingHost::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
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_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
129void ChromotingHost::AddStatusObserver(HostStatusObserver* observer) {
130  DCHECK(CalledOnValidThread());
131  status_observers_.AddObserver(observer);
132}
133
134void ChromotingHost::RemoveStatusObserver(HostStatusObserver* observer) {
135  DCHECK(CalledOnValidThread());
136  status_observers_.RemoveObserver(observer);
137}
138
139void ChromotingHost::AddExtension(scoped_ptr<HostExtension> extension) {
140  extensions_.push_back(extension.release());
141}
142
143void ChromotingHost::RejectAuthenticatingClient() {
144  DCHECK(authenticating_client_);
145  reject_authenticating_client_ = true;
146}
147
148void ChromotingHost::SetAuthenticatorFactory(
149    scoped_ptr<protocol::AuthenticatorFactory> authenticator_factory) {
150  DCHECK(CalledOnValidThread());
151  session_manager_->set_authenticator_factory(authenticator_factory.Pass());
152}
153
154void 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
172void ChromotingHost::SetMaximumSessionDuration(
173    const base::TimeDelta& max_session_duration) {
174  max_session_duration_ = max_session_duration;
175}
176
177////////////////////////////////////////////////////////////////////////////
178// protocol::ClientSession::EventHandler implementation.
179void 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
193bool 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
224void 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
232void 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
240void 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
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      extensions_.get());
318
319  clients_.push_back(client);
320}
321
322void 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
330void 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