Always preselect first manager pod, allow to select previous/next pod with up/down...
[chromium-blink-merge.git] / remoting / protocol / jingle_session.cc
blob2aecd4c25d6856d86a4f8f0ab1645219edad48a1
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/stl_util.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/time/time.h"
12 #include "remoting/base/constants.h"
13 #include "remoting/jingle_glue/iq_sender.h"
14 #include "remoting/protocol/authenticator.h"
15 #include "remoting/protocol/channel_authenticator.h"
16 #include "remoting/protocol/channel_multiplexer.h"
17 #include "remoting/protocol/content_description.h"
18 #include "remoting/protocol/jingle_messages.h"
19 #include "remoting/protocol/jingle_session_manager.h"
20 #include "remoting/protocol/session_config.h"
21 #include "third_party/libjingle/source/talk/p2p/base/candidate.h"
22 #include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
24 using buzz::XmlElement;
26 namespace remoting {
27 namespace protocol {
29 namespace {
30 // Delay after candidate creation before sending transport-info
31 // message. This is neccessary to be able to pack multiple candidates
32 // into one transport-info messages. The value needs to be greater
33 // than zero because ports are opened asynchronously in the browser
34 // process.
35 const int kTransportInfoSendDelayMs = 2;
37 // How long we should wait for a response from the other end. This value is used
38 // for all requests except |transport-info|.
39 const int kDefaultMessageTimeout = 10;
41 // Timeout for the transport-info messages.
42 const int kTransportInfoTimeout = 10 * 60;
44 // Name of the multiplexed channel.
45 const char kMuxChannelName[] = "mux";
47 ErrorCode AuthRejectionReasonToErrorCode(
48 Authenticator::RejectionReason reason) {
49 switch (reason) {
50 case Authenticator::INVALID_CREDENTIALS:
51 return AUTHENTICATION_FAILED;
52 case Authenticator::PROTOCOL_ERROR:
53 return INCOMPATIBLE_PROTOCOL;
55 NOTREACHED();
56 return UNKNOWN_ERROR;
59 } // namespace
61 JingleSession::JingleSession(JingleSessionManager* session_manager)
62 : session_manager_(session_manager),
63 event_handler_(NULL),
64 state_(INITIALIZING),
65 error_(OK),
66 config_is_set_(false) {
69 JingleSession::~JingleSession() {
70 channel_multiplexer_.reset();
71 STLDeleteContainerPointers(pending_requests_.begin(),
72 pending_requests_.end());
73 STLDeleteContainerPointers(transport_info_requests_.begin(),
74 transport_info_requests_.end());
75 STLDeleteContainerPairSecondPointers(channels_.begin(), channels_.end());
76 session_manager_->SessionDestroyed(this);
79 void JingleSession::SetEventHandler(Session::EventHandler* event_handler) {
80 DCHECK(CalledOnValidThread());
81 DCHECK(event_handler);
82 event_handler_ = event_handler;
85 ErrorCode JingleSession::error() {
86 DCHECK(CalledOnValidThread());
87 return error_;
90 void JingleSession::StartConnection(
91 const std::string& peer_jid,
92 scoped_ptr<Authenticator> authenticator,
93 scoped_ptr<CandidateSessionConfig> config) {
94 DCHECK(CalledOnValidThread());
95 DCHECK(authenticator.get());
96 DCHECK_EQ(authenticator->state(), Authenticator::MESSAGE_READY);
98 peer_jid_ = peer_jid;
99 authenticator_ = authenticator.Pass();
100 candidate_config_ = config.Pass();
102 // Generate random session ID. There are usually not more than 1
103 // concurrent session per host, so a random 64-bit integer provides
104 // enough entropy. In the worst case connection will fail when two
105 // clients generate the same session ID concurrently.
106 session_id_ = base::Int64ToString(base::RandGenerator(kint64max));
108 // Send session-initiate message.
109 JingleMessage message(peer_jid_, JingleMessage::SESSION_INITIATE,
110 session_id_);
111 message.initiator = session_manager_->signal_strategy_->GetLocalJid();
112 message.description.reset(
113 new ContentDescription(candidate_config_->Clone(),
114 authenticator_->GetNextMessage()));
115 SendMessage(message);
117 SetState(CONNECTING);
120 void JingleSession::InitializeIncomingConnection(
121 const JingleMessage& initiate_message,
122 scoped_ptr<Authenticator> authenticator) {
123 DCHECK(CalledOnValidThread());
124 DCHECK(initiate_message.description.get());
125 DCHECK(authenticator.get());
126 DCHECK_EQ(authenticator->state(), Authenticator::WAITING_MESSAGE);
128 peer_jid_ = initiate_message.from;
129 authenticator_ = authenticator.Pass();
130 session_id_ = initiate_message.sid;
131 candidate_config_ = initiate_message.description->config()->Clone();
133 SetState(ACCEPTING);
136 void JingleSession::AcceptIncomingConnection(
137 const JingleMessage& initiate_message) {
138 DCHECK(config_is_set_);
140 // Process the first authentication message.
141 const buzz::XmlElement* first_auth_message =
142 initiate_message.description->authenticator_message();
144 if (!first_auth_message) {
145 CloseInternal(INCOMPATIBLE_PROTOCOL);
146 return;
149 DCHECK_EQ(authenticator_->state(), Authenticator::WAITING_MESSAGE);
150 // |authenticator_| is owned, so Unretained() is safe here.
151 authenticator_->ProcessMessage(first_auth_message, base::Bind(
152 &JingleSession::ContinueAcceptIncomingConnection,
153 base::Unretained(this)));
156 void JingleSession::ContinueAcceptIncomingConnection() {
157 DCHECK_NE(authenticator_->state(), Authenticator::PROCESSING_MESSAGE);
158 if (authenticator_->state() == Authenticator::REJECTED) {
159 CloseInternal(AuthRejectionReasonToErrorCode(
160 authenticator_->rejection_reason()));
161 return;
164 // Send the session-accept message.
165 JingleMessage message(peer_jid_, JingleMessage::SESSION_ACCEPT,
166 session_id_);
168 scoped_ptr<buzz::XmlElement> auth_message;
169 if (authenticator_->state() == Authenticator::MESSAGE_READY)
170 auth_message = authenticator_->GetNextMessage();
172 message.description.reset(
173 new ContentDescription(CandidateSessionConfig::CreateFrom(config_),
174 auth_message.Pass()));
175 SendMessage(message);
177 // Update state.
178 SetState(CONNECTED);
180 if (authenticator_->state() == Authenticator::ACCEPTED) {
181 SetState(AUTHENTICATED);
182 } else {
183 DCHECK_EQ(authenticator_->state(), Authenticator::WAITING_MESSAGE);
186 return;
189 const std::string& JingleSession::jid() {
190 DCHECK(CalledOnValidThread());
191 return peer_jid_;
194 const CandidateSessionConfig* JingleSession::candidate_config() {
195 DCHECK(CalledOnValidThread());
196 return candidate_config_.get();
199 const SessionConfig& JingleSession::config() {
200 DCHECK(CalledOnValidThread());
201 return config_;
204 void JingleSession::set_config(const SessionConfig& config) {
205 DCHECK(CalledOnValidThread());
206 DCHECK(!config_is_set_);
207 config_ = config;
208 config_is_set_ = true;
211 ChannelFactory* JingleSession::GetTransportChannelFactory() {
212 DCHECK(CalledOnValidThread());
213 return this;
216 ChannelFactory* JingleSession::GetMultiplexedChannelFactory() {
217 DCHECK(CalledOnValidThread());
218 if (!channel_multiplexer_.get())
219 channel_multiplexer_.reset(new ChannelMultiplexer(this, kMuxChannelName));
220 return channel_multiplexer_.get();
223 void JingleSession::Close() {
224 DCHECK(CalledOnValidThread());
226 CloseInternal(OK);
229 void JingleSession::AddPendingRemoteCandidates(Transport* channel,
230 const std::string& name) {
231 std::list<JingleMessage::NamedCandidate>::iterator it =
232 pending_remote_candidates_.begin();
233 while(it != pending_remote_candidates_.end()) {
234 if (it->name == name) {
235 channel->AddRemoteCandidate(it->candidate);
236 it = pending_remote_candidates_.erase(it);
237 } else {
238 ++it;
243 void JingleSession::CreateStreamChannel(
244 const std::string& name,
245 const StreamChannelCallback& callback) {
246 DCHECK(!channels_[name]);
248 scoped_ptr<ChannelAuthenticator> channel_authenticator =
249 authenticator_->CreateChannelAuthenticator();
250 scoped_ptr<StreamTransport> channel =
251 session_manager_->transport_factory_->CreateStreamTransport();
252 channel->Initialize(name, this, channel_authenticator.Pass());
253 channel->Connect(callback);
254 AddPendingRemoteCandidates(channel.get(), name);
255 channels_[name] = channel.release();
258 void JingleSession::CreateDatagramChannel(
259 const std::string& name,
260 const DatagramChannelCallback& callback) {
261 DCHECK(!channels_[name]);
263 scoped_ptr<ChannelAuthenticator> channel_authenticator =
264 authenticator_->CreateChannelAuthenticator();
265 scoped_ptr<DatagramTransport> channel =
266 session_manager_->transport_factory_->CreateDatagramTransport();
267 channel->Initialize(name, this, channel_authenticator.Pass());
268 channel->Connect(callback);
269 AddPendingRemoteCandidates(channel.get(), name);
270 channels_[name] = channel.release();
273 void JingleSession::CancelChannelCreation(const std::string& name) {
274 ChannelsMap::iterator it = channels_.find(name);
275 if (it != channels_.end() && !it->second->is_connected()) {
276 delete it->second;
277 DCHECK(!channels_[name]);
281 void JingleSession::OnTransportCandidate(Transport* transport,
282 const cricket::Candidate& candidate) {
283 pending_candidates_.push_back(JingleMessage::NamedCandidate(
284 transport->name(), candidate));
286 if (!transport_infos_timer_.IsRunning()) {
287 // Delay sending the new candidates in case we get more candidates
288 // that we can send in one message.
289 transport_infos_timer_.Start(
290 FROM_HERE, base::TimeDelta::FromMilliseconds(kTransportInfoSendDelayMs),
291 this, &JingleSession::SendTransportInfo);
295 void JingleSession::OnTransportRouteChange(Transport* transport,
296 const TransportRoute& route) {
297 if (event_handler_)
298 event_handler_->OnSessionRouteChange(transport->name(), route);
301 void JingleSession::OnTransportReady(Transport* transport, bool ready) {
302 if (event_handler_)
303 event_handler_->OnSessionChannelReady(transport->name(), ready);
306 void JingleSession::OnTransportFailed(Transport* transport) {
307 CloseInternal(CHANNEL_CONNECTION_ERROR);
310 void JingleSession::OnTransportDeleted(Transport* transport) {
311 ChannelsMap::iterator it = channels_.find(transport->name());
312 DCHECK_EQ(it->second, transport);
313 channels_.erase(it);
316 void JingleSession::SendMessage(const JingleMessage& message) {
317 scoped_ptr<IqRequest> request = session_manager_->iq_sender()->SendIq(
318 message.ToXml(),
319 base::Bind(&JingleSession::OnMessageResponse,
320 base::Unretained(this), message.action));
321 if (request) {
322 request->SetTimeout(base::TimeDelta::FromSeconds(kDefaultMessageTimeout));
323 pending_requests_.insert(request.release());
324 } else {
325 LOG(ERROR) << "Failed to send a "
326 << JingleMessage::GetActionName(message.action) << " message";
330 void JingleSession::OnMessageResponse(
331 JingleMessage::ActionType request_type,
332 IqRequest* request,
333 const buzz::XmlElement* response) {
334 std::string type_str = JingleMessage::GetActionName(request_type);
336 // Delete the request from the list of pending requests.
337 pending_requests_.erase(request);
338 delete request;
340 // |response| will be NULL if the request timed out.
341 if (!response) {
342 LOG(ERROR) << type_str << " request timed out.";
343 CloseInternal(SIGNALING_TIMEOUT);
344 return;
345 } else {
346 const std::string& type =
347 response->Attr(buzz::QName(std::string(), "type"));
348 if (type != "result") {
349 LOG(ERROR) << "Received error in response to " << type_str
350 << " message: \"" << response->Str()
351 << "\". Terminating the session.";
353 switch (request_type) {
354 case JingleMessage::SESSION_INFO:
355 // session-info is used for the new authentication protocol,
356 // and wasn't previously supported.
357 CloseInternal(INCOMPATIBLE_PROTOCOL);
358 break;
360 default:
361 // TODO(sergeyu): There may be different reasons for error
362 // here. Parse the response stanza to find failure reason.
363 CloseInternal(PEER_IS_OFFLINE);
369 void JingleSession::SendTransportInfo() {
370 JingleMessage message(peer_jid_, JingleMessage::TRANSPORT_INFO, session_id_);
371 message.candidates.swap(pending_candidates_);
373 scoped_ptr<IqRequest> request = session_manager_->iq_sender()->SendIq(
374 message.ToXml(),
375 base::Bind(&JingleSession::OnTransportInfoResponse,
376 base::Unretained(this)));
377 if (request) {
378 request->SetTimeout(base::TimeDelta::FromSeconds(kTransportInfoTimeout));
379 transport_info_requests_.push_back(request.release());
380 } else {
381 LOG(ERROR) << "Failed to send a transport-info message";
385 void JingleSession::OnTransportInfoResponse(IqRequest* request,
386 const buzz::XmlElement* response) {
387 DCHECK(!transport_info_requests_.empty());
389 // Consider transport-info requests sent before this one lost and delete
390 // corresponding IqRequest objects.
391 while (transport_info_requests_.front() != request) {
392 delete transport_info_requests_.front();
393 transport_info_requests_.pop_front();
396 // Delete the |request| itself.
397 DCHECK_EQ(request, transport_info_requests_.front());
398 delete request;
399 transport_info_requests_.pop_front();
401 // Ignore transport-info timeouts.
402 if (!response) {
403 LOG(ERROR) << "transport-info request has timed out.";
404 return;
407 const std::string& type = response->Attr(buzz::QName(std::string(), "type"));
408 if (type != "result") {
409 LOG(ERROR) << "Received error in response to transport-info message: \""
410 << response->Str() << "\". Terminating the session.";
411 CloseInternal(PEER_IS_OFFLINE);
415 void JingleSession::OnIncomingMessage(const JingleMessage& message,
416 const ReplyCallback& reply_callback) {
417 DCHECK(CalledOnValidThread());
419 if (message.from != peer_jid_) {
420 // Ignore messages received from a different Jid.
421 reply_callback.Run(JingleMessageReply::INVALID_SID);
422 return;
425 switch (message.action) {
426 case JingleMessage::SESSION_ACCEPT:
427 OnAccept(message, reply_callback);
428 break;
430 case JingleMessage::SESSION_INFO:
431 OnSessionInfo(message, reply_callback);
432 break;
434 case JingleMessage::TRANSPORT_INFO:
435 reply_callback.Run(JingleMessageReply::NONE);
436 ProcessTransportInfo(message);
437 break;
439 case JingleMessage::SESSION_TERMINATE:
440 OnTerminate(message, reply_callback);
441 break;
443 default:
444 reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
448 void JingleSession::OnAccept(const JingleMessage& message,
449 const ReplyCallback& reply_callback) {
450 if (state_ != CONNECTING) {
451 reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
452 return;
455 reply_callback.Run(JingleMessageReply::NONE);
457 const buzz::XmlElement* auth_message =
458 message.description->authenticator_message();
459 if (!auth_message) {
460 DLOG(WARNING) << "Received session-accept without authentication message ";
461 CloseInternal(INCOMPATIBLE_PROTOCOL);
462 return;
465 if (!InitializeConfigFromDescription(message.description.get())) {
466 CloseInternal(INCOMPATIBLE_PROTOCOL);
467 return;
470 // In case there is transport information in the accept message.
471 ProcessTransportInfo(message);
473 SetState(CONNECTED);
475 DCHECK(authenticator_->state() == Authenticator::WAITING_MESSAGE);
476 authenticator_->ProcessMessage(auth_message, base::Bind(
477 &JingleSession::ProcessAuthenticationStep,base::Unretained(this)));
480 void JingleSession::OnSessionInfo(const JingleMessage& message,
481 const ReplyCallback& reply_callback) {
482 if (!message.info.get() ||
483 !Authenticator::IsAuthenticatorMessage(message.info.get())) {
484 reply_callback.Run(JingleMessageReply::UNSUPPORTED_INFO);
485 return;
488 if (state_ != CONNECTED ||
489 authenticator_->state() != Authenticator::WAITING_MESSAGE) {
490 LOG(WARNING) << "Received unexpected authenticator message "
491 << message.info->Str();
492 reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
493 CloseInternal(INCOMPATIBLE_PROTOCOL);
494 return;
497 reply_callback.Run(JingleMessageReply::NONE);
499 authenticator_->ProcessMessage(message.info.get(), base::Bind(
500 &JingleSession::ProcessAuthenticationStep, base::Unretained(this)));
503 void JingleSession::ProcessTransportInfo(const JingleMessage& message) {
504 for (std::list<JingleMessage::NamedCandidate>::const_iterator it =
505 message.candidates.begin();
506 it != message.candidates.end(); ++it) {
507 ChannelsMap::iterator channel = channels_.find(it->name);
508 if (channel != channels_.end()) {
509 channel->second->AddRemoteCandidate(it->candidate);
510 } else {
511 // Transport info was received before the channel was created.
512 // This could happen due to messages being reordered on the wire.
513 pending_remote_candidates_.push_back(*it);
518 void JingleSession::OnTerminate(const JingleMessage& message,
519 const ReplyCallback& reply_callback) {
520 if (state_ != CONNECTING && state_ != ACCEPTING && state_ != CONNECTED &&
521 state_ != AUTHENTICATED) {
522 LOG(WARNING) << "Received unexpected session-terminate message.";
523 reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
524 return;
527 reply_callback.Run(JingleMessageReply::NONE);
529 switch (message.reason) {
530 case JingleMessage::SUCCESS:
531 if (state_ == CONNECTING) {
532 error_ = SESSION_REJECTED;
533 } else {
534 error_ = OK;
536 break;
537 case JingleMessage::DECLINE:
538 error_ = AUTHENTICATION_FAILED;
539 break;
540 case JingleMessage::CANCEL:
541 error_ = HOST_OVERLOAD;
542 break;
543 case JingleMessage::GENERAL_ERROR:
544 error_ = CHANNEL_CONNECTION_ERROR;
545 break;
546 case JingleMessage::INCOMPATIBLE_PARAMETERS:
547 error_ = INCOMPATIBLE_PROTOCOL;
548 break;
549 default:
550 error_ = UNKNOWN_ERROR;
553 if (error_ != OK) {
554 SetState(FAILED);
555 } else {
556 SetState(CLOSED);
560 bool JingleSession::InitializeConfigFromDescription(
561 const ContentDescription* description) {
562 DCHECK(description);
564 if (!description->config()->GetFinalConfig(&config_)) {
565 LOG(ERROR) << "session-accept does not specify configuration";
566 return false;
568 if (!candidate_config()->IsSupported(config_)) {
569 LOG(ERROR) << "session-accept specifies an invalid configuration";
570 return false;
573 return true;
576 void JingleSession::ProcessAuthenticationStep() {
577 DCHECK(CalledOnValidThread());
578 DCHECK_NE(authenticator_->state(), Authenticator::PROCESSING_MESSAGE);
580 if (state_ != CONNECTED) {
581 DCHECK(state_ == FAILED || state_ == CLOSED);
582 // The remote host closed the connection while the authentication was being
583 // processed asynchronously, nothing to do.
584 return;
587 if (authenticator_->state() == Authenticator::MESSAGE_READY) {
588 JingleMessage message(peer_jid_, JingleMessage::SESSION_INFO, session_id_);
589 message.info = authenticator_->GetNextMessage();
590 DCHECK(message.info.get());
591 SendMessage(message);
593 DCHECK_NE(authenticator_->state(), Authenticator::MESSAGE_READY);
595 if (authenticator_->state() == Authenticator::ACCEPTED) {
596 SetState(AUTHENTICATED);
597 } else if (authenticator_->state() == Authenticator::REJECTED) {
598 CloseInternal(AuthRejectionReasonToErrorCode(
599 authenticator_->rejection_reason()));
603 void JingleSession::CloseInternal(ErrorCode error) {
604 DCHECK(CalledOnValidThread());
606 if (state_ == CONNECTING || state_ == ACCEPTING || state_ == CONNECTED ||
607 state_ == AUTHENTICATED) {
608 // Send session-terminate message with the appropriate error code.
609 JingleMessage::Reason reason;
610 switch (error) {
611 case OK:
612 reason = JingleMessage::SUCCESS;
613 break;
614 case SESSION_REJECTED:
615 case AUTHENTICATION_FAILED:
616 reason = JingleMessage::DECLINE;
617 break;
618 case INCOMPATIBLE_PROTOCOL:
619 reason = JingleMessage::INCOMPATIBLE_PARAMETERS;
620 break;
621 case HOST_OVERLOAD:
622 reason = JingleMessage::CANCEL;
623 break;
624 default:
625 reason = JingleMessage::GENERAL_ERROR;
628 JingleMessage message(peer_jid_, JingleMessage::SESSION_TERMINATE,
629 session_id_);
630 message.reason = reason;
631 SendMessage(message);
634 error_ = error;
636 if (state_ != FAILED && state_ != CLOSED) {
637 if (error != OK) {
638 SetState(FAILED);
639 } else {
640 SetState(CLOSED);
645 void JingleSession::SetState(State new_state) {
646 DCHECK(CalledOnValidThread());
648 if (new_state != state_) {
649 DCHECK_NE(state_, CLOSED);
650 DCHECK_NE(state_, FAILED);
652 state_ = new_state;
653 if (event_handler_)
654 event_handler_->OnSessionStateChange(new_state);
658 } // namespace protocol
659 } // namespace remoting