password_manager::metrics_util::LogUMAHistogramBoolean should upload
[chromium-blink-merge.git] / remoting / protocol / jingle_session.cc
blobedc48eea6b6649d4de693cab122266516821dc0b
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 {
35 // Delay after candidate creation before sending transport-info
36 // message. This is neccessary to be able to pack multiple candidates
37 // into one transport-info messages. The value needs to be greater
38 // than zero because ports are opened asynchronously in the browser
39 // process.
40 const int kTransportInfoSendDelayMs = 2;
42 // How long we should wait for a response from the other end. This value is used
43 // for all requests except |transport-info|.
44 const int kDefaultMessageTimeout = 10;
46 // During a reconnection, it usually takes longer for the peer to respond due to
47 // pending messages in the channel from the previous session. From experiment,
48 // it can take up to 20s for the session to reconnect. To make it safe, setting
49 // the timeout to 30s.
50 const int kSessionInitiateAndAcceptTimeout = kDefaultMessageTimeout * 3;
52 // Timeout for the transport-info messages.
53 const int kTransportInfoTimeout = 10 * 60;
55 // Name of the multiplexed channel.
56 const char kMuxChannelName[] = "mux";
58 ErrorCode AuthRejectionReasonToErrorCode(
59 Authenticator::RejectionReason reason) {
60 switch (reason) {
61 case Authenticator::INVALID_CREDENTIALS:
62 return AUTHENTICATION_FAILED;
63 case Authenticator::PROTOCOL_ERROR:
64 return INCOMPATIBLE_PROTOCOL;
66 NOTREACHED();
67 return UNKNOWN_ERROR;
70 } // namespace
72 JingleSession::JingleSession(JingleSessionManager* session_manager)
73 : session_manager_(session_manager),
74 event_handler_(nullptr),
75 state_(INITIALIZING),
76 error_(OK),
77 config_is_set_(false),
78 weak_factory_(this) {
81 JingleSession::~JingleSession() {
82 channel_multiplexer_.reset();
83 STLDeleteContainerPointers(pending_requests_.begin(),
84 pending_requests_.end());
85 STLDeleteContainerPointers(transport_info_requests_.begin(),
86 transport_info_requests_.end());
88 DCHECK(channels_.empty());
90 session_manager_->SessionDestroyed(this);
93 void JingleSession::SetEventHandler(Session::EventHandler* event_handler) {
94 DCHECK(CalledOnValidThread());
95 DCHECK(event_handler);
96 event_handler_ = event_handler;
99 ErrorCode JingleSession::error() {
100 DCHECK(CalledOnValidThread());
101 return error_;
104 void JingleSession::StartConnection(
105 const std::string& peer_jid,
106 scoped_ptr<Authenticator> authenticator,
107 scoped_ptr<CandidateSessionConfig> config) {
108 DCHECK(CalledOnValidThread());
109 DCHECK(authenticator.get());
110 DCHECK_EQ(authenticator->state(), Authenticator::MESSAGE_READY);
112 peer_jid_ = peer_jid;
113 authenticator_ = authenticator.Pass();
114 candidate_config_ = config.Pass();
116 // Generate random session ID. There are usually not more than 1
117 // concurrent session per host, so a random 64-bit integer provides
118 // enough entropy. In the worst case connection will fail when two
119 // clients generate the same session ID concurrently.
120 session_id_ = base::Int64ToString(base::RandGenerator(kint64max));
122 // Send session-initiate message.
123 JingleMessage message(peer_jid_, JingleMessage::SESSION_INITIATE,
124 session_id_);
125 message.initiator = session_manager_->signal_strategy_->GetLocalJid();
126 message.description.reset(
127 new ContentDescription(candidate_config_->Clone(),
128 authenticator_->GetNextMessage()));
129 SendMessage(message);
131 SetState(CONNECTING);
134 void JingleSession::InitializeIncomingConnection(
135 const JingleMessage& initiate_message,
136 scoped_ptr<Authenticator> authenticator) {
137 DCHECK(CalledOnValidThread());
138 DCHECK(initiate_message.description.get());
139 DCHECK(authenticator.get());
140 DCHECK_EQ(authenticator->state(), Authenticator::WAITING_MESSAGE);
142 peer_jid_ = initiate_message.from;
143 authenticator_ = authenticator.Pass();
144 session_id_ = initiate_message.sid;
145 candidate_config_ = initiate_message.description->config()->Clone();
147 SetState(ACCEPTING);
150 void JingleSession::AcceptIncomingConnection(
151 const JingleMessage& initiate_message) {
152 DCHECK(config_is_set_);
154 // Process the first authentication message.
155 const buzz::XmlElement* first_auth_message =
156 initiate_message.description->authenticator_message();
158 if (!first_auth_message) {
159 CloseInternal(INCOMPATIBLE_PROTOCOL);
160 return;
163 DCHECK_EQ(authenticator_->state(), Authenticator::WAITING_MESSAGE);
164 // |authenticator_| is owned, so Unretained() is safe here.
165 authenticator_->ProcessMessage(first_auth_message, base::Bind(
166 &JingleSession::ContinueAcceptIncomingConnection,
167 base::Unretained(this)));
170 void JingleSession::ContinueAcceptIncomingConnection() {
171 DCHECK_NE(authenticator_->state(), Authenticator::PROCESSING_MESSAGE);
172 if (authenticator_->state() == Authenticator::REJECTED) {
173 CloseInternal(AuthRejectionReasonToErrorCode(
174 authenticator_->rejection_reason()));
175 return;
178 // Send the session-accept message.
179 JingleMessage message(peer_jid_, JingleMessage::SESSION_ACCEPT,
180 session_id_);
182 scoped_ptr<buzz::XmlElement> auth_message;
183 if (authenticator_->state() == Authenticator::MESSAGE_READY)
184 auth_message = authenticator_->GetNextMessage();
186 message.description.reset(
187 new ContentDescription(CandidateSessionConfig::CreateFrom(config_),
188 auth_message.Pass()));
189 SendMessage(message);
191 // Update state.
192 SetState(CONNECTED);
194 if (authenticator_->state() == Authenticator::ACCEPTED) {
195 OnAuthenticated();
196 } else {
197 DCHECK_EQ(authenticator_->state(), Authenticator::WAITING_MESSAGE);
198 if (authenticator_->started()) {
199 SetState(AUTHENTICATING);
204 const std::string& JingleSession::jid() {
205 DCHECK(CalledOnValidThread());
206 return peer_jid_;
209 const CandidateSessionConfig* JingleSession::candidate_config() {
210 DCHECK(CalledOnValidThread());
211 return candidate_config_.get();
214 const SessionConfig& JingleSession::config() {
215 DCHECK(CalledOnValidThread());
216 return config_;
219 void JingleSession::set_config(const SessionConfig& config) {
220 DCHECK(CalledOnValidThread());
221 DCHECK(!config_is_set_);
222 config_ = config;
223 config_is_set_ = true;
226 StreamChannelFactory* JingleSession::GetTransportChannelFactory() {
227 DCHECK(CalledOnValidThread());
228 return secure_channel_factory_.get();
231 StreamChannelFactory* JingleSession::GetMultiplexedChannelFactory() {
232 DCHECK(CalledOnValidThread());
233 if (!channel_multiplexer_.get()) {
234 channel_multiplexer_.reset(
235 new ChannelMultiplexer(GetTransportChannelFactory(), kMuxChannelName));
237 return channel_multiplexer_.get();
240 void JingleSession::Close() {
241 DCHECK(CalledOnValidThread());
243 CloseInternal(OK);
246 void JingleSession::AddPendingRemoteCandidates(Transport* channel,
247 const std::string& name) {
248 std::list<JingleMessage::NamedCandidate>::iterator it =
249 pending_remote_candidates_.begin();
250 while(it != pending_remote_candidates_.end()) {
251 if (it->name == name) {
252 channel->AddRemoteCandidate(it->candidate);
253 it = pending_remote_candidates_.erase(it);
254 } else {
255 ++it;
260 void JingleSession::CreateChannel(const std::string& name,
261 const ChannelCreatedCallback& callback) {
262 DCHECK(!channels_[name]);
264 scoped_ptr<Transport> channel =
265 session_manager_->transport_factory_->CreateTransport();
266 channel->Connect(name, this, callback);
267 AddPendingRemoteCandidates(channel.get(), name);
268 channels_[name] = channel.release();
271 void JingleSession::CancelChannelCreation(const std::string& name) {
272 ChannelsMap::iterator it = channels_.find(name);
273 if (it != channels_.end()) {
274 DCHECK(!it->second->is_connected());
275 delete it->second;
276 DCHECK(channels_.find(name) == channels_.end());
280 void JingleSession::OnTransportCandidate(Transport* transport,
281 const cricket::Candidate& candidate) {
282 pending_candidates_.push_back(JingleMessage::NamedCandidate(
283 transport->name(), candidate));
285 if (!transport_infos_timer_.IsRunning()) {
286 // Delay sending the new candidates in case we get more candidates
287 // that we can send in one message.
288 transport_infos_timer_.Start(
289 FROM_HERE, base::TimeDelta::FromMilliseconds(kTransportInfoSendDelayMs),
290 this, &JingleSession::SendTransportInfo);
294 void JingleSession::OnTransportRouteChange(Transport* transport,
295 const TransportRoute& route) {
296 if (event_handler_)
297 event_handler_->OnSessionRouteChange(transport->name(), route);
300 void JingleSession::OnTransportFailed(Transport* transport) {
301 CloseInternal(CHANNEL_CONNECTION_ERROR);
304 void JingleSession::OnTransportDeleted(Transport* transport) {
305 ChannelsMap::iterator it = channels_.find(transport->name());
306 DCHECK_EQ(it->second, transport);
307 channels_.erase(it);
310 void JingleSession::SendMessage(const JingleMessage& message) {
311 scoped_ptr<IqRequest> request = session_manager_->iq_sender()->SendIq(
312 message.ToXml(),
313 base::Bind(&JingleSession::OnMessageResponse,
314 base::Unretained(this),
315 message.action));
317 int timeout = kDefaultMessageTimeout;
318 if (message.action == JingleMessage::SESSION_INITIATE ||
319 message.action == JingleMessage::SESSION_ACCEPT) {
320 timeout = kSessionInitiateAndAcceptTimeout;
322 if (request) {
323 request->SetTimeout(base::TimeDelta::FromSeconds(timeout));
324 pending_requests_.insert(request.release());
325 } else {
326 LOG(ERROR) << "Failed to send a "
327 << JingleMessage::GetActionName(message.action) << " message";
331 void JingleSession::OnMessageResponse(
332 JingleMessage::ActionType request_type,
333 IqRequest* request,
334 const buzz::XmlElement* response) {
335 // Delete the request from the list of pending requests.
336 pending_requests_.erase(request);
337 delete request;
339 // Ignore all responses after session was closed.
340 if (state_ == CLOSED || state_ == FAILED)
341 return;
343 std::string type_str = JingleMessage::GetActionName(request_type);
345 // |response| will be nullptr if the request timed out.
346 if (!response) {
347 LOG(ERROR) << type_str << " request timed out.";
348 CloseInternal(SIGNALING_TIMEOUT);
349 return;
350 } else {
351 const std::string& type =
352 response->Attr(buzz::QName(std::string(), "type"));
353 if (type != "result") {
354 LOG(ERROR) << "Received error in response to " << type_str
355 << " message: \"" << response->Str()
356 << "\". Terminating the session.";
358 // TODO(sergeyu): There may be different reasons for error
359 // here. Parse the response stanza to find failure reason.
360 CloseInternal(PEER_IS_OFFLINE);
365 void JingleSession::SendTransportInfo() {
366 JingleMessage message(peer_jid_, JingleMessage::TRANSPORT_INFO, session_id_);
367 message.candidates.swap(pending_candidates_);
369 scoped_ptr<IqRequest> request = session_manager_->iq_sender()->SendIq(
370 message.ToXml(),
371 base::Bind(&JingleSession::OnTransportInfoResponse,
372 base::Unretained(this)));
373 if (request) {
374 request->SetTimeout(base::TimeDelta::FromSeconds(kTransportInfoTimeout));
375 transport_info_requests_.push_back(request.release());
376 } else {
377 LOG(ERROR) << "Failed to send a transport-info message";
381 void JingleSession::OnTransportInfoResponse(IqRequest* request,
382 const buzz::XmlElement* response) {
383 DCHECK(!transport_info_requests_.empty());
385 // Consider transport-info requests sent before this one lost and delete
386 // corresponding IqRequest objects.
387 while (transport_info_requests_.front() != request) {
388 delete transport_info_requests_.front();
389 transport_info_requests_.pop_front();
392 // Delete the |request| itself.
393 DCHECK_EQ(request, transport_info_requests_.front());
394 delete request;
395 transport_info_requests_.pop_front();
397 // Ignore transport-info timeouts.
398 if (!response) {
399 LOG(ERROR) << "transport-info request has timed out.";
400 return;
403 const std::string& type = response->Attr(buzz::QName(std::string(), "type"));
404 if (type != "result") {
405 LOG(ERROR) << "Received error in response to transport-info message: \""
406 << response->Str() << "\". Terminating the session.";
407 CloseInternal(PEER_IS_OFFLINE);
411 void JingleSession::OnIncomingMessage(const JingleMessage& message,
412 const ReplyCallback& reply_callback) {
413 DCHECK(CalledOnValidThread());
415 if (message.from != peer_jid_) {
416 // Ignore messages received from a different Jid.
417 reply_callback.Run(JingleMessageReply::INVALID_SID);
418 return;
421 switch (message.action) {
422 case JingleMessage::SESSION_ACCEPT:
423 OnAccept(message, reply_callback);
424 break;
426 case JingleMessage::SESSION_INFO:
427 OnSessionInfo(message, reply_callback);
428 break;
430 case JingleMessage::TRANSPORT_INFO:
431 reply_callback.Run(JingleMessageReply::NONE);
432 ProcessTransportInfo(message);
433 break;
435 case JingleMessage::SESSION_TERMINATE:
436 OnTerminate(message, reply_callback);
437 break;
439 default:
440 reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
444 void JingleSession::OnAccept(const JingleMessage& message,
445 const ReplyCallback& reply_callback) {
446 if (state_ != CONNECTING) {
447 reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
448 return;
451 reply_callback.Run(JingleMessageReply::NONE);
453 const buzz::XmlElement* auth_message =
454 message.description->authenticator_message();
455 if (!auth_message) {
456 DLOG(WARNING) << "Received session-accept without authentication message ";
457 CloseInternal(INCOMPATIBLE_PROTOCOL);
458 return;
461 if (!InitializeConfigFromDescription(message.description.get())) {
462 CloseInternal(INCOMPATIBLE_PROTOCOL);
463 return;
466 // In case there is transport information in the accept message.
467 ProcessTransportInfo(message);
469 SetState(CONNECTED);
471 DCHECK(authenticator_->state() == Authenticator::WAITING_MESSAGE);
472 authenticator_->ProcessMessage(auth_message, base::Bind(
473 &JingleSession::ProcessAuthenticationStep,base::Unretained(this)));
476 void JingleSession::OnSessionInfo(const JingleMessage& message,
477 const ReplyCallback& reply_callback) {
478 if (!message.info.get() ||
479 !Authenticator::IsAuthenticatorMessage(message.info.get())) {
480 reply_callback.Run(JingleMessageReply::UNSUPPORTED_INFO);
481 return;
484 if ((state_ != CONNECTED && state_ != AUTHENTICATING) ||
485 authenticator_->state() != Authenticator::WAITING_MESSAGE) {
486 LOG(WARNING) << "Received unexpected authenticator message "
487 << message.info->Str();
488 reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
489 CloseInternal(INCOMPATIBLE_PROTOCOL);
490 return;
493 reply_callback.Run(JingleMessageReply::NONE);
495 authenticator_->ProcessMessage(message.info.get(), base::Bind(
496 &JingleSession::ProcessAuthenticationStep, base::Unretained(this)));
499 void JingleSession::ProcessTransportInfo(const JingleMessage& message) {
500 for (std::list<JingleMessage::NamedCandidate>::const_iterator it =
501 message.candidates.begin();
502 it != message.candidates.end(); ++it) {
503 ChannelsMap::iterator channel = channels_.find(it->name);
504 if (channel != channels_.end()) {
505 channel->second->AddRemoteCandidate(it->candidate);
506 } else {
507 // Transport info was received before the channel was created.
508 // This could happen due to messages being reordered on the wire.
509 pending_remote_candidates_.push_back(*it);
514 void JingleSession::OnTerminate(const JingleMessage& message,
515 const ReplyCallback& reply_callback) {
516 if (!is_session_active()) {
517 LOG(WARNING) << "Received unexpected session-terminate message.";
518 reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
519 return;
522 reply_callback.Run(JingleMessageReply::NONE);
524 switch (message.reason) {
525 case JingleMessage::SUCCESS:
526 if (state_ == CONNECTING) {
527 error_ = SESSION_REJECTED;
528 } else {
529 error_ = OK;
531 break;
532 case JingleMessage::DECLINE:
533 error_ = AUTHENTICATION_FAILED;
534 break;
535 case JingleMessage::CANCEL:
536 error_ = HOST_OVERLOAD;
537 break;
538 case JingleMessage::GENERAL_ERROR:
539 error_ = CHANNEL_CONNECTION_ERROR;
540 break;
541 case JingleMessage::INCOMPATIBLE_PARAMETERS:
542 error_ = INCOMPATIBLE_PROTOCOL;
543 break;
544 default:
545 error_ = UNKNOWN_ERROR;
548 if (error_ != OK) {
549 SetState(FAILED);
550 } else {
551 SetState(CLOSED);
555 bool JingleSession::InitializeConfigFromDescription(
556 const ContentDescription* description) {
557 DCHECK(description);
559 if (!description->config()->GetFinalConfig(&config_)) {
560 LOG(ERROR) << "session-accept does not specify configuration";
561 return false;
563 if (!candidate_config()->IsSupported(config_)) {
564 LOG(ERROR) << "session-accept specifies an invalid configuration";
565 return false;
568 return true;
571 void JingleSession::ProcessAuthenticationStep() {
572 DCHECK(CalledOnValidThread());
573 DCHECK_NE(authenticator_->state(), Authenticator::PROCESSING_MESSAGE);
575 if (state_ != CONNECTED && state_ != AUTHENTICATING) {
576 DCHECK(state_ == FAILED || state_ == CLOSED);
577 // The remote host closed the connection while the authentication was being
578 // processed asynchronously, nothing to do.
579 return;
582 if (authenticator_->state() == Authenticator::MESSAGE_READY) {
583 JingleMessage message(peer_jid_, JingleMessage::SESSION_INFO, session_id_);
584 message.info = authenticator_->GetNextMessage();
585 DCHECK(message.info.get());
586 SendMessage(message);
588 DCHECK_NE(authenticator_->state(), Authenticator::MESSAGE_READY);
590 // The current JingleSession object can be destroyed by event_handler of
591 // SetState(AUTHENTICATING) and cause subsequent dereferencing of the this
592 // pointer to crash. To protect against it, we run ContinueAuthenticationStep
593 // asychronously using a weak pointer.
594 base::ThreadTaskRunnerHandle::Get()->PostTask(
595 FROM_HERE,
596 base::Bind(&JingleSession::ContinueAuthenticationStep,
597 weak_factory_.GetWeakPtr()));
599 if (authenticator_->started()) {
600 SetState(AUTHENTICATING);
604 void JingleSession::ContinueAuthenticationStep() {
605 if (authenticator_->state() == Authenticator::ACCEPTED) {
606 OnAuthenticated();
607 } else if (authenticator_->state() == Authenticator::REJECTED) {
608 CloseInternal(AuthRejectionReasonToErrorCode(
609 authenticator_->rejection_reason()));
613 void JingleSession::OnAuthenticated() {
614 pseudotcp_channel_factory_.reset(new PseudoTcpChannelFactory(this));
615 secure_channel_factory_.reset(
616 new SecureChannelFactory(pseudotcp_channel_factory_.get(),
617 authenticator_.get()));
619 SetState(AUTHENTICATED);
622 void JingleSession::CloseInternal(ErrorCode error) {
623 DCHECK(CalledOnValidThread());
625 if (is_session_active()) {
626 // Send session-terminate message with the appropriate error code.
627 JingleMessage::Reason reason;
628 switch (error) {
629 case OK:
630 reason = JingleMessage::SUCCESS;
631 break;
632 case SESSION_REJECTED:
633 case AUTHENTICATION_FAILED:
634 reason = JingleMessage::DECLINE;
635 break;
636 case INCOMPATIBLE_PROTOCOL:
637 reason = JingleMessage::INCOMPATIBLE_PARAMETERS;
638 break;
639 case HOST_OVERLOAD:
640 reason = JingleMessage::CANCEL;
641 break;
642 default:
643 reason = JingleMessage::GENERAL_ERROR;
646 JingleMessage message(peer_jid_, JingleMessage::SESSION_TERMINATE,
647 session_id_);
648 message.reason = reason;
649 SendMessage(message);
652 error_ = error;
654 if (state_ != FAILED && state_ != CLOSED) {
655 if (error != OK) {
656 SetState(FAILED);
657 } else {
658 SetState(CLOSED);
663 void JingleSession::SetState(State new_state) {
664 DCHECK(CalledOnValidThread());
666 if (new_state != state_) {
667 DCHECK_NE(state_, CLOSED);
668 DCHECK_NE(state_, FAILED);
670 state_ = new_state;
671 if (event_handler_)
672 event_handler_->OnSessionStateChange(new_state);
676 bool JingleSession::is_session_active() {
677 return state_ == CONNECTING || state_ == ACCEPTING || state_ == CONNECTED ||
678 state_ == AUTHENTICATING || state_ == AUTHENTICATED;
681 } // namespace protocol
682 } // namespace remoting