Migrate away from the PrefMetricsService-based device ID in PrefHashCalculator.
[chromium-blink-merge.git] / remoting / protocol / jingle_session.cc
blob57a78fdb3ab466eb7b2c83011badec307c1111be
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.
5 #include "remoting/protocol/jingle_session.h"
7 #include "base/bind.h"
8 #include "base/rand_util.h"
9 #include "base/single_thread_task_runner.h"
10 #include "base/stl_util.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/thread_task_runner_handle.h"
13 #include "base/time/time.h"
14 #include "remoting/base/constants.h"
15 #include "remoting/protocol/authenticator.h"
16 #include "remoting/protocol/channel_authenticator.h"
17 #include "remoting/protocol/channel_multiplexer.h"
18 #include "remoting/protocol/content_description.h"
19 #include "remoting/protocol/jingle_messages.h"
20 #include "remoting/protocol/jingle_session_manager.h"
21 #include "remoting/protocol/session_config.h"
22 #include "remoting/signaling/iq_sender.h"
23 #include "third_party/libjingle/source/talk/p2p/base/candidate.h"
24 #include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
26 using buzz::XmlElement;
28 namespace remoting {
29 namespace protocol {
31 namespace {
32 // Delay after candidate creation before sending transport-info
33 // message. This is neccessary to be able to pack multiple candidates
34 // into one transport-info messages. The value needs to be greater
35 // than zero because ports are opened asynchronously in the browser
36 // process.
37 const int kTransportInfoSendDelayMs = 2;
39 // How long we should wait for a response from the other end. This value is used
40 // for all requests except |transport-info|.
41 const int kDefaultMessageTimeout = 10;
43 // During a reconnection, it usually takes longer for the peer to respond due to
44 // pending messages in the channel from the previous session. From experiment,
45 // it can take up to 20s for the session to reconnect. To make it safe, setting
46 // the timeout to 30s.
47 const int kSessionInitiateAndAcceptTimeout = kDefaultMessageTimeout * 3;
49 // Timeout for the transport-info messages.
50 const int kTransportInfoTimeout = 10 * 60;
52 // Name of the multiplexed channel.
53 const char kMuxChannelName[] = "mux";
55 ErrorCode AuthRejectionReasonToErrorCode(
56 Authenticator::RejectionReason reason) {
57 switch (reason) {
58 case Authenticator::INVALID_CREDENTIALS:
59 return AUTHENTICATION_FAILED;
60 case Authenticator::PROTOCOL_ERROR:
61 return INCOMPATIBLE_PROTOCOL;
63 NOTREACHED();
64 return UNKNOWN_ERROR;
67 } // namespace
69 JingleSession::JingleSession(JingleSessionManager* session_manager)
70 : session_manager_(session_manager),
71 event_handler_(NULL),
72 state_(INITIALIZING),
73 error_(OK),
74 config_is_set_(false),
75 weak_factory_(this) {
78 JingleSession::~JingleSession() {
79 channel_multiplexer_.reset();
80 STLDeleteContainerPointers(pending_requests_.begin(),
81 pending_requests_.end());
82 STLDeleteContainerPointers(transport_info_requests_.begin(),
83 transport_info_requests_.end());
84 STLDeleteContainerPairSecondPointers(channels_.begin(), channels_.end());
85 session_manager_->SessionDestroyed(this);
88 void JingleSession::SetEventHandler(Session::EventHandler* event_handler) {
89 DCHECK(CalledOnValidThread());
90 DCHECK(event_handler);
91 event_handler_ = event_handler;
94 ErrorCode JingleSession::error() {
95 DCHECK(CalledOnValidThread());
96 return error_;
99 void JingleSession::StartConnection(
100 const std::string& peer_jid,
101 scoped_ptr<Authenticator> authenticator,
102 scoped_ptr<CandidateSessionConfig> config) {
103 DCHECK(CalledOnValidThread());
104 DCHECK(authenticator.get());
105 DCHECK_EQ(authenticator->state(), Authenticator::MESSAGE_READY);
107 peer_jid_ = peer_jid;
108 authenticator_ = authenticator.Pass();
109 candidate_config_ = config.Pass();
111 // Generate random session ID. There are usually not more than 1
112 // concurrent session per host, so a random 64-bit integer provides
113 // enough entropy. In the worst case connection will fail when two
114 // clients generate the same session ID concurrently.
115 session_id_ = base::Int64ToString(base::RandGenerator(kint64max));
117 // Send session-initiate message.
118 JingleMessage message(peer_jid_, JingleMessage::SESSION_INITIATE,
119 session_id_);
120 message.initiator = session_manager_->signal_strategy_->GetLocalJid();
121 message.description.reset(
122 new ContentDescription(candidate_config_->Clone(),
123 authenticator_->GetNextMessage()));
124 SendMessage(message);
126 SetState(CONNECTING);
129 void JingleSession::InitializeIncomingConnection(
130 const JingleMessage& initiate_message,
131 scoped_ptr<Authenticator> authenticator) {
132 DCHECK(CalledOnValidThread());
133 DCHECK(initiate_message.description.get());
134 DCHECK(authenticator.get());
135 DCHECK_EQ(authenticator->state(), Authenticator::WAITING_MESSAGE);
137 peer_jid_ = initiate_message.from;
138 authenticator_ = authenticator.Pass();
139 session_id_ = initiate_message.sid;
140 candidate_config_ = initiate_message.description->config()->Clone();
142 SetState(ACCEPTING);
145 void JingleSession::AcceptIncomingConnection(
146 const JingleMessage& initiate_message) {
147 DCHECK(config_is_set_);
149 // Process the first authentication message.
150 const buzz::XmlElement* first_auth_message =
151 initiate_message.description->authenticator_message();
153 if (!first_auth_message) {
154 CloseInternal(INCOMPATIBLE_PROTOCOL);
155 return;
158 DCHECK_EQ(authenticator_->state(), Authenticator::WAITING_MESSAGE);
159 // |authenticator_| is owned, so Unretained() is safe here.
160 authenticator_->ProcessMessage(first_auth_message, base::Bind(
161 &JingleSession::ContinueAcceptIncomingConnection,
162 base::Unretained(this)));
165 void JingleSession::ContinueAcceptIncomingConnection() {
166 DCHECK_NE(authenticator_->state(), Authenticator::PROCESSING_MESSAGE);
167 if (authenticator_->state() == Authenticator::REJECTED) {
168 CloseInternal(AuthRejectionReasonToErrorCode(
169 authenticator_->rejection_reason()));
170 return;
173 // Send the session-accept message.
174 JingleMessage message(peer_jid_, JingleMessage::SESSION_ACCEPT,
175 session_id_);
177 scoped_ptr<buzz::XmlElement> auth_message;
178 if (authenticator_->state() == Authenticator::MESSAGE_READY)
179 auth_message = authenticator_->GetNextMessage();
181 message.description.reset(
182 new ContentDescription(CandidateSessionConfig::CreateFrom(config_),
183 auth_message.Pass()));
184 SendMessage(message);
186 // Update state.
187 SetState(CONNECTED);
189 if (authenticator_->state() == Authenticator::ACCEPTED) {
190 SetState(AUTHENTICATED);
191 } else {
192 DCHECK_EQ(authenticator_->state(), Authenticator::WAITING_MESSAGE);
193 if (authenticator_->started()) {
194 SetState(AUTHENTICATING);
199 const std::string& JingleSession::jid() {
200 DCHECK(CalledOnValidThread());
201 return peer_jid_;
204 const CandidateSessionConfig* JingleSession::candidate_config() {
205 DCHECK(CalledOnValidThread());
206 return candidate_config_.get();
209 const SessionConfig& JingleSession::config() {
210 DCHECK(CalledOnValidThread());
211 return config_;
214 void JingleSession::set_config(const SessionConfig& config) {
215 DCHECK(CalledOnValidThread());
216 DCHECK(!config_is_set_);
217 config_ = config;
218 config_is_set_ = true;
221 ChannelFactory* JingleSession::GetTransportChannelFactory() {
222 DCHECK(CalledOnValidThread());
223 return this;
226 ChannelFactory* JingleSession::GetMultiplexedChannelFactory() {
227 DCHECK(CalledOnValidThread());
228 if (!channel_multiplexer_.get())
229 channel_multiplexer_.reset(new ChannelMultiplexer(this, kMuxChannelName));
230 return channel_multiplexer_.get();
233 void JingleSession::Close() {
234 DCHECK(CalledOnValidThread());
236 CloseInternal(OK);
239 void JingleSession::AddPendingRemoteCandidates(Transport* channel,
240 const std::string& name) {
241 std::list<JingleMessage::NamedCandidate>::iterator it =
242 pending_remote_candidates_.begin();
243 while(it != pending_remote_candidates_.end()) {
244 if (it->name == name) {
245 channel->AddRemoteCandidate(it->candidate);
246 it = pending_remote_candidates_.erase(it);
247 } else {
248 ++it;
253 void JingleSession::CreateStreamChannel(
254 const std::string& name,
255 const StreamChannelCallback& callback) {
256 DCHECK(!channels_[name]);
258 scoped_ptr<ChannelAuthenticator> channel_authenticator =
259 authenticator_->CreateChannelAuthenticator();
260 scoped_ptr<StreamTransport> channel =
261 session_manager_->transport_factory_->CreateStreamTransport();
262 channel->Initialize(name, this, channel_authenticator.Pass());
263 channel->Connect(callback);
264 AddPendingRemoteCandidates(channel.get(), name);
265 channels_[name] = channel.release();
268 void JingleSession::CreateDatagramChannel(
269 const std::string& name,
270 const DatagramChannelCallback& callback) {
271 DCHECK(!channels_[name]);
273 scoped_ptr<ChannelAuthenticator> channel_authenticator =
274 authenticator_->CreateChannelAuthenticator();
275 scoped_ptr<DatagramTransport> channel =
276 session_manager_->transport_factory_->CreateDatagramTransport();
277 channel->Initialize(name, this, channel_authenticator.Pass());
278 channel->Connect(callback);
279 AddPendingRemoteCandidates(channel.get(), name);
280 channels_[name] = channel.release();
283 void JingleSession::CancelChannelCreation(const std::string& name) {
284 ChannelsMap::iterator it = channels_.find(name);
285 if (it != channels_.end() && !it->second->is_connected()) {
286 delete it->second;
287 DCHECK(!channels_[name]);
291 void JingleSession::OnTransportCandidate(Transport* transport,
292 const cricket::Candidate& candidate) {
293 pending_candidates_.push_back(JingleMessage::NamedCandidate(
294 transport->name(), candidate));
296 if (!transport_infos_timer_.IsRunning()) {
297 // Delay sending the new candidates in case we get more candidates
298 // that we can send in one message.
299 transport_infos_timer_.Start(
300 FROM_HERE, base::TimeDelta::FromMilliseconds(kTransportInfoSendDelayMs),
301 this, &JingleSession::SendTransportInfo);
305 void JingleSession::OnTransportRouteChange(Transport* transport,
306 const TransportRoute& route) {
307 if (event_handler_)
308 event_handler_->OnSessionRouteChange(transport->name(), route);
311 void JingleSession::OnTransportFailed(Transport* transport) {
312 CloseInternal(CHANNEL_CONNECTION_ERROR);
315 void JingleSession::OnTransportDeleted(Transport* transport) {
316 ChannelsMap::iterator it = channels_.find(transport->name());
317 DCHECK_EQ(it->second, transport);
318 channels_.erase(it);
321 void JingleSession::SendMessage(const JingleMessage& message) {
322 scoped_ptr<IqRequest> request = session_manager_->iq_sender()->SendIq(
323 message.ToXml(),
324 base::Bind(&JingleSession::OnMessageResponse,
325 base::Unretained(this),
326 message.action));
328 int timeout = kDefaultMessageTimeout;
329 if (message.action == JingleMessage::SESSION_INITIATE ||
330 message.action == JingleMessage::SESSION_ACCEPT) {
331 timeout = kSessionInitiateAndAcceptTimeout;
333 if (request) {
334 request->SetTimeout(base::TimeDelta::FromSeconds(timeout));
335 pending_requests_.insert(request.release());
336 } else {
337 LOG(ERROR) << "Failed to send a "
338 << JingleMessage::GetActionName(message.action) << " message";
342 void JingleSession::OnMessageResponse(
343 JingleMessage::ActionType request_type,
344 IqRequest* request,
345 const buzz::XmlElement* response) {
346 std::string type_str = JingleMessage::GetActionName(request_type);
348 // Delete the request from the list of pending requests.
349 pending_requests_.erase(request);
350 delete request;
352 // |response| will be NULL if the request timed out.
353 if (!response) {
354 LOG(ERROR) << type_str << " request timed out.";
355 CloseInternal(SIGNALING_TIMEOUT);
356 return;
357 } else {
358 const std::string& type =
359 response->Attr(buzz::QName(std::string(), "type"));
360 if (type != "result") {
361 LOG(ERROR) << "Received error in response to " << type_str
362 << " message: \"" << response->Str()
363 << "\". Terminating the session.";
365 switch (request_type) {
366 case JingleMessage::SESSION_INFO:
367 // session-info is used for the new authentication protocol,
368 // and wasn't previously supported.
369 CloseInternal(INCOMPATIBLE_PROTOCOL);
370 break;
372 default:
373 // TODO(sergeyu): There may be different reasons for error
374 // here. Parse the response stanza to find failure reason.
375 CloseInternal(PEER_IS_OFFLINE);
381 void JingleSession::SendTransportInfo() {
382 JingleMessage message(peer_jid_, JingleMessage::TRANSPORT_INFO, session_id_);
383 message.candidates.swap(pending_candidates_);
385 scoped_ptr<IqRequest> request = session_manager_->iq_sender()->SendIq(
386 message.ToXml(),
387 base::Bind(&JingleSession::OnTransportInfoResponse,
388 base::Unretained(this)));
389 if (request) {
390 request->SetTimeout(base::TimeDelta::FromSeconds(kTransportInfoTimeout));
391 transport_info_requests_.push_back(request.release());
392 } else {
393 LOG(ERROR) << "Failed to send a transport-info message";
397 void JingleSession::OnTransportInfoResponse(IqRequest* request,
398 const buzz::XmlElement* response) {
399 DCHECK(!transport_info_requests_.empty());
401 // Consider transport-info requests sent before this one lost and delete
402 // corresponding IqRequest objects.
403 while (transport_info_requests_.front() != request) {
404 delete transport_info_requests_.front();
405 transport_info_requests_.pop_front();
408 // Delete the |request| itself.
409 DCHECK_EQ(request, transport_info_requests_.front());
410 delete request;
411 transport_info_requests_.pop_front();
413 // Ignore transport-info timeouts.
414 if (!response) {
415 LOG(ERROR) << "transport-info request has timed out.";
416 return;
419 const std::string& type = response->Attr(buzz::QName(std::string(), "type"));
420 if (type != "result") {
421 LOG(ERROR) << "Received error in response to transport-info message: \""
422 << response->Str() << "\". Terminating the session.";
423 CloseInternal(PEER_IS_OFFLINE);
427 void JingleSession::OnIncomingMessage(const JingleMessage& message,
428 const ReplyCallback& reply_callback) {
429 DCHECK(CalledOnValidThread());
431 if (message.from != peer_jid_) {
432 // Ignore messages received from a different Jid.
433 reply_callback.Run(JingleMessageReply::INVALID_SID);
434 return;
437 switch (message.action) {
438 case JingleMessage::SESSION_ACCEPT:
439 OnAccept(message, reply_callback);
440 break;
442 case JingleMessage::SESSION_INFO:
443 OnSessionInfo(message, reply_callback);
444 break;
446 case JingleMessage::TRANSPORT_INFO:
447 reply_callback.Run(JingleMessageReply::NONE);
448 ProcessTransportInfo(message);
449 break;
451 case JingleMessage::SESSION_TERMINATE:
452 OnTerminate(message, reply_callback);
453 break;
455 default:
456 reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
460 void JingleSession::OnAccept(const JingleMessage& message,
461 const ReplyCallback& reply_callback) {
462 if (state_ != CONNECTING) {
463 reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
464 return;
467 reply_callback.Run(JingleMessageReply::NONE);
469 const buzz::XmlElement* auth_message =
470 message.description->authenticator_message();
471 if (!auth_message) {
472 DLOG(WARNING) << "Received session-accept without authentication message ";
473 CloseInternal(INCOMPATIBLE_PROTOCOL);
474 return;
477 if (!InitializeConfigFromDescription(message.description.get())) {
478 CloseInternal(INCOMPATIBLE_PROTOCOL);
479 return;
482 // In case there is transport information in the accept message.
483 ProcessTransportInfo(message);
485 SetState(CONNECTED);
487 DCHECK(authenticator_->state() == Authenticator::WAITING_MESSAGE);
488 authenticator_->ProcessMessage(auth_message, base::Bind(
489 &JingleSession::ProcessAuthenticationStep,base::Unretained(this)));
492 void JingleSession::OnSessionInfo(const JingleMessage& message,
493 const ReplyCallback& reply_callback) {
494 if (!message.info.get() ||
495 !Authenticator::IsAuthenticatorMessage(message.info.get())) {
496 reply_callback.Run(JingleMessageReply::UNSUPPORTED_INFO);
497 return;
500 if ((state_ != CONNECTED && state_ != AUTHENTICATING) ||
501 authenticator_->state() != Authenticator::WAITING_MESSAGE) {
502 LOG(WARNING) << "Received unexpected authenticator message "
503 << message.info->Str();
504 reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
505 CloseInternal(INCOMPATIBLE_PROTOCOL);
506 return;
509 reply_callback.Run(JingleMessageReply::NONE);
511 authenticator_->ProcessMessage(message.info.get(), base::Bind(
512 &JingleSession::ProcessAuthenticationStep, base::Unretained(this)));
515 void JingleSession::ProcessTransportInfo(const JingleMessage& message) {
516 for (std::list<JingleMessage::NamedCandidate>::const_iterator it =
517 message.candidates.begin();
518 it != message.candidates.end(); ++it) {
519 ChannelsMap::iterator channel = channels_.find(it->name);
520 if (channel != channels_.end()) {
521 channel->second->AddRemoteCandidate(it->candidate);
522 } else {
523 // Transport info was received before the channel was created.
524 // This could happen due to messages being reordered on the wire.
525 pending_remote_candidates_.push_back(*it);
530 void JingleSession::OnTerminate(const JingleMessage& message,
531 const ReplyCallback& reply_callback) {
532 if (!is_session_active()) {
533 LOG(WARNING) << "Received unexpected session-terminate message.";
534 reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
535 return;
538 reply_callback.Run(JingleMessageReply::NONE);
540 switch (message.reason) {
541 case JingleMessage::SUCCESS:
542 if (state_ == CONNECTING) {
543 error_ = SESSION_REJECTED;
544 } else {
545 error_ = OK;
547 break;
548 case JingleMessage::DECLINE:
549 error_ = AUTHENTICATION_FAILED;
550 break;
551 case JingleMessage::CANCEL:
552 error_ = HOST_OVERLOAD;
553 break;
554 case JingleMessage::GENERAL_ERROR:
555 error_ = CHANNEL_CONNECTION_ERROR;
556 break;
557 case JingleMessage::INCOMPATIBLE_PARAMETERS:
558 error_ = INCOMPATIBLE_PROTOCOL;
559 break;
560 default:
561 error_ = UNKNOWN_ERROR;
564 if (error_ != OK) {
565 SetState(FAILED);
566 } else {
567 SetState(CLOSED);
571 bool JingleSession::InitializeConfigFromDescription(
572 const ContentDescription* description) {
573 DCHECK(description);
575 if (!description->config()->GetFinalConfig(&config_)) {
576 LOG(ERROR) << "session-accept does not specify configuration";
577 return false;
579 if (!candidate_config()->IsSupported(config_)) {
580 LOG(ERROR) << "session-accept specifies an invalid configuration";
581 return false;
584 return true;
587 void JingleSession::ProcessAuthenticationStep() {
588 DCHECK(CalledOnValidThread());
589 DCHECK_NE(authenticator_->state(), Authenticator::PROCESSING_MESSAGE);
591 if (state_ != CONNECTED && state_ != AUTHENTICATING) {
592 DCHECK(state_ == FAILED || state_ == CLOSED);
593 // The remote host closed the connection while the authentication was being
594 // processed asynchronously, nothing to do.
595 return;
598 if (authenticator_->state() == Authenticator::MESSAGE_READY) {
599 JingleMessage message(peer_jid_, JingleMessage::SESSION_INFO, session_id_);
600 message.info = authenticator_->GetNextMessage();
601 DCHECK(message.info.get());
602 SendMessage(message);
604 DCHECK_NE(authenticator_->state(), Authenticator::MESSAGE_READY);
606 // The current JingleSession object can be destroyed by event_handler of
607 // SetState(AUTHENTICATING) and cause subsequent dereferencing of the this
608 // pointer to crash. To protect against it, we run ContinueAuthenticationStep
609 // asychronously using a weak pointer.
610 base::ThreadTaskRunnerHandle::Get()->PostTask(
611 FROM_HERE,
612 base::Bind(&JingleSession::ContinueAuthenticationStep,
613 weak_factory_.GetWeakPtr()));
615 if (authenticator_->started()) {
616 SetState(AUTHENTICATING);
620 void JingleSession::ContinueAuthenticationStep() {
621 if (authenticator_->state() == Authenticator::ACCEPTED) {
622 SetState(AUTHENTICATED);
623 } else if (authenticator_->state() == Authenticator::REJECTED) {
624 CloseInternal(AuthRejectionReasonToErrorCode(
625 authenticator_->rejection_reason()));
629 void JingleSession::CloseInternal(ErrorCode error) {
630 DCHECK(CalledOnValidThread());
632 if (is_session_active()) {
633 // Send session-terminate message with the appropriate error code.
634 JingleMessage::Reason reason;
635 switch (error) {
636 case OK:
637 reason = JingleMessage::SUCCESS;
638 break;
639 case SESSION_REJECTED:
640 case AUTHENTICATION_FAILED:
641 reason = JingleMessage::DECLINE;
642 break;
643 case INCOMPATIBLE_PROTOCOL:
644 reason = JingleMessage::INCOMPATIBLE_PARAMETERS;
645 break;
646 case HOST_OVERLOAD:
647 reason = JingleMessage::CANCEL;
648 break;
649 default:
650 reason = JingleMessage::GENERAL_ERROR;
653 JingleMessage message(peer_jid_, JingleMessage::SESSION_TERMINATE,
654 session_id_);
655 message.reason = reason;
656 SendMessage(message);
659 error_ = error;
661 if (state_ != FAILED && state_ != CLOSED) {
662 if (error != OK) {
663 SetState(FAILED);
664 } else {
665 SetState(CLOSED);
670 void JingleSession::SetState(State new_state) {
671 DCHECK(CalledOnValidThread());
673 if (new_state != state_) {
674 DCHECK_NE(state_, CLOSED);
675 DCHECK_NE(state_, FAILED);
677 state_ = new_state;
678 if (event_handler_)
679 event_handler_->OnSessionStateChange(new_state);
683 bool JingleSession::is_session_active() {
684 return state_ == CONNECTING || state_ == ACCEPTING || state_ == CONNECTED ||
685 state_ == AUTHENTICATING || state_ == AUTHENTICATED;
688 } // namespace protocol
689 } // namespace remoting