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"
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
;
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
) {
60 case Authenticator::INVALID_CREDENTIALS
:
61 return AUTHENTICATION_FAILED
;
62 case Authenticator::PROTOCOL_ERROR
:
63 return INCOMPATIBLE_PROTOCOL
;
71 JingleSession::JingleSession(JingleSessionManager
* session_manager
)
72 : session_manager_(session_manager
),
73 event_handler_(nullptr),
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());
102 void JingleSession::StartConnection(
103 const std::string
& peer_jid
,
104 scoped_ptr
<Authenticator
> authenticator
,
105 scoped_ptr
<CandidateSessionConfig
> config
) {
106 DCHECK(CalledOnValidThread());
107 DCHECK(authenticator
.get());
108 DCHECK_EQ(authenticator
->state(), Authenticator::MESSAGE_READY
);
110 peer_jid_
= peer_jid
;
111 authenticator_
= authenticator
.Pass();
112 candidate_config_
= config
.Pass();
114 // Generate random session ID. There are usually not more than 1
115 // concurrent session per host, so a random 64-bit integer provides
116 // enough entropy. In the worst case connection will fail when two
117 // clients generate the same session ID concurrently.
118 session_id_
= base::Int64ToString(base::RandGenerator(kint64max
));
120 // Send session-initiate message.
121 JingleMessage
message(peer_jid_
, JingleMessage::SESSION_INITIATE
,
123 message
.initiator
= session_manager_
->signal_strategy_
->GetLocalJid();
124 message
.description
.reset(
125 new ContentDescription(candidate_config_
->Clone(),
126 authenticator_
->GetNextMessage()));
127 SendMessage(message
);
129 SetState(CONNECTING
);
132 void JingleSession::InitializeIncomingConnection(
133 const JingleMessage
& initiate_message
,
134 scoped_ptr
<Authenticator
> authenticator
) {
135 DCHECK(CalledOnValidThread());
136 DCHECK(initiate_message
.description
.get());
137 DCHECK(authenticator
.get());
138 DCHECK_EQ(authenticator
->state(), Authenticator::WAITING_MESSAGE
);
140 peer_jid_
= initiate_message
.from
;
141 authenticator_
= authenticator
.Pass();
142 session_id_
= initiate_message
.sid
;
143 candidate_config_
= initiate_message
.description
->config()->Clone();
148 void JingleSession::AcceptIncomingConnection(
149 const JingleMessage
& initiate_message
) {
152 // Process the first authentication message.
153 const buzz::XmlElement
* first_auth_message
=
154 initiate_message
.description
->authenticator_message();
156 if (!first_auth_message
) {
157 CloseInternal(INCOMPATIBLE_PROTOCOL
);
161 DCHECK_EQ(authenticator_
->state(), Authenticator::WAITING_MESSAGE
);
162 // |authenticator_| is owned, so Unretained() is safe here.
163 authenticator_
->ProcessMessage(first_auth_message
, base::Bind(
164 &JingleSession::ContinueAcceptIncomingConnection
,
165 base::Unretained(this)));
168 void JingleSession::ContinueAcceptIncomingConnection() {
169 DCHECK_NE(authenticator_
->state(), Authenticator::PROCESSING_MESSAGE
);
170 if (authenticator_
->state() == Authenticator::REJECTED
) {
171 CloseInternal(AuthRejectionReasonToErrorCode(
172 authenticator_
->rejection_reason()));
176 // Send the session-accept message.
177 JingleMessage
message(peer_jid_
, JingleMessage::SESSION_ACCEPT
,
180 scoped_ptr
<buzz::XmlElement
> auth_message
;
181 if (authenticator_
->state() == Authenticator::MESSAGE_READY
)
182 auth_message
= authenticator_
->GetNextMessage();
184 message
.description
.reset(
185 new ContentDescription(CandidateSessionConfig::CreateFrom(*config_
),
186 auth_message
.Pass()));
187 SendMessage(message
);
192 if (authenticator_
->state() == Authenticator::ACCEPTED
) {
195 DCHECK_EQ(authenticator_
->state(), Authenticator::WAITING_MESSAGE
);
196 if (authenticator_
->started()) {
197 SetState(AUTHENTICATING
);
202 const std::string
& JingleSession::jid() {
203 DCHECK(CalledOnValidThread());
207 const CandidateSessionConfig
* JingleSession::candidate_config() {
208 DCHECK(CalledOnValidThread());
209 return candidate_config_
.get();
212 const SessionConfig
& JingleSession::config() {
213 DCHECK(CalledOnValidThread());
217 void JingleSession::set_config(scoped_ptr
<SessionConfig
> config
) {
218 DCHECK(CalledOnValidThread());
220 config_
= config
.Pass();
223 StreamChannelFactory
* JingleSession::GetTransportChannelFactory() {
224 DCHECK(CalledOnValidThread());
225 return secure_channel_factory_
.get();
228 StreamChannelFactory
* JingleSession::GetMultiplexedChannelFactory() {
229 DCHECK(CalledOnValidThread());
230 if (!channel_multiplexer_
.get()) {
231 channel_multiplexer_
.reset(
232 new ChannelMultiplexer(GetTransportChannelFactory(), kMuxChannelName
));
234 return channel_multiplexer_
.get();
237 void JingleSession::Close() {
238 DCHECK(CalledOnValidThread());
243 void JingleSession::AddPendingRemoteTransportInfo(Transport
* channel
) {
244 std::list
<JingleMessage::IceCredentials
>::iterator credentials
=
245 pending_remote_ice_credentials_
.begin();
246 while (credentials
!= pending_remote_ice_credentials_
.end()) {
247 if (credentials
->channel
== channel
->name()) {
248 channel
->SetRemoteCredentials(credentials
->ufrag
, credentials
->password
);
249 credentials
= pending_remote_ice_credentials_
.erase(credentials
);
255 std::list
<JingleMessage::NamedCandidate
>::iterator candidate
=
256 pending_remote_candidates_
.begin();
257 while (candidate
!= pending_remote_candidates_
.end()) {
258 if (candidate
->name
== channel
->name()) {
259 channel
->AddRemoteCandidate(candidate
->candidate
);
260 candidate
= pending_remote_candidates_
.erase(candidate
);
267 void JingleSession::CreateChannel(const std::string
& name
,
268 const ChannelCreatedCallback
& callback
) {
269 DCHECK(!channels_
[name
]);
271 scoped_ptr
<Transport
> channel
=
272 session_manager_
->transport_factory_
->CreateTransport();
273 channel
->SetUseStandardIce(config_
->standard_ice());
274 channel
->Connect(name
, this, callback
);
275 AddPendingRemoteTransportInfo(channel
.get());
276 channels_
[name
] = channel
.release();
279 void JingleSession::CancelChannelCreation(const std::string
& name
) {
280 ChannelsMap::iterator it
= channels_
.find(name
);
281 if (it
!= channels_
.end()) {
282 DCHECK(!it
->second
->is_connected());
284 DCHECK(channels_
.find(name
) == channels_
.end());
288 void JingleSession::OnTransportIceCredentials(Transport
* transport
,
289 const std::string
& ufrag
,
290 const std::string
& password
) {
291 EnsurePendingTransportInfoMessage();
292 pending_transport_info_message_
->ice_credentials
.push_back(
293 JingleMessage::IceCredentials(transport
->name(), ufrag
, password
));
296 void JingleSession::OnTransportCandidate(Transport
* transport
,
297 const cricket::Candidate
& candidate
) {
298 EnsurePendingTransportInfoMessage();
299 pending_transport_info_message_
->candidates
.push_back(
300 JingleMessage::NamedCandidate(transport
->name(), candidate
));
303 void JingleSession::OnTransportRouteChange(Transport
* transport
,
304 const TransportRoute
& route
) {
306 event_handler_
->OnSessionRouteChange(transport
->name(), route
);
309 void JingleSession::OnTransportFailed(Transport
* transport
) {
310 CloseInternal(CHANNEL_CONNECTION_ERROR
);
313 void JingleSession::OnTransportDeleted(Transport
* transport
) {
314 ChannelsMap::iterator it
= channels_
.find(transport
->name());
315 DCHECK_EQ(it
->second
, transport
);
319 void JingleSession::SendMessage(const JingleMessage
& message
) {
320 scoped_ptr
<IqRequest
> request
= session_manager_
->iq_sender()->SendIq(
322 base::Bind(&JingleSession::OnMessageResponse
,
323 base::Unretained(this),
326 int timeout
= kDefaultMessageTimeout
;
327 if (message
.action
== JingleMessage::SESSION_INITIATE
||
328 message
.action
== JingleMessage::SESSION_ACCEPT
) {
329 timeout
= kSessionInitiateAndAcceptTimeout
;
332 request
->SetTimeout(base::TimeDelta::FromSeconds(timeout
));
333 pending_requests_
.insert(request
.release());
335 LOG(ERROR
) << "Failed to send a "
336 << JingleMessage::GetActionName(message
.action
) << " message";
340 void JingleSession::OnMessageResponse(
341 JingleMessage::ActionType request_type
,
343 const buzz::XmlElement
* response
) {
344 // Delete the request from the list of pending requests.
345 pending_requests_
.erase(request
);
348 // Ignore all responses after session was closed.
349 if (state_
== CLOSED
|| state_
== FAILED
)
352 std::string type_str
= JingleMessage::GetActionName(request_type
);
354 // |response| will be nullptr if the request timed out.
356 LOG(ERROR
) << type_str
<< " request timed out.";
357 CloseInternal(SIGNALING_TIMEOUT
);
360 const std::string
& type
=
361 response
->Attr(buzz::QName(std::string(), "type"));
362 if (type
!= "result") {
363 LOG(ERROR
) << "Received error in response to " << type_str
364 << " message: \"" << response
->Str()
365 << "\". Terminating the session.";
367 // TODO(sergeyu): There may be different reasons for error
368 // here. Parse the response stanza to find failure reason.
369 CloseInternal(PEER_IS_OFFLINE
);
374 void JingleSession::EnsurePendingTransportInfoMessage() {
375 // |transport_info_timer_| must be running iff
376 // |pending_transport_info_message_| exists.
377 DCHECK_EQ(pending_transport_info_message_
!= nullptr,
378 transport_info_timer_
.IsRunning());
380 if (!pending_transport_info_message_
) {
381 pending_transport_info_message_
.reset(new JingleMessage(
382 peer_jid_
, JingleMessage::TRANSPORT_INFO
, session_id_
));
383 pending_transport_info_message_
->standard_ice
= config_
->standard_ice();
385 // Delay sending the new candidates in case we get more candidates
386 // that we can send in one message.
387 transport_info_timer_
.Start(
388 FROM_HERE
, base::TimeDelta::FromMilliseconds(kTransportInfoSendDelayMs
),
389 this, &JingleSession::SendTransportInfo
);
393 void JingleSession::SendTransportInfo() {
394 DCHECK(pending_transport_info_message_
);
396 scoped_ptr
<IqRequest
> request
= session_manager_
->iq_sender()->SendIq(
397 pending_transport_info_message_
->ToXml(),
398 base::Bind(&JingleSession::OnTransportInfoResponse
,
399 base::Unretained(this)));
400 pending_transport_info_message_
.reset();
402 request
->SetTimeout(base::TimeDelta::FromSeconds(kTransportInfoTimeout
));
403 transport_info_requests_
.push_back(request
.release());
405 LOG(ERROR
) << "Failed to send a transport-info message";
409 void JingleSession::OnTransportInfoResponse(IqRequest
* request
,
410 const buzz::XmlElement
* response
) {
411 DCHECK(!transport_info_requests_
.empty());
413 // Consider transport-info requests sent before this one lost and delete
414 // corresponding IqRequest objects.
415 while (transport_info_requests_
.front() != request
) {
416 delete transport_info_requests_
.front();
417 transport_info_requests_
.pop_front();
420 // Delete the |request| itself.
421 DCHECK_EQ(request
, transport_info_requests_
.front());
423 transport_info_requests_
.pop_front();
425 // Ignore transport-info timeouts.
427 LOG(ERROR
) << "transport-info request has timed out.";
431 const std::string
& type
= response
->Attr(buzz::QName(std::string(), "type"));
432 if (type
!= "result") {
433 LOG(ERROR
) << "Received error in response to transport-info message: \""
434 << response
->Str() << "\". Terminating the session.";
435 CloseInternal(PEER_IS_OFFLINE
);
439 void JingleSession::OnIncomingMessage(const JingleMessage
& message
,
440 const ReplyCallback
& reply_callback
) {
441 DCHECK(CalledOnValidThread());
443 if (message
.from
!= peer_jid_
) {
444 // Ignore messages received from a different Jid.
445 reply_callback
.Run(JingleMessageReply::INVALID_SID
);
449 switch (message
.action
) {
450 case JingleMessage::SESSION_ACCEPT
:
451 OnAccept(message
, reply_callback
);
454 case JingleMessage::SESSION_INFO
:
455 OnSessionInfo(message
, reply_callback
);
458 case JingleMessage::TRANSPORT_INFO
:
459 reply_callback
.Run(JingleMessageReply::NONE
);
460 ProcessTransportInfo(message
);
463 case JingleMessage::SESSION_TERMINATE
:
464 OnTerminate(message
, reply_callback
);
468 reply_callback
.Run(JingleMessageReply::UNEXPECTED_REQUEST
);
472 void JingleSession::OnAccept(const JingleMessage
& message
,
473 const ReplyCallback
& reply_callback
) {
474 if (state_
!= CONNECTING
) {
475 reply_callback
.Run(JingleMessageReply::UNEXPECTED_REQUEST
);
479 reply_callback
.Run(JingleMessageReply::NONE
);
481 const buzz::XmlElement
* auth_message
=
482 message
.description
->authenticator_message();
484 DLOG(WARNING
) << "Received session-accept without authentication message ";
485 CloseInternal(INCOMPATIBLE_PROTOCOL
);
489 if (!InitializeConfigFromDescription(message
.description
.get())) {
490 CloseInternal(INCOMPATIBLE_PROTOCOL
);
496 DCHECK(authenticator_
->state() == Authenticator::WAITING_MESSAGE
);
497 authenticator_
->ProcessMessage(auth_message
, base::Bind(
498 &JingleSession::ProcessAuthenticationStep
,base::Unretained(this)));
501 void JingleSession::OnSessionInfo(const JingleMessage
& message
,
502 const ReplyCallback
& reply_callback
) {
503 if (!message
.info
.get() ||
504 !Authenticator::IsAuthenticatorMessage(message
.info
.get())) {
505 reply_callback
.Run(JingleMessageReply::UNSUPPORTED_INFO
);
509 if ((state_
!= CONNECTED
&& state_
!= AUTHENTICATING
) ||
510 authenticator_
->state() != Authenticator::WAITING_MESSAGE
) {
511 LOG(WARNING
) << "Received unexpected authenticator message "
512 << message
.info
->Str();
513 reply_callback
.Run(JingleMessageReply::UNEXPECTED_REQUEST
);
514 CloseInternal(INCOMPATIBLE_PROTOCOL
);
518 reply_callback
.Run(JingleMessageReply::NONE
);
520 authenticator_
->ProcessMessage(message
.info
.get(), base::Bind(
521 &JingleSession::ProcessAuthenticationStep
, base::Unretained(this)));
524 void JingleSession::ProcessTransportInfo(const JingleMessage
& message
) {
525 // Check if the transport information version matches what was negotiated.
526 if (message
.standard_ice
!= config_
->standard_ice()) {
527 LOG(ERROR
) << "Received transport-info message in format different from "
529 CloseInternal(INCOMPATIBLE_PROTOCOL
);
533 for (std::list
<JingleMessage::IceCredentials
>::const_iterator it
=
534 message
.ice_credentials
.begin();
535 it
!= message
.ice_credentials
.end(); ++it
) {
536 ChannelsMap::iterator channel
= channels_
.find(it
->channel
);
537 if (channel
!= channels_
.end()) {
538 channel
->second
->SetRemoteCredentials(it
->ufrag
, it
->password
);
540 // Transport info was received before the channel was created.
541 // This could happen due to messages being reordered on the wire.
542 pending_remote_ice_credentials_
.push_back(*it
);
546 for (std::list
<JingleMessage::NamedCandidate
>::const_iterator it
=
547 message
.candidates
.begin();
548 it
!= message
.candidates
.end(); ++it
) {
549 ChannelsMap::iterator channel
= channels_
.find(it
->name
);
550 if (channel
!= channels_
.end()) {
551 channel
->second
->AddRemoteCandidate(it
->candidate
);
553 // Transport info was received before the channel was created.
554 // This could happen due to messages being reordered on the wire.
555 pending_remote_candidates_
.push_back(*it
);
560 void JingleSession::OnTerminate(const JingleMessage
& message
,
561 const ReplyCallback
& reply_callback
) {
562 if (!is_session_active()) {
563 LOG(WARNING
) << "Received unexpected session-terminate message.";
564 reply_callback
.Run(JingleMessageReply::UNEXPECTED_REQUEST
);
568 reply_callback
.Run(JingleMessageReply::NONE
);
570 switch (message
.reason
) {
571 case JingleMessage::SUCCESS
:
572 if (state_
== CONNECTING
) {
573 error_
= SESSION_REJECTED
;
578 case JingleMessage::DECLINE
:
579 error_
= AUTHENTICATION_FAILED
;
581 case JingleMessage::CANCEL
:
582 error_
= HOST_OVERLOAD
;
584 case JingleMessage::GENERAL_ERROR
:
585 error_
= CHANNEL_CONNECTION_ERROR
;
587 case JingleMessage::INCOMPATIBLE_PARAMETERS
:
588 error_
= INCOMPATIBLE_PROTOCOL
;
591 error_
= UNKNOWN_ERROR
;
601 bool JingleSession::InitializeConfigFromDescription(
602 const ContentDescription
* description
) {
604 config_
= SessionConfig::GetFinalConfig(description
->config());
606 LOG(ERROR
) << "session-accept does not specify configuration";
609 if (!candidate_config()->IsSupported(*config_
)) {
610 LOG(ERROR
) << "session-accept specifies an invalid configuration";
617 void JingleSession::ProcessAuthenticationStep() {
618 DCHECK(CalledOnValidThread());
619 DCHECK_NE(authenticator_
->state(), Authenticator::PROCESSING_MESSAGE
);
621 if (state_
!= CONNECTED
&& state_
!= AUTHENTICATING
) {
622 DCHECK(state_
== FAILED
|| state_
== CLOSED
);
623 // The remote host closed the connection while the authentication was being
624 // processed asynchronously, nothing to do.
628 if (authenticator_
->state() == Authenticator::MESSAGE_READY
) {
629 JingleMessage
message(peer_jid_
, JingleMessage::SESSION_INFO
, session_id_
);
630 message
.info
= authenticator_
->GetNextMessage();
631 DCHECK(message
.info
.get());
632 SendMessage(message
);
634 DCHECK_NE(authenticator_
->state(), Authenticator::MESSAGE_READY
);
636 // The current JingleSession object can be destroyed by event_handler of
637 // SetState(AUTHENTICATING) and cause subsequent dereferencing of the this
638 // pointer to crash. To protect against it, we run ContinueAuthenticationStep
639 // asychronously using a weak pointer.
640 base::ThreadTaskRunnerHandle::Get()->PostTask(
642 base::Bind(&JingleSession::ContinueAuthenticationStep
,
643 weak_factory_
.GetWeakPtr()));
645 if (authenticator_
->started()) {
646 SetState(AUTHENTICATING
);
650 void JingleSession::ContinueAuthenticationStep() {
651 if (authenticator_
->state() == Authenticator::ACCEPTED
) {
653 } else if (authenticator_
->state() == Authenticator::REJECTED
) {
654 CloseInternal(AuthRejectionReasonToErrorCode(
655 authenticator_
->rejection_reason()));
659 void JingleSession::OnAuthenticated() {
660 pseudotcp_channel_factory_
.reset(new PseudoTcpChannelFactory(this));
661 secure_channel_factory_
.reset(
662 new SecureChannelFactory(pseudotcp_channel_factory_
.get(),
663 authenticator_
.get()));
665 SetState(AUTHENTICATED
);
668 void JingleSession::CloseInternal(ErrorCode error
) {
669 DCHECK(CalledOnValidThread());
671 if (is_session_active()) {
672 // Send session-terminate message with the appropriate error code.
673 JingleMessage::Reason reason
;
676 reason
= JingleMessage::SUCCESS
;
678 case SESSION_REJECTED
:
679 case AUTHENTICATION_FAILED
:
680 reason
= JingleMessage::DECLINE
;
682 case INCOMPATIBLE_PROTOCOL
:
683 reason
= JingleMessage::INCOMPATIBLE_PARAMETERS
;
686 reason
= JingleMessage::CANCEL
;
689 reason
= JingleMessage::GENERAL_ERROR
;
692 JingleMessage
message(peer_jid_
, JingleMessage::SESSION_TERMINATE
,
694 message
.reason
= reason
;
695 SendMessage(message
);
700 if (state_
!= FAILED
&& state_
!= CLOSED
) {
709 void JingleSession::SetState(State new_state
) {
710 DCHECK(CalledOnValidThread());
712 if (new_state
!= state_
) {
713 DCHECK_NE(state_
, CLOSED
);
714 DCHECK_NE(state_
, FAILED
);
718 event_handler_
->OnSessionStateChange(new_state
);
722 bool JingleSession::is_session_active() {
723 return state_
== CONNECTING
|| state_
== ACCEPTING
|| state_
== CONNECTED
||
724 state_
== AUTHENTICATING
|| state_
== AUTHENTICATED
;
727 } // namespace protocol
728 } // namespace remoting