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/session_config.h"
22 #include "remoting/signaling/iq_sender.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
;
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
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 // During a reconnection, it usually takes longer for the peer to respond due to
44 // pending messages in the channel from the previous session. From experiment,
45 // it can take up to 20s for the session to reconnect. To make it safe, setting
46 // the timeout to 30s.
47 const int kSessionInitiateAndAcceptTimeout
= kDefaultMessageTimeout
* 3;
49 // Timeout for the transport-info messages.
50 const int kTransportInfoTimeout
= 10 * 60;
52 // Name of the multiplexed channel.
53 const char kMuxChannelName
[] = "mux";
55 ErrorCode
AuthRejectionReasonToErrorCode(
56 Authenticator::RejectionReason reason
) {
58 case Authenticator::INVALID_CREDENTIALS
:
59 return AUTHENTICATION_FAILED
;
60 case Authenticator::PROTOCOL_ERROR
:
61 return INCOMPATIBLE_PROTOCOL
;
69 JingleSession::JingleSession(JingleSessionManager
* session_manager
)
70 : session_manager_(session_manager
),
74 config_is_set_(false),
78 JingleSession::~JingleSession() {
79 channel_multiplexer_
.reset();
80 STLDeleteContainerPointers(pending_requests_
.begin(),
81 pending_requests_
.end());
82 STLDeleteContainerPointers(transport_info_requests_
.begin(),
83 transport_info_requests_
.end());
84 STLDeleteContainerPairSecondPointers(channels_
.begin(), channels_
.end());
85 session_manager_
->SessionDestroyed(this);
88 void JingleSession::SetEventHandler(Session::EventHandler
* event_handler
) {
89 DCHECK(CalledOnValidThread());
90 DCHECK(event_handler
);
91 event_handler_
= event_handler
;
94 ErrorCode
JingleSession::error() {
95 DCHECK(CalledOnValidThread());
99 void JingleSession::StartConnection(
100 const std::string
& peer_jid
,
101 scoped_ptr
<Authenticator
> authenticator
,
102 scoped_ptr
<CandidateSessionConfig
> config
) {
103 DCHECK(CalledOnValidThread());
104 DCHECK(authenticator
.get());
105 DCHECK_EQ(authenticator
->state(), Authenticator::MESSAGE_READY
);
107 peer_jid_
= peer_jid
;
108 authenticator_
= authenticator
.Pass();
109 candidate_config_
= config
.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
,
120 message
.initiator
= session_manager_
->signal_strategy_
->GetLocalJid();
121 message
.description
.reset(
122 new ContentDescription(candidate_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
;
140 candidate_config_
= initiate_message
.description
->config()->Clone();
145 void JingleSession::AcceptIncomingConnection(
146 const JingleMessage
& initiate_message
) {
147 DCHECK(config_is_set_
);
149 // Process the first authentication message.
150 const buzz::XmlElement
* first_auth_message
=
151 initiate_message
.description
->authenticator_message();
153 if (!first_auth_message
) {
154 CloseInternal(INCOMPATIBLE_PROTOCOL
);
158 DCHECK_EQ(authenticator_
->state(), Authenticator::WAITING_MESSAGE
);
159 // |authenticator_| is owned, so Unretained() is safe here.
160 authenticator_
->ProcessMessage(first_auth_message
, base::Bind(
161 &JingleSession::ContinueAcceptIncomingConnection
,
162 base::Unretained(this)));
165 void JingleSession::ContinueAcceptIncomingConnection() {
166 DCHECK_NE(authenticator_
->state(), Authenticator::PROCESSING_MESSAGE
);
167 if (authenticator_
->state() == Authenticator::REJECTED
) {
168 CloseInternal(AuthRejectionReasonToErrorCode(
169 authenticator_
->rejection_reason()));
173 // Send the session-accept message.
174 JingleMessage
message(peer_jid_
, JingleMessage::SESSION_ACCEPT
,
177 scoped_ptr
<buzz::XmlElement
> auth_message
;
178 if (authenticator_
->state() == Authenticator::MESSAGE_READY
)
179 auth_message
= authenticator_
->GetNextMessage();
181 message
.description
.reset(
182 new ContentDescription(CandidateSessionConfig::CreateFrom(config_
),
183 auth_message
.Pass()));
184 SendMessage(message
);
189 if (authenticator_
->state() == Authenticator::ACCEPTED
) {
190 SetState(AUTHENTICATED
);
192 DCHECK_EQ(authenticator_
->state(), Authenticator::WAITING_MESSAGE
);
193 if (authenticator_
->started()) {
194 SetState(AUTHENTICATING
);
199 const std::string
& JingleSession::jid() {
200 DCHECK(CalledOnValidThread());
204 const CandidateSessionConfig
* JingleSession::candidate_config() {
205 DCHECK(CalledOnValidThread());
206 return candidate_config_
.get();
209 const SessionConfig
& JingleSession::config() {
210 DCHECK(CalledOnValidThread());
214 void JingleSession::set_config(const SessionConfig
& config
) {
215 DCHECK(CalledOnValidThread());
216 DCHECK(!config_is_set_
);
218 config_is_set_
= true;
221 ChannelFactory
* JingleSession::GetTransportChannelFactory() {
222 DCHECK(CalledOnValidThread());
226 ChannelFactory
* JingleSession::GetMultiplexedChannelFactory() {
227 DCHECK(CalledOnValidThread());
228 if (!channel_multiplexer_
.get())
229 channel_multiplexer_
.reset(new ChannelMultiplexer(this, kMuxChannelName
));
230 return channel_multiplexer_
.get();
233 void JingleSession::Close() {
234 DCHECK(CalledOnValidThread());
239 void JingleSession::AddPendingRemoteCandidates(Transport
* channel
,
240 const std::string
& name
) {
241 std::list
<JingleMessage::NamedCandidate
>::iterator it
=
242 pending_remote_candidates_
.begin();
243 while(it
!= pending_remote_candidates_
.end()) {
244 if (it
->name
== name
) {
245 channel
->AddRemoteCandidate(it
->candidate
);
246 it
= pending_remote_candidates_
.erase(it
);
253 void JingleSession::CreateStreamChannel(
254 const std::string
& name
,
255 const StreamChannelCallback
& callback
) {
256 DCHECK(!channels_
[name
]);
258 scoped_ptr
<ChannelAuthenticator
> channel_authenticator
=
259 authenticator_
->CreateChannelAuthenticator();
260 scoped_ptr
<StreamTransport
> channel
=
261 session_manager_
->transport_factory_
->CreateStreamTransport();
262 channel
->Initialize(name
, this, channel_authenticator
.Pass());
263 channel
->Connect(callback
);
264 AddPendingRemoteCandidates(channel
.get(), name
);
265 channels_
[name
] = channel
.release();
268 void JingleSession::CreateDatagramChannel(
269 const std::string
& name
,
270 const DatagramChannelCallback
& callback
) {
271 DCHECK(!channels_
[name
]);
273 scoped_ptr
<ChannelAuthenticator
> channel_authenticator
=
274 authenticator_
->CreateChannelAuthenticator();
275 scoped_ptr
<DatagramTransport
> channel
=
276 session_manager_
->transport_factory_
->CreateDatagramTransport();
277 channel
->Initialize(name
, this, channel_authenticator
.Pass());
278 channel
->Connect(callback
);
279 AddPendingRemoteCandidates(channel
.get(), name
);
280 channels_
[name
] = channel
.release();
283 void JingleSession::CancelChannelCreation(const std::string
& name
) {
284 ChannelsMap::iterator it
= channels_
.find(name
);
285 if (it
!= channels_
.end() && !it
->second
->is_connected()) {
287 DCHECK(!channels_
[name
]);
291 void JingleSession::OnTransportCandidate(Transport
* transport
,
292 const cricket::Candidate
& candidate
) {
293 pending_candidates_
.push_back(JingleMessage::NamedCandidate(
294 transport
->name(), candidate
));
296 if (!transport_infos_timer_
.IsRunning()) {
297 // Delay sending the new candidates in case we get more candidates
298 // that we can send in one message.
299 transport_infos_timer_
.Start(
300 FROM_HERE
, base::TimeDelta::FromMilliseconds(kTransportInfoSendDelayMs
),
301 this, &JingleSession::SendTransportInfo
);
305 void JingleSession::OnTransportRouteChange(Transport
* transport
,
306 const TransportRoute
& route
) {
308 event_handler_
->OnSessionRouteChange(transport
->name(), route
);
311 void JingleSession::OnTransportFailed(Transport
* transport
) {
312 CloseInternal(CHANNEL_CONNECTION_ERROR
);
315 void JingleSession::OnTransportDeleted(Transport
* transport
) {
316 ChannelsMap::iterator it
= channels_
.find(transport
->name());
317 DCHECK_EQ(it
->second
, transport
);
321 void JingleSession::SendMessage(const JingleMessage
& message
) {
322 scoped_ptr
<IqRequest
> request
= session_manager_
->iq_sender()->SendIq(
324 base::Bind(&JingleSession::OnMessageResponse
,
325 base::Unretained(this),
328 int timeout
= kDefaultMessageTimeout
;
329 if (message
.action
== JingleMessage::SESSION_INITIATE
||
330 message
.action
== JingleMessage::SESSION_ACCEPT
) {
331 timeout
= kSessionInitiateAndAcceptTimeout
;
334 request
->SetTimeout(base::TimeDelta::FromSeconds(timeout
));
335 pending_requests_
.insert(request
.release());
337 LOG(ERROR
) << "Failed to send a "
338 << JingleMessage::GetActionName(message
.action
) << " message";
342 void JingleSession::OnMessageResponse(
343 JingleMessage::ActionType request_type
,
345 const buzz::XmlElement
* response
) {
346 std::string type_str
= JingleMessage::GetActionName(request_type
);
348 // Delete the request from the list of pending requests.
349 pending_requests_
.erase(request
);
352 // |response| will be NULL if the request timed out.
354 LOG(ERROR
) << type_str
<< " request timed out.";
355 CloseInternal(SIGNALING_TIMEOUT
);
358 const std::string
& type
=
359 response
->Attr(buzz::QName(std::string(), "type"));
360 if (type
!= "result") {
361 LOG(ERROR
) << "Received error in response to " << type_str
362 << " message: \"" << response
->Str()
363 << "\". Terminating the session.";
365 switch (request_type
) {
366 case JingleMessage::SESSION_INFO
:
367 // session-info is used for the new authentication protocol,
368 // and wasn't previously supported.
369 CloseInternal(INCOMPATIBLE_PROTOCOL
);
373 // TODO(sergeyu): There may be different reasons for error
374 // here. Parse the response stanza to find failure reason.
375 CloseInternal(PEER_IS_OFFLINE
);
381 void JingleSession::SendTransportInfo() {
382 JingleMessage
message(peer_jid_
, JingleMessage::TRANSPORT_INFO
, session_id_
);
383 message
.candidates
.swap(pending_candidates_
);
385 scoped_ptr
<IqRequest
> request
= session_manager_
->iq_sender()->SendIq(
387 base::Bind(&JingleSession::OnTransportInfoResponse
,
388 base::Unretained(this)));
390 request
->SetTimeout(base::TimeDelta::FromSeconds(kTransportInfoTimeout
));
391 transport_info_requests_
.push_back(request
.release());
393 LOG(ERROR
) << "Failed to send a transport-info message";
397 void JingleSession::OnTransportInfoResponse(IqRequest
* request
,
398 const buzz::XmlElement
* response
) {
399 DCHECK(!transport_info_requests_
.empty());
401 // Consider transport-info requests sent before this one lost and delete
402 // corresponding IqRequest objects.
403 while (transport_info_requests_
.front() != request
) {
404 delete transport_info_requests_
.front();
405 transport_info_requests_
.pop_front();
408 // Delete the |request| itself.
409 DCHECK_EQ(request
, transport_info_requests_
.front());
411 transport_info_requests_
.pop_front();
413 // Ignore transport-info timeouts.
415 LOG(ERROR
) << "transport-info request has timed out.";
419 const std::string
& type
= response
->Attr(buzz::QName(std::string(), "type"));
420 if (type
!= "result") {
421 LOG(ERROR
) << "Received error in response to transport-info message: \""
422 << response
->Str() << "\". Terminating the session.";
423 CloseInternal(PEER_IS_OFFLINE
);
427 void JingleSession::OnIncomingMessage(const JingleMessage
& message
,
428 const ReplyCallback
& reply_callback
) {
429 DCHECK(CalledOnValidThread());
431 if (message
.from
!= peer_jid_
) {
432 // Ignore messages received from a different Jid.
433 reply_callback
.Run(JingleMessageReply::INVALID_SID
);
437 switch (message
.action
) {
438 case JingleMessage::SESSION_ACCEPT
:
439 OnAccept(message
, reply_callback
);
442 case JingleMessage::SESSION_INFO
:
443 OnSessionInfo(message
, reply_callback
);
446 case JingleMessage::TRANSPORT_INFO
:
447 reply_callback
.Run(JingleMessageReply::NONE
);
448 ProcessTransportInfo(message
);
451 case JingleMessage::SESSION_TERMINATE
:
452 OnTerminate(message
, reply_callback
);
456 reply_callback
.Run(JingleMessageReply::UNEXPECTED_REQUEST
);
460 void JingleSession::OnAccept(const JingleMessage
& message
,
461 const ReplyCallback
& reply_callback
) {
462 if (state_
!= CONNECTING
) {
463 reply_callback
.Run(JingleMessageReply::UNEXPECTED_REQUEST
);
467 reply_callback
.Run(JingleMessageReply::NONE
);
469 const buzz::XmlElement
* auth_message
=
470 message
.description
->authenticator_message();
472 DLOG(WARNING
) << "Received session-accept without authentication message ";
473 CloseInternal(INCOMPATIBLE_PROTOCOL
);
477 if (!InitializeConfigFromDescription(message
.description
.get())) {
478 CloseInternal(INCOMPATIBLE_PROTOCOL
);
482 // In case there is transport information in the accept message.
483 ProcessTransportInfo(message
);
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
);
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
);
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::NamedCandidate
>::const_iterator it
=
517 message
.candidates
.begin();
518 it
!= message
.candidates
.end(); ++it
) {
519 ChannelsMap::iterator channel
= channels_
.find(it
->name
);
520 if (channel
!= channels_
.end()) {
521 channel
->second
->AddRemoteCandidate(it
->candidate
);
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_candidates_
.push_back(*it
);
530 void JingleSession::OnTerminate(const JingleMessage
& message
,
531 const ReplyCallback
& reply_callback
) {
532 if (!is_session_active()) {
533 LOG(WARNING
) << "Received unexpected session-terminate message.";
534 reply_callback
.Run(JingleMessageReply::UNEXPECTED_REQUEST
);
538 reply_callback
.Run(JingleMessageReply::NONE
);
540 switch (message
.reason
) {
541 case JingleMessage::SUCCESS
:
542 if (state_
== CONNECTING
) {
543 error_
= SESSION_REJECTED
;
548 case JingleMessage::DECLINE
:
549 error_
= AUTHENTICATION_FAILED
;
551 case JingleMessage::CANCEL
:
552 error_
= HOST_OVERLOAD
;
554 case JingleMessage::GENERAL_ERROR
:
555 error_
= CHANNEL_CONNECTION_ERROR
;
557 case JingleMessage::INCOMPATIBLE_PARAMETERS
:
558 error_
= INCOMPATIBLE_PROTOCOL
;
561 error_
= UNKNOWN_ERROR
;
571 bool JingleSession::InitializeConfigFromDescription(
572 const ContentDescription
* description
) {
575 if (!description
->config()->GetFinalConfig(&config_
)) {
576 LOG(ERROR
) << "session-accept does not specify configuration";
579 if (!candidate_config()->IsSupported(config_
)) {
580 LOG(ERROR
) << "session-accept specifies an invalid configuration";
587 void JingleSession::ProcessAuthenticationStep() {
588 DCHECK(CalledOnValidThread());
589 DCHECK_NE(authenticator_
->state(), Authenticator::PROCESSING_MESSAGE
);
591 if (state_
!= CONNECTED
&& state_
!= AUTHENTICATING
) {
592 DCHECK(state_
== FAILED
|| state_
== CLOSED
);
593 // The remote host closed the connection while the authentication was being
594 // processed asynchronously, nothing to do.
598 if (authenticator_
->state() == Authenticator::MESSAGE_READY
) {
599 JingleMessage
message(peer_jid_
, JingleMessage::SESSION_INFO
, session_id_
);
600 message
.info
= authenticator_
->GetNextMessage();
601 DCHECK(message
.info
.get());
602 SendMessage(message
);
604 DCHECK_NE(authenticator_
->state(), Authenticator::MESSAGE_READY
);
606 // The current JingleSession object can be destroyed by event_handler of
607 // SetState(AUTHENTICATING) and cause subsequent dereferencing of the this
608 // pointer to crash. To protect against it, we run ContinueAuthenticationStep
609 // asychronously using a weak pointer.
610 base::ThreadTaskRunnerHandle::Get()->PostTask(
612 base::Bind(&JingleSession::ContinueAuthenticationStep
,
613 weak_factory_
.GetWeakPtr()));
615 if (authenticator_
->started()) {
616 SetState(AUTHENTICATING
);
620 void JingleSession::ContinueAuthenticationStep() {
621 if (authenticator_
->state() == Authenticator::ACCEPTED
) {
622 SetState(AUTHENTICATED
);
623 } else if (authenticator_
->state() == Authenticator::REJECTED
) {
624 CloseInternal(AuthRejectionReasonToErrorCode(
625 authenticator_
->rejection_reason()));
629 void JingleSession::CloseInternal(ErrorCode error
) {
630 DCHECK(CalledOnValidThread());
632 if (is_session_active()) {
633 // Send session-terminate message with the appropriate error code.
634 JingleMessage::Reason reason
;
637 reason
= JingleMessage::SUCCESS
;
639 case SESSION_REJECTED
:
640 case AUTHENTICATION_FAILED
:
641 reason
= JingleMessage::DECLINE
;
643 case INCOMPATIBLE_PROTOCOL
:
644 reason
= JingleMessage::INCOMPATIBLE_PARAMETERS
;
647 reason
= JingleMessage::CANCEL
;
650 reason
= JingleMessage::GENERAL_ERROR
;
653 JingleMessage
message(peer_jid_
, JingleMessage::SESSION_TERMINATE
,
655 message
.reason
= reason
;
656 SendMessage(message
);
661 if (state_
!= FAILED
&& state_
!= CLOSED
) {
670 void JingleSession::SetState(State new_state
) {
671 DCHECK(CalledOnValidThread());
673 if (new_state
!= state_
) {
674 DCHECK_NE(state_
, CLOSED
);
675 DCHECK_NE(state_
, FAILED
);
679 event_handler_
->OnSessionStateChange(new_state
);
683 bool JingleSession::is_session_active() {
684 return state_
== CONNECTING
|| state_
== ACCEPTING
|| state_
== CONNECTED
||
685 state_
== AUTHENTICATING
|| state_
== AUTHENTICATED
;
688 } // namespace protocol
689 } // namespace remoting