Updating XTBs based on .GRDs from branch master
[chromium-blink-merge.git] / remoting / protocol / jingle_session.cc
blobba2faaad3fb54be4550060d12e4e2106bd22ab8f
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/pseudotcp_channel_factory.h"
22 #include "remoting/protocol/secure_channel_factory.h"
23 #include "remoting/protocol/session_config.h"
24 #include "remoting/protocol/stream_channel_factory.h"
25 #include "remoting/signaling/iq_sender.h"
26 #include "third_party/webrtc/libjingle/xmllite/xmlelement.h"
27 #include "third_party/webrtc/p2p/base/candidate.h"
29 using buzz::XmlElement;
31 namespace remoting {
32 namespace protocol {
34 namespace {
36 // Delay after candidate creation before sending transport-info message to
37 // accumulate multiple candidates. This is an optimization to reduce number of
38 // transport-info messages.
39 const int kTransportInfoSendDelayMs = 20;
41 // How long we should wait for a response from the other end. This value is used
42 // for all requests except |transport-info|.
43 const int kDefaultMessageTimeout = 10;
45 // During a reconnection, it usually takes longer for the peer to respond due to
46 // pending messages in the channel from the previous session. From experiment,
47 // it can take up to 20s for the session to reconnect. To make it safe, setting
48 // the timeout to 30s.
49 const int kSessionInitiateAndAcceptTimeout = kDefaultMessageTimeout * 3;
51 // Timeout for the transport-info messages.
52 const int kTransportInfoTimeout = 10 * 60;
54 // Name of the multiplexed channel.
55 const char kMuxChannelName[] = "mux";
57 ErrorCode AuthRejectionReasonToErrorCode(
58 Authenticator::RejectionReason reason) {
59 switch (reason) {
60 case Authenticator::INVALID_CREDENTIALS:
61 return AUTHENTICATION_FAILED;
62 case Authenticator::PROTOCOL_ERROR:
63 return INCOMPATIBLE_PROTOCOL;
65 NOTREACHED();
66 return UNKNOWN_ERROR;
69 } // namespace
71 JingleSession::JingleSession(JingleSessionManager* session_manager)
72 : session_manager_(session_manager),
73 event_handler_(nullptr),
74 state_(INITIALIZING),
75 error_(OK),
76 weak_factory_(this) {
79 JingleSession::~JingleSession() {
80 channel_multiplexer_.reset();
81 STLDeleteContainerPointers(pending_requests_.begin(),
82 pending_requests_.end());
83 STLDeleteContainerPointers(transport_info_requests_.begin(),
84 transport_info_requests_.end());
86 DCHECK(channels_.empty());
88 session_manager_->SessionDestroyed(this);
91 void JingleSession::SetEventHandler(Session::EventHandler* event_handler) {
92 DCHECK(CalledOnValidThread());
93 DCHECK(event_handler);
94 event_handler_ = event_handler;
97 ErrorCode JingleSession::error() {
98 DCHECK(CalledOnValidThread());
99 return error_;
102 void JingleSession::StartConnection(const std::string& peer_jid,
103 scoped_ptr<Authenticator> authenticator) {
104 DCHECK(CalledOnValidThread());
105 DCHECK(authenticator.get());
106 DCHECK_EQ(authenticator->state(), Authenticator::MESSAGE_READY);
108 peer_jid_ = peer_jid;
109 authenticator_ = authenticator.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(session_manager_->protocol_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;
141 SetState(ACCEPTING);
143 config_ =
144 SessionConfig::SelectCommon(initiate_message.description->config(),
145 session_manager_->protocol_config_.get());
146 if (!config_) {
147 LOG(WARNING) << "Rejecting connection from " << peer_jid_
148 << " because no compatible configuration has been found.";
149 CloseInternal(INCOMPATIBLE_PROTOCOL);
153 void JingleSession::AcceptIncomingConnection(
154 const JingleMessage& initiate_message) {
155 DCHECK(config_);
157 // Process the first authentication message.
158 const buzz::XmlElement* first_auth_message =
159 initiate_message.description->authenticator_message();
161 if (!first_auth_message) {
162 CloseInternal(INCOMPATIBLE_PROTOCOL);
163 return;
166 DCHECK_EQ(authenticator_->state(), Authenticator::WAITING_MESSAGE);
167 // |authenticator_| is owned, so Unretained() is safe here.
168 authenticator_->ProcessMessage(first_auth_message, base::Bind(
169 &JingleSession::ContinueAcceptIncomingConnection,
170 base::Unretained(this)));
173 void JingleSession::ContinueAcceptIncomingConnection() {
174 DCHECK_NE(authenticator_->state(), Authenticator::PROCESSING_MESSAGE);
175 if (authenticator_->state() == Authenticator::REJECTED) {
176 CloseInternal(AuthRejectionReasonToErrorCode(
177 authenticator_->rejection_reason()));
178 return;
181 // Send the session-accept message.
182 JingleMessage message(peer_jid_, JingleMessage::SESSION_ACCEPT,
183 session_id_);
185 scoped_ptr<buzz::XmlElement> auth_message;
186 if (authenticator_->state() == Authenticator::MESSAGE_READY)
187 auth_message = authenticator_->GetNextMessage();
189 message.description.reset(
190 new ContentDescription(CandidateSessionConfig::CreateFrom(*config_),
191 auth_message.Pass()));
192 SendMessage(message);
194 // Update state.
195 SetState(CONNECTED);
197 if (authenticator_->state() == Authenticator::ACCEPTED) {
198 OnAuthenticated();
199 } else {
200 DCHECK_EQ(authenticator_->state(), Authenticator::WAITING_MESSAGE);
201 if (authenticator_->started()) {
202 SetState(AUTHENTICATING);
207 const std::string& JingleSession::jid() {
208 DCHECK(CalledOnValidThread());
209 return peer_jid_;
212 const SessionConfig& JingleSession::config() {
213 DCHECK(CalledOnValidThread());
214 return *config_;
217 StreamChannelFactory* JingleSession::GetTransportChannelFactory() {
218 DCHECK(CalledOnValidThread());
219 return secure_channel_factory_.get();
222 StreamChannelFactory* JingleSession::GetMultiplexedChannelFactory() {
223 DCHECK(CalledOnValidThread());
224 if (!channel_multiplexer_.get()) {
225 channel_multiplexer_.reset(
226 new ChannelMultiplexer(GetTransportChannelFactory(), kMuxChannelName));
228 return channel_multiplexer_.get();
231 void JingleSession::Close() {
232 DCHECK(CalledOnValidThread());
234 CloseInternal(OK);
237 void JingleSession::AddPendingRemoteTransportInfo(Transport* channel) {
238 std::list<JingleMessage::IceCredentials>::iterator credentials =
239 pending_remote_ice_credentials_.begin();
240 while (credentials != pending_remote_ice_credentials_.end()) {
241 if (credentials->channel == channel->name()) {
242 channel->SetRemoteCredentials(credentials->ufrag, credentials->password);
243 credentials = pending_remote_ice_credentials_.erase(credentials);
244 } else {
245 ++credentials;
249 std::list<JingleMessage::NamedCandidate>::iterator candidate =
250 pending_remote_candidates_.begin();
251 while (candidate != pending_remote_candidates_.end()) {
252 if (candidate->name == channel->name()) {
253 channel->AddRemoteCandidate(candidate->candidate);
254 candidate = pending_remote_candidates_.erase(candidate);
255 } else {
256 ++candidate;
261 void JingleSession::CreateChannel(const std::string& name,
262 const ChannelCreatedCallback& callback) {
263 DCHECK(!channels_[name]);
265 scoped_ptr<Transport> channel =
266 session_manager_->transport_factory_->CreateTransport();
267 channel->Connect(name, this, callback);
268 AddPendingRemoteTransportInfo(channel.get());
269 channels_[name] = channel.release();
272 void JingleSession::CancelChannelCreation(const std::string& name) {
273 ChannelsMap::iterator it = channels_.find(name);
274 if (it != channels_.end()) {
275 DCHECK(!it->second->is_connected());
276 delete it->second;
277 DCHECK(channels_.find(name) == channels_.end());
281 void JingleSession::OnTransportIceCredentials(Transport* transport,
282 const std::string& ufrag,
283 const std::string& password) {
284 EnsurePendingTransportInfoMessage();
285 pending_transport_info_message_->ice_credentials.push_back(
286 JingleMessage::IceCredentials(transport->name(), ufrag, password));
289 void JingleSession::OnTransportCandidate(Transport* transport,
290 const cricket::Candidate& candidate) {
291 EnsurePendingTransportInfoMessage();
292 pending_transport_info_message_->candidates.push_back(
293 JingleMessage::NamedCandidate(transport->name(), candidate));
296 void JingleSession::OnTransportRouteChange(Transport* transport,
297 const TransportRoute& route) {
298 if (event_handler_)
299 event_handler_->OnSessionRouteChange(transport->name(), route);
302 void JingleSession::OnTransportFailed(Transport* transport) {
303 CloseInternal(CHANNEL_CONNECTION_ERROR);
306 void JingleSession::OnTransportDeleted(Transport* transport) {
307 ChannelsMap::iterator it = channels_.find(transport->name());
308 DCHECK_EQ(it->second, transport);
309 channels_.erase(it);
312 void JingleSession::SendMessage(const JingleMessage& message) {
313 scoped_ptr<IqRequest> request = session_manager_->iq_sender()->SendIq(
314 message.ToXml(),
315 base::Bind(&JingleSession::OnMessageResponse,
316 base::Unretained(this),
317 message.action));
319 int timeout = kDefaultMessageTimeout;
320 if (message.action == JingleMessage::SESSION_INITIATE ||
321 message.action == JingleMessage::SESSION_ACCEPT) {
322 timeout = kSessionInitiateAndAcceptTimeout;
324 if (request) {
325 request->SetTimeout(base::TimeDelta::FromSeconds(timeout));
326 pending_requests_.insert(request.release());
327 } else {
328 LOG(ERROR) << "Failed to send a "
329 << JingleMessage::GetActionName(message.action) << " message";
333 void JingleSession::OnMessageResponse(
334 JingleMessage::ActionType request_type,
335 IqRequest* request,
336 const buzz::XmlElement* response) {
337 // Delete the request from the list of pending requests.
338 pending_requests_.erase(request);
339 delete request;
341 // Ignore all responses after session was closed.
342 if (state_ == CLOSED || state_ == FAILED)
343 return;
345 std::string type_str = JingleMessage::GetActionName(request_type);
347 // |response| will be nullptr if the request timed out.
348 if (!response) {
349 LOG(ERROR) << type_str << " request timed out.";
350 CloseInternal(SIGNALING_TIMEOUT);
351 return;
352 } else {
353 const std::string& type =
354 response->Attr(buzz::QName(std::string(), "type"));
355 if (type != "result") {
356 LOG(ERROR) << "Received error in response to " << type_str
357 << " message: \"" << response->Str()
358 << "\". Terminating the session.";
360 // TODO(sergeyu): There may be different reasons for error
361 // here. Parse the response stanza to find failure reason.
362 CloseInternal(PEER_IS_OFFLINE);
367 void JingleSession::EnsurePendingTransportInfoMessage() {
368 // |transport_info_timer_| must be running iff
369 // |pending_transport_info_message_| exists.
370 DCHECK_EQ(pending_transport_info_message_ != nullptr,
371 transport_info_timer_.IsRunning());
373 if (!pending_transport_info_message_) {
374 pending_transport_info_message_.reset(new JingleMessage(
375 peer_jid_, JingleMessage::TRANSPORT_INFO, session_id_));
376 // Delay sending the new candidates in case we get more candidates
377 // that we can send in one message.
378 transport_info_timer_.Start(
379 FROM_HERE, base::TimeDelta::FromMilliseconds(kTransportInfoSendDelayMs),
380 this, &JingleSession::SendTransportInfo);
384 void JingleSession::SendTransportInfo() {
385 DCHECK(pending_transport_info_message_);
387 scoped_ptr<IqRequest> request = session_manager_->iq_sender()->SendIq(
388 pending_transport_info_message_->ToXml(),
389 base::Bind(&JingleSession::OnTransportInfoResponse,
390 base::Unretained(this)));
391 pending_transport_info_message_.reset();
392 if (request) {
393 request->SetTimeout(base::TimeDelta::FromSeconds(kTransportInfoTimeout));
394 transport_info_requests_.push_back(request.release());
395 } else {
396 LOG(ERROR) << "Failed to send a transport-info message";
400 void JingleSession::OnTransportInfoResponse(IqRequest* request,
401 const buzz::XmlElement* response) {
402 DCHECK(!transport_info_requests_.empty());
404 // Consider transport-info requests sent before this one lost and delete
405 // corresponding IqRequest objects.
406 while (transport_info_requests_.front() != request) {
407 delete transport_info_requests_.front();
408 transport_info_requests_.pop_front();
411 // Delete the |request| itself.
412 DCHECK_EQ(request, transport_info_requests_.front());
413 delete request;
414 transport_info_requests_.pop_front();
416 // Ignore transport-info timeouts.
417 if (!response) {
418 LOG(ERROR) << "transport-info request has timed out.";
419 return;
422 const std::string& type = response->Attr(buzz::QName(std::string(), "type"));
423 if (type != "result") {
424 LOG(ERROR) << "Received error in response to transport-info message: \""
425 << response->Str() << "\". Terminating the session.";
426 CloseInternal(PEER_IS_OFFLINE);
430 void JingleSession::OnIncomingMessage(const JingleMessage& message,
431 const ReplyCallback& reply_callback) {
432 DCHECK(CalledOnValidThread());
434 if (message.from != peer_jid_) {
435 // Ignore messages received from a different Jid.
436 reply_callback.Run(JingleMessageReply::INVALID_SID);
437 return;
440 switch (message.action) {
441 case JingleMessage::SESSION_ACCEPT:
442 OnAccept(message, reply_callback);
443 break;
445 case JingleMessage::SESSION_INFO:
446 OnSessionInfo(message, reply_callback);
447 break;
449 case JingleMessage::TRANSPORT_INFO:
450 reply_callback.Run(JingleMessageReply::NONE);
451 ProcessTransportInfo(message);
452 break;
454 case JingleMessage::SESSION_TERMINATE:
455 OnTerminate(message, reply_callback);
456 break;
458 default:
459 reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
463 void JingleSession::OnAccept(const JingleMessage& message,
464 const ReplyCallback& reply_callback) {
465 if (state_ != CONNECTING) {
466 reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
467 return;
470 reply_callback.Run(JingleMessageReply::NONE);
472 const buzz::XmlElement* auth_message =
473 message.description->authenticator_message();
474 if (!auth_message) {
475 DLOG(WARNING) << "Received session-accept without authentication message ";
476 CloseInternal(INCOMPATIBLE_PROTOCOL);
477 return;
480 if (!InitializeConfigFromDescription(message.description.get())) {
481 CloseInternal(INCOMPATIBLE_PROTOCOL);
482 return;
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::IceCredentials>::const_iterator it =
517 message.ice_credentials.begin();
518 it != message.ice_credentials.end(); ++it) {
519 ChannelsMap::iterator channel = channels_.find(it->channel);
520 if (channel != channels_.end()) {
521 channel->second->SetRemoteCredentials(it->ufrag, it->password);
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_ice_credentials_.push_back(*it);
529 for (std::list<JingleMessage::NamedCandidate>::const_iterator it =
530 message.candidates.begin();
531 it != message.candidates.end(); ++it) {
532 ChannelsMap::iterator channel = channels_.find(it->name);
533 if (channel != channels_.end()) {
534 channel->second->AddRemoteCandidate(it->candidate);
535 } else {
536 // Transport info was received before the channel was created.
537 // This could happen due to messages being reordered on the wire.
538 pending_remote_candidates_.push_back(*it);
543 void JingleSession::OnTerminate(const JingleMessage& message,
544 const ReplyCallback& reply_callback) {
545 if (!is_session_active()) {
546 LOG(WARNING) << "Received unexpected session-terminate message.";
547 reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
548 return;
551 reply_callback.Run(JingleMessageReply::NONE);
553 switch (message.reason) {
554 case JingleMessage::SUCCESS:
555 if (state_ == CONNECTING) {
556 error_ = SESSION_REJECTED;
557 } else {
558 error_ = OK;
560 break;
561 case JingleMessage::DECLINE:
562 error_ = AUTHENTICATION_FAILED;
563 break;
564 case JingleMessage::CANCEL:
565 error_ = HOST_OVERLOAD;
566 break;
567 case JingleMessage::GENERAL_ERROR:
568 error_ = CHANNEL_CONNECTION_ERROR;
569 break;
570 case JingleMessage::INCOMPATIBLE_PARAMETERS:
571 error_ = INCOMPATIBLE_PROTOCOL;
572 break;
573 default:
574 error_ = UNKNOWN_ERROR;
577 if (error_ != OK) {
578 SetState(FAILED);
579 } else {
580 SetState(CLOSED);
584 bool JingleSession::InitializeConfigFromDescription(
585 const ContentDescription* description) {
586 DCHECK(description);
587 config_ = SessionConfig::GetFinalConfig(description->config());
588 if (!config_) {
589 LOG(ERROR) << "session-accept does not specify configuration";
590 return false;
592 if (!session_manager_->protocol_config_->IsSupported(*config_)) {
593 LOG(ERROR) << "session-accept specifies an invalid configuration";
594 return false;
597 return true;
600 void JingleSession::ProcessAuthenticationStep() {
601 DCHECK(CalledOnValidThread());
602 DCHECK_NE(authenticator_->state(), Authenticator::PROCESSING_MESSAGE);
604 if (state_ != CONNECTED && state_ != AUTHENTICATING) {
605 DCHECK(state_ == FAILED || state_ == CLOSED);
606 // The remote host closed the connection while the authentication was being
607 // processed asynchronously, nothing to do.
608 return;
611 if (authenticator_->state() == Authenticator::MESSAGE_READY) {
612 JingleMessage message(peer_jid_, JingleMessage::SESSION_INFO, session_id_);
613 message.info = authenticator_->GetNextMessage();
614 DCHECK(message.info.get());
615 SendMessage(message);
617 DCHECK_NE(authenticator_->state(), Authenticator::MESSAGE_READY);
619 // The current JingleSession object can be destroyed by event_handler of
620 // SetState(AUTHENTICATING) and cause subsequent dereferencing of the this
621 // pointer to crash. To protect against it, we run ContinueAuthenticationStep
622 // asychronously using a weak pointer.
623 base::ThreadTaskRunnerHandle::Get()->PostTask(
624 FROM_HERE,
625 base::Bind(&JingleSession::ContinueAuthenticationStep,
626 weak_factory_.GetWeakPtr()));
628 if (authenticator_->started()) {
629 SetState(AUTHENTICATING);
633 void JingleSession::ContinueAuthenticationStep() {
634 if (authenticator_->state() == Authenticator::ACCEPTED) {
635 OnAuthenticated();
636 } else if (authenticator_->state() == Authenticator::REJECTED) {
637 CloseInternal(AuthRejectionReasonToErrorCode(
638 authenticator_->rejection_reason()));
642 void JingleSession::OnAuthenticated() {
643 pseudotcp_channel_factory_.reset(new PseudoTcpChannelFactory(this));
644 secure_channel_factory_.reset(
645 new SecureChannelFactory(pseudotcp_channel_factory_.get(),
646 authenticator_.get()));
648 SetState(AUTHENTICATED);
651 void JingleSession::CloseInternal(ErrorCode error) {
652 DCHECK(CalledOnValidThread());
654 if (is_session_active()) {
655 // Send session-terminate message with the appropriate error code.
656 JingleMessage::Reason reason;
657 switch (error) {
658 case OK:
659 reason = JingleMessage::SUCCESS;
660 break;
661 case SESSION_REJECTED:
662 case AUTHENTICATION_FAILED:
663 reason = JingleMessage::DECLINE;
664 break;
665 case INCOMPATIBLE_PROTOCOL:
666 reason = JingleMessage::INCOMPATIBLE_PARAMETERS;
667 break;
668 case HOST_OVERLOAD:
669 reason = JingleMessage::CANCEL;
670 break;
671 default:
672 reason = JingleMessage::GENERAL_ERROR;
675 JingleMessage message(peer_jid_, JingleMessage::SESSION_TERMINATE,
676 session_id_);
677 message.reason = reason;
678 SendMessage(message);
681 error_ = error;
683 if (state_ != FAILED && state_ != CLOSED) {
684 if (error != OK) {
685 SetState(FAILED);
686 } else {
687 SetState(CLOSED);
692 void JingleSession::SetState(State new_state) {
693 DCHECK(CalledOnValidThread());
695 if (new_state != state_) {
696 DCHECK_NE(state_, CLOSED);
697 DCHECK_NE(state_, FAILED);
699 state_ = new_state;
700 if (event_handler_)
701 event_handler_->OnSessionStateChange(new_state);
705 bool JingleSession::is_session_active() {
706 return state_ == CONNECTING || state_ == ACCEPTING || state_ == CONNECTED ||
707 state_ == AUTHENTICATING || state_ == AUTHENTICATED;
710 } // namespace protocol
711 } // namespace remoting