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
25 using remoting::protocol::ConnectionToClient;
26 using remoting::protocol::InputStub;
27
28 namespace remoting {
29
30 namespace {
31
32 const 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
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)60 ChromotingHost::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
~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)116 void 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
AddStatusObserver(HostStatusObserver * observer)128 void ChromotingHost::AddStatusObserver(HostStatusObserver* observer) {
129 DCHECK(CalledOnValidThread());
130 status_observers_.AddObserver(observer);
131 }
132
RemoveStatusObserver(HostStatusObserver * observer)133 void ChromotingHost::RemoveStatusObserver(HostStatusObserver* observer) {
134 DCHECK(CalledOnValidThread());
135 status_observers_.RemoveObserver(observer);
136 }
137
RejectAuthenticatingClient()138 void ChromotingHost::RejectAuthenticatingClient() {
139 DCHECK(authenticating_client_);
140 reject_authenticating_client_ = true;
141 }
142
SetAuthenticatorFactory(scoped_ptr<protocol::AuthenticatorFactory> authenticator_factory)143 void ChromotingHost::SetAuthenticatorFactory(
144 scoped_ptr<protocol::AuthenticatorFactory> authenticator_factory) {
145 DCHECK(CalledOnValidThread());
146 session_manager_->set_authenticator_factory(authenticator_factory.Pass());
147 }
148
SetEnableCurtaining(bool enable)149 void 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
SetMaximumSessionDuration(const base::TimeDelta & max_session_duration)167 void ChromotingHost::SetMaximumSessionDuration(
168 const base::TimeDelta& max_session_duration) {
169 max_session_duration_ = max_session_duration;
170 }
171
172 ////////////////////////////////////////////////////////////////////////////
173 // protocol::ClientSession::EventHandler implementation.
OnSessionAuthenticated(ClientSession * client)174 bool ChromotingHost::OnSessionAuthenticated(ClientSession* client) {
175 DCHECK(CalledOnValidThread());
176
177 login_backoff_.Reset();
178
179 // Disconnect all other clients. |it| should be advanced before Disconnect()
180 // is called to avoid it becoming invalid when the client is removed from
181 // the list.
182 ClientList::iterator it = clients_.begin();
183 while (it != clients_.end()) {
184 ClientSession* other_client = *it++;
185 if (other_client != client)
186 other_client->DisconnectSession();
187 }
188
189 // Disconnects above must have destroyed all other clients.
190 DCHECK_EQ(clients_.size(), 1U);
191
192 // Notify observers that there is at least one authenticated client.
193 const std::string& jid = client->client_jid();
194
195 reject_authenticating_client_ = false;
196
197 authenticating_client_ = true;
198 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_,
199 OnClientAuthenticated(jid));
200 authenticating_client_ = false;
201
202 return !reject_authenticating_client_;
203 }
204
OnSessionChannelsConnected(ClientSession * client)205 void ChromotingHost::OnSessionChannelsConnected(ClientSession* client) {
206 DCHECK(CalledOnValidThread());
207
208 // Notify observers.
209 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_,
210 OnClientConnected(client->client_jid()));
211 }
212
OnSessionAuthenticationFailed(ClientSession * client)213 void ChromotingHost::OnSessionAuthenticationFailed(ClientSession* client) {
214 DCHECK(CalledOnValidThread());
215
216 // Notify observers.
217 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_,
218 OnAccessDenied(client->client_jid()));
219 }
220
OnSessionClosed(ClientSession * client)221 void ChromotingHost::OnSessionClosed(ClientSession* client) {
222 DCHECK(CalledOnValidThread());
223
224 ClientList::iterator it = std::find(clients_.begin(), clients_.end(), client);
225 CHECK(it != clients_.end());
226
227 if (client->is_authenticated()) {
228 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_,
229 OnClientDisconnected(client->client_jid()));
230 }
231
232 clients_.erase(it);
233 delete client;
234 }
235
OnSessionSequenceNumber(ClientSession * session,int64 sequence_number)236 void ChromotingHost::OnSessionSequenceNumber(ClientSession* session,
237 int64 sequence_number) {
238 DCHECK(CalledOnValidThread());
239 }
240
OnSessionRouteChange(ClientSession * session,const std::string & channel_name,const protocol::TransportRoute & route)241 void ChromotingHost::OnSessionRouteChange(
242 ClientSession* session,
243 const std::string& channel_name,
244 const protocol::TransportRoute& route) {
245 DCHECK(CalledOnValidThread());
246 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_,
247 OnClientRouteChange(session->client_jid(), channel_name,
248 route));
249 }
250
OnSessionManagerReady()251 void ChromotingHost::OnSessionManagerReady() {
252 DCHECK(CalledOnValidThread());
253 // Don't need to do anything here, just wait for incoming
254 // connections.
255 }
256
OnIncomingSession(protocol::Session * session,protocol::SessionManager::IncomingSessionResponse * response)257 void ChromotingHost::OnIncomingSession(
258 protocol::Session* session,
259 protocol::SessionManager::IncomingSessionResponse* response) {
260 DCHECK(CalledOnValidThread());
261
262 if (!started_) {
263 *response = protocol::SessionManager::DECLINE;
264 return;
265 }
266
267 if (login_backoff_.ShouldRejectRequest()) {
268 *response = protocol::SessionManager::OVERLOAD;
269 return;
270 }
271
272 // We treat each incoming connection as a failure to authenticate,
273 // and clear the backoff when a connection successfully
274 // authenticates. This allows the backoff to protect from parallel
275 // connection attempts as well as sequential ones.
276 login_backoff_.InformOfRequest(false);
277
278 protocol::SessionConfig config;
279 if (!protocol_config_->Select(session->candidate_config(), &config)) {
280 LOG(WARNING) << "Rejecting connection from " << session->jid()
281 << " because no compatible configuration has been found.";
282 *response = protocol::SessionManager::INCOMPATIBLE;
283 return;
284 }
285
286 session->set_config(config);
287
288 *response = protocol::SessionManager::ACCEPT;
289
290 HOST_LOG << "Client connected: " << session->jid();
291
292 // Create a client object.
293 scoped_ptr<protocol::ConnectionToClient> connection(
294 new protocol::ConnectionToClient(session));
295 ClientSession* client = new ClientSession(
296 this,
297 audio_task_runner_,
298 input_task_runner_,
299 video_capture_task_runner_,
300 video_encode_task_runner_,
301 network_task_runner_,
302 ui_task_runner_,
303 connection.Pass(),
304 desktop_environment_factory_,
305 max_session_duration_,
306 pairing_registry_);
307 clients_.push_back(client);
308 }
309
set_protocol_config(scoped_ptr<protocol::CandidateSessionConfig> config)310 void ChromotingHost::set_protocol_config(
311 scoped_ptr<protocol::CandidateSessionConfig> config) {
312 DCHECK(CalledOnValidThread());
313 DCHECK(config.get());
314 DCHECK(!started_);
315 protocol_config_ = config.Pass();
316 }
317
DisconnectAllClients()318 void ChromotingHost::DisconnectAllClients() {
319 DCHECK(CalledOnValidThread());
320
321 while (!clients_.empty()) {
322 size_t size = clients_.size();
323 clients_.front()->DisconnectSession();
324 CHECK_EQ(clients_.size(), size - 1);
325 }
326 }
327
328 } // namespace remoting
329