Revert 264226 "Reduce dependency of TiclInvalidationService on P..."
[chromium-blink-merge.git] / remoting / protocol / jingle_session.cc
blob57883cb4a7300995fc8265cb2f8808a0958559c4
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/jingle_glue/iq_sender.h"
16 #include "remoting/protocol/authenticator.h"
17 #include "remoting/protocol/channel_authenticator.h"
18 #include "remoting/protocol/channel_multiplexer.h"
19 #include "remoting/protocol/content_description.h"
20 #include "remoting/protocol/jingle_messages.h"
21 #include "remoting/protocol/jingle_session_manager.h"
22 #include "remoting/protocol/session_config.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 // Timeout for the transport-info messages.
44 const int kTransportInfoTimeout = 10 * 60;
46 // Name of the multiplexed channel.
47 const char kMuxChannelName[] = "mux";
49 ErrorCode AuthRejectionReasonToErrorCode(
50 Authenticator::RejectionReason reason) {
51 switch (reason) {
52 case Authenticator::INVALID_CREDENTIALS:
53 return AUTHENTICATION_FAILED;
54 case Authenticator::PROTOCOL_ERROR:
55 return INCOMPATIBLE_PROTOCOL;
57 NOTREACHED();
58 return UNKNOWN_ERROR;
61 } // namespace
63 JingleSession::JingleSession(JingleSessionManager* session_manager)
64 : session_manager_(session_manager),
65 event_handler_(NULL),
66 state_(INITIALIZING),
67 error_(OK),
68 config_is_set_(false),
69 weak_factory_(this) {
72 JingleSession::~JingleSession() {
73 channel_multiplexer_.reset();
74 STLDeleteContainerPointers(pending_requests_.begin(),
75 pending_requests_.end());
76 STLDeleteContainerPointers(transport_info_requests_.begin(),
77 transport_info_requests_.end());
78 STLDeleteContainerPairSecondPointers(channels_.begin(), channels_.end());
79 session_manager_->SessionDestroyed(this);
82 void JingleSession::SetEventHandler(Session::EventHandler* event_handler) {
83 DCHECK(CalledOnValidThread());
84 DCHECK(event_handler);
85 event_handler_ = event_handler;
88 ErrorCode JingleSession::error() {
89 DCHECK(CalledOnValidThread());
90 return error_;
93 void JingleSession::StartConnection(
94 const std::string& peer_jid,
95 scoped_ptr<Authenticator> authenticator,
96 scoped_ptr<CandidateSessionConfig> config) {
97 DCHECK(CalledOnValidThread());
98 DCHECK(authenticator.get());
99 DCHECK_EQ(authenticator->state(), Authenticator::MESSAGE_READY);
101 peer_jid_ = peer_jid;
102 authenticator_ = authenticator.Pass();
103 candidate_config_ = config.Pass();
105 // Generate random session ID. There are usually not more than 1
106 // concurrent session per host, so a random 64-bit integer provides
107 // enough entropy. In the worst case connection will fail when two
108 // clients generate the same session ID concurrently.
109 session_id_ = base::Int64ToString(base::RandGenerator(kint64max));
111 // Send session-initiate message.
112 JingleMessage message(peer_jid_, JingleMessage::SESSION_INITIATE,
113 session_id_);
114 message.initiator = session_manager_->signal_strategy_->GetLocalJid();
115 message.description.reset(
116 new ContentDescription(candidate_config_->Clone(),
117 authenticator_->GetNextMessage()));
118 SendMessage(message);
120 SetState(CONNECTING);
123 void JingleSession::InitializeIncomingConnection(
124 const JingleMessage& initiate_message,
125 scoped_ptr<Authenticator> authenticator) {
126 DCHECK(CalledOnValidThread());
127 DCHECK(initiate_message.description.get());
128 DCHECK(authenticator.get());
129 DCHECK_EQ(authenticator->state(), Authenticator::WAITING_MESSAGE);
131 peer_jid_ = initiate_message.from;
132 authenticator_ = authenticator.Pass();
133 session_id_ = initiate_message.sid;
134 candidate_config_ = initiate_message.description->config()->Clone();
136 SetState(ACCEPTING);
139 void JingleSession::AcceptIncomingConnection(
140 const JingleMessage& initiate_message) {
141 DCHECK(config_is_set_);
143 // Process the first authentication message.
144 const buzz::XmlElement* first_auth_message =
145 initiate_message.description->authenticator_message();
147 if (!first_auth_message) {
148 CloseInternal(INCOMPATIBLE_PROTOCOL);
149 return;
152 DCHECK_EQ(authenticator_->state(), Authenticator::WAITING_MESSAGE);
153 // |authenticator_| is owned, so Unretained() is safe here.
154 authenticator_->ProcessMessage(first_auth_message, base::Bind(
155 &JingleSession::ContinueAcceptIncomingConnection,
156 base::Unretained(this)));
159 void JingleSession::ContinueAcceptIncomingConnection() {
160 DCHECK_NE(authenticator_->state(), Authenticator::PROCESSING_MESSAGE);
161 if (authenticator_->state() == Authenticator::REJECTED) {
162 CloseInternal(AuthRejectionReasonToErrorCode(
163 authenticator_->rejection_reason()));
164 return;
167 // Send the session-accept message.
168 JingleMessage message(peer_jid_, JingleMessage::SESSION_ACCEPT,
169 session_id_);
171 scoped_ptr<buzz::XmlElement> auth_message;
172 if (authenticator_->state() == Authenticator::MESSAGE_READY)
173 auth_message = authenticator_->GetNextMessage();
175 message.description.reset(
176 new ContentDescription(CandidateSessionConfig::CreateFrom(config_),
177 auth_message.Pass()));
178 SendMessage(message);
180 // Update state.
181 SetState(CONNECTED);
183 if (authenticator_->state() == Authenticator::ACCEPTED) {
184 SetState(AUTHENTICATED);
185 } else {
186 DCHECK_EQ(authenticator_->state(), Authenticator::WAITING_MESSAGE);
187 if (authenticator_->started()) {
188 SetState(AUTHENTICATING);
193 const std::string& JingleSession::jid() {
194 DCHECK(CalledOnValidThread());
195 return peer_jid_;
198 const CandidateSessionConfig* JingleSession::candidate_config() {
199 DCHECK(CalledOnValidThread());
200 return candidate_config_.get();
203 const SessionConfig& JingleSession::config() {
204 DCHECK(CalledOnValidThread());
205 return config_;
208 void JingleSession::set_config(const SessionConfig& config) {
209 DCHECK(CalledOnValidThread());
210 DCHECK(!config_is_set_);
211 config_ = config;
212 config_is_set_ = true;
215 ChannelFactory* JingleSession::GetTransportChannelFactory() {
216 DCHECK(CalledOnValidThread());
217 return this;
220 ChannelFactory* JingleSession::GetMultiplexedChannelFactory() {
221 DCHECK(CalledOnValidThread());
222 if (!channel_multiplexer_.get())
223 channel_multiplexer_.reset(new ChannelMultiplexer(this, kMuxChannelName));
224 return channel_multiplexer_.get();
227 void JingleSession::Close() {
228 DCHECK(CalledOnValidThread());
230 CloseInternal(OK);
233 void JingleSession::AddPendingRemoteCandidates(Transport* channel,
234 const std::string& name) {
235 std::list<JingleMessage::NamedCandidate>::iterator it =
236 pending_remote_candidates_.begin();
237 while(it != pending_remote_candidates_.end()) {
238 if (it->name == name) {
239 channel->AddRemoteCandidate(it->candidate);
240 it = pending_remote_candidates_.erase(it);
241 } else {
242 ++it;
247 void JingleSession::CreateStreamChannel(
248 const std::string& name,
249 const StreamChannelCallback& callback) {
250 DCHECK(!channels_[name]);
252 scoped_ptr<ChannelAuthenticator> channel_authenticator =
253 authenticator_->CreateChannelAuthenticator();
254 scoped_ptr<StreamTransport> channel =
255 session_manager_->transport_factory_->CreateStreamTransport();
256 channel->Initialize(name, this, channel_authenticator.Pass());
257 channel->Connect(callback);
258 AddPendingRemoteCandidates(channel.get(), name);
259 channels_[name] = channel.release();
262 void JingleSession::CreateDatagramChannel(
263 const std::string& name,
264 const DatagramChannelCallback& callback) {
265 DCHECK(!channels_[name]);
267 scoped_ptr<ChannelAuthenticator> channel_authenticator =
268 authenticator_->CreateChannelAuthenticator();
269 scoped_ptr<DatagramTransport> channel =
270 session_manager_->transport_factory_->CreateDatagramTransport();
271 channel->Initialize(name, this, channel_authenticator.Pass());
272 channel->Connect(callback);
273 AddPendingRemoteCandidates(channel.get(), name);
274 channels_[name] = channel.release();
277 void JingleSession::CancelChannelCreation(const std::string& name) {
278 ChannelsMap::iterator it = channels_.find(name);
279 if (it != channels_.end() && !it->second->is_connected()) {
280 delete it->second;
281 DCHECK(!channels_[name]);
285 void JingleSession::OnTransportCandidate(Transport* transport,
286 const cricket::Candidate& candidate) {
287 pending_candidates_.push_back(JingleMessage::NamedCandidate(
288 transport->name(), candidate));
290 if (!transport_infos_timer_.IsRunning()) {
291 // Delay sending the new candidates in case we get more candidates
292 // that we can send in one message.
293 transport_infos_timer_.Start(
294 FROM_HERE, base::TimeDelta::FromMilliseconds(kTransportInfoSendDelayMs),
295 this, &JingleSession::SendTransportInfo);
299 void JingleSession::OnTransportRouteChange(Transport* transport,
300 const TransportRoute& route) {
301 if (event_handler_)
302 event_handler_->OnSessionRouteChange(transport->name(), route);
305 void JingleSession::OnTransportReady(Transport* transport, bool ready) {
306 if (event_handler_)
307 event_handler_->OnSessionChannelReady(transport->name(), ready);
310 void JingleSession::OnTransportFailed(Transport* transport) {
311 CloseInternal(CHANNEL_CONNECTION_ERROR);
314 void JingleSession::OnTransportDeleted(Transport* transport) {
315 ChannelsMap::iterator it = channels_.find(transport->name());
316 DCHECK_EQ(it->second, transport);
317 channels_.erase(it);
320 void JingleSession::SendMessage(const JingleMessage& message) {
321 scoped_ptr<IqRequest> request = session_manager_->iq_sender()->SendIq(
322 message.ToXml(),
323 base::Bind(&JingleSession::OnMessageResponse,
324 base::Unretained(this), message.action));
325 if (request) {
326 request->SetTimeout(base::TimeDelta::FromSeconds(kDefaultMessageTimeout));
327 pending_requests_.insert(request.release());
328 } else {
329 LOG(ERROR) << "Failed to send a "
330 << JingleMessage::GetActionName(message.action) << " message";
334 void JingleSession::OnMessageResponse(
335 JingleMessage::ActionType request_type,
336 IqRequest* request,
337 const buzz::XmlElement* response) {
338 std::string type_str = JingleMessage::GetActionName(request_type);
340 // Delete the request from the list of pending requests.
341 pending_requests_.erase(request);
342 delete request;
344 // |response| will be NULL if the request timed out.
345 if (!response) {
346 LOG(ERROR) << type_str << " request timed out.";
347 CloseInternal(SIGNALING_TIMEOUT);
348 return;
349 } else {
350 const std::string& type =
351 response->Attr(buzz::QName(std::string(), "type"));
352 if (type != "result") {
353 LOG(ERROR) << "Received error in response to " << type_str
354 << " message: \"" << response->Str()
355 << "\". Terminating the session.";
357 switch (request_type) {
358 case JingleMessage::SESSION_INFO:
359 // session-info is used for the new authentication protocol,
360 // and wasn't previously supported.
361 CloseInternal(INCOMPATIBLE_PROTOCOL);
362 break;
364 default:
365 // TODO(sergeyu): There may be different reasons for error
366 // here. Parse the response stanza to find failure reason.
367 CloseInternal(PEER_IS_OFFLINE);
373 void JingleSession::SendTransportInfo() {
374 JingleMessage message(peer_jid_, JingleMessage::TRANSPORT_INFO, session_id_);
375 message.candidates.swap(pending_candidates_);
377 scoped_ptr<IqRequest> request = session_manager_->iq_sender()->SendIq(
378 message.ToXml(),
379 base::Bind(&JingleSession::OnTransportInfoResponse,
380 base::Unretained(this)));
381 if (request) {
382 request->SetTimeout(base::TimeDelta::FromSeconds(kTransportInfoTimeout));
383 transport_info_requests_.push_back(request.release());
384 } else {
385 LOG(ERROR) << "Failed to send a transport-info message";
389 void JingleSession::OnTransportInfoResponse(IqRequest* request,
390 const buzz::XmlElement* response) {
391 DCHECK(!transport_info_requests_.empty());
393 // Consider transport-info requests sent before this one lost and delete
394 // corresponding IqRequest objects.
395 while (transport_info_requests_.front() != request) {
396 delete transport_info_requests_.front();
397 transport_info_requests_.pop_front();
400 // Delete the |request| itself.
401 DCHECK_EQ(request, transport_info_requests_.front());
402 delete request;
403 transport_info_requests_.pop_front();
405 // Ignore transport-info timeouts.
406 if (!response) {
407 LOG(ERROR) << "transport-info request has timed out.";
408 return;
411 const std::string& type = response->Attr(buzz::QName(std::string(), "type"));
412 if (type != "result") {
413 LOG(ERROR) << "Received error in response to transport-info message: \""
414 << response->Str() << "\". Terminating the session.";
415 CloseInternal(PEER_IS_OFFLINE);
419 void JingleSession::OnIncomingMessage(const JingleMessage& message,
420 const ReplyCallback& reply_callback) {
421 DCHECK(CalledOnValidThread());
423 if (message.from != peer_jid_) {
424 // Ignore messages received from a different Jid.
425 reply_callback.Run(JingleMessageReply::INVALID_SID);
426 return;
429 switch (message.action) {
430 case JingleMessage::SESSION_ACCEPT:
431 OnAccept(message, reply_callback);
432 break;
434 case JingleMessage::SESSION_INFO:
435 OnSessionInfo(message, reply_callback);
436 break;
438 case JingleMessage::TRANSPORT_INFO:
439 reply_callback.Run(JingleMessageReply::NONE);
440 ProcessTransportInfo(message);
441 break;
443 case JingleMessage::SESSION_TERMINATE:
444 OnTerminate(message, reply_callback);
445 break;
447 default:
448 reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
452 void JingleSession::OnAccept(const JingleMessage& message,
453 const ReplyCallback& reply_callback) {
454 if (state_ != CONNECTING) {
455 reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
456 return;
459 reply_callback.Run(JingleMessageReply::NONE);
461 const buzz::XmlElement* auth_message =
462 message.description->authenticator_message();
463 if (!auth_message) {
464 DLOG(WARNING) << "Received session-accept without authentication message ";
465 CloseInternal(INCOMPATIBLE_PROTOCOL);
466 return;
469 if (!InitializeConfigFromDescription(message.description.get())) {
470 CloseInternal(INCOMPATIBLE_PROTOCOL);
471 return;
474 // In case there is transport information in the accept message.
475 ProcessTransportInfo(message);
477 SetState(CONNECTED);
479 DCHECK(authenticator_->state() == Authenticator::WAITING_MESSAGE);
480 authenticator_->ProcessMessage(auth_message, base::Bind(
481 &JingleSession::ProcessAuthenticationStep,base::Unretained(this)));
484 void JingleSession::OnSessionInfo(const JingleMessage& message,
485 const ReplyCallback& reply_callback) {
486 if (!message.info.get() ||
487 !Authenticator::IsAuthenticatorMessage(message.info.get())) {
488 reply_callback.Run(JingleMessageReply::UNSUPPORTED_INFO);
489 return;
492 if ((state_ != CONNECTED && state_ != AUTHENTICATING) ||
493 authenticator_->state() != Authenticator::WAITING_MESSAGE) {
494 LOG(WARNING) << "Received unexpected authenticator message "
495 << message.info->Str();
496 reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
497 CloseInternal(INCOMPATIBLE_PROTOCOL);
498 return;
501 reply_callback.Run(JingleMessageReply::NONE);
503 authenticator_->ProcessMessage(message.info.get(), base::Bind(
504 &JingleSession::ProcessAuthenticationStep, base::Unretained(this)));
507 void JingleSession::ProcessTransportInfo(const JingleMessage& message) {
508 for (std::list<JingleMessage::NamedCandidate>::const_iterator it =
509 message.candidates.begin();
510 it != message.candidates.end(); ++it) {
511 ChannelsMap::iterator channel = channels_.find(it->name);
512 if (channel != channels_.end()) {
513 channel->second->AddRemoteCandidate(it->candidate);
514 } else {
515 // Transport info was received before the channel was created.
516 // This could happen due to messages being reordered on the wire.
517 pending_remote_candidates_.push_back(*it);
522 void JingleSession::OnTerminate(const JingleMessage& message,
523 const ReplyCallback& reply_callback) {
524 if (!is_session_active()) {
525 LOG(WARNING) << "Received unexpected session-terminate message.";
526 reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
527 return;
530 reply_callback.Run(JingleMessageReply::NONE);
532 switch (message.reason) {
533 case JingleMessage::SUCCESS:
534 if (state_ == CONNECTING) {
535 error_ = SESSION_REJECTED;
536 } else {
537 error_ = OK;
539 break;
540 case JingleMessage::DECLINE:
541 error_ = AUTHENTICATION_FAILED;
542 break;
543 case JingleMessage::CANCEL:
544 error_ = HOST_OVERLOAD;
545 break;
546 case JingleMessage::GENERAL_ERROR:
547 error_ = CHANNEL_CONNECTION_ERROR;
548 break;
549 case JingleMessage::INCOMPATIBLE_PARAMETERS:
550 error_ = INCOMPATIBLE_PROTOCOL;
551 break;
552 default:
553 error_ = UNKNOWN_ERROR;
556 if (error_ != OK) {
557 SetState(FAILED);
558 } else {
559 SetState(CLOSED);
563 bool JingleSession::InitializeConfigFromDescription(
564 const ContentDescription* description) {
565 DCHECK(description);
567 if (!description->config()->GetFinalConfig(&config_)) {
568 LOG(ERROR) << "session-accept does not specify configuration";
569 return false;
571 if (!candidate_config()->IsSupported(config_)) {
572 LOG(ERROR) << "session-accept specifies an invalid configuration";
573 return false;
576 return true;
579 void JingleSession::ProcessAuthenticationStep() {
580 DCHECK(CalledOnValidThread());
581 DCHECK_NE(authenticator_->state(), Authenticator::PROCESSING_MESSAGE);
583 if (state_ != CONNECTED && state_ != AUTHENTICATING) {
584 DCHECK(state_ == FAILED || state_ == CLOSED);
585 // The remote host closed the connection while the authentication was being
586 // processed asynchronously, nothing to do.
587 return;
590 if (authenticator_->state() == Authenticator::MESSAGE_READY) {
591 JingleMessage message(peer_jid_, JingleMessage::SESSION_INFO, session_id_);
592 message.info = authenticator_->GetNextMessage();
593 DCHECK(message.info.get());
594 SendMessage(message);
596 DCHECK_NE(authenticator_->state(), Authenticator::MESSAGE_READY);
598 // The current JingleSession object can be destroyed by event_handler of
599 // SetState(AUTHENTICATING) and cause subsequent dereferencing of the this
600 // pointer to crash. To protect against it, we run ContinueAuthenticationStep
601 // asychronously using a weak pointer.
602 base::ThreadTaskRunnerHandle::Get()->PostTask(
603 FROM_HERE,
604 base::Bind(&JingleSession::ContinueAuthenticationStep,
605 weak_factory_.GetWeakPtr()));
607 if (authenticator_->started()) {
608 SetState(AUTHENTICATING);
612 void JingleSession::ContinueAuthenticationStep() {
613 if (authenticator_->state() == Authenticator::ACCEPTED) {
614 SetState(AUTHENTICATED);
615 } else if (authenticator_->state() == Authenticator::REJECTED) {
616 CloseInternal(AuthRejectionReasonToErrorCode(
617 authenticator_->rejection_reason()));
621 void JingleSession::CloseInternal(ErrorCode error) {
622 DCHECK(CalledOnValidThread());
624 if (is_session_active()) {
625 // Send session-terminate message with the appropriate error code.
626 JingleMessage::Reason reason;
627 switch (error) {
628 case OK:
629 reason = JingleMessage::SUCCESS;
630 break;
631 case SESSION_REJECTED:
632 case AUTHENTICATION_FAILED:
633 reason = JingleMessage::DECLINE;
634 break;
635 case INCOMPATIBLE_PROTOCOL:
636 reason = JingleMessage::INCOMPATIBLE_PARAMETERS;
637 break;
638 case HOST_OVERLOAD:
639 reason = JingleMessage::CANCEL;
640 break;
641 default:
642 reason = JingleMessage::GENERAL_ERROR;
645 JingleMessage message(peer_jid_, JingleMessage::SESSION_TERMINATE,
646 session_id_);
647 message.reason = reason;
648 SendMessage(message);
651 error_ = error;
653 if (state_ != FAILED && state_ != CLOSED) {
654 if (error != OK) {
655 SetState(FAILED);
656 } else {
657 SetState(CLOSED);
662 void JingleSession::SetState(State new_state) {
663 DCHECK(CalledOnValidThread());
665 if (new_state != state_) {
666 DCHECK_NE(state_, CLOSED);
667 DCHECK_NE(state_, FAILED);
669 state_ = new_state;
670 if (event_handler_)
671 event_handler_->OnSessionStateChange(new_state);
675 bool JingleSession::is_session_active() {
676 return state_ == CONNECTING || state_ == ACCEPTING || state_ == CONNECTED ||
677 state_ == AUTHENTICATING || state_ == AUTHENTICATED;
680 } // namespace protocol
681 } // namespace remoting