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/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
;
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 // 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
) {
52 case Authenticator::INVALID_CREDENTIALS
:
53 return AUTHENTICATION_FAILED
;
54 case Authenticator::PROTOCOL_ERROR
:
55 return INCOMPATIBLE_PROTOCOL
;
63 JingleSession::JingleSession(JingleSessionManager
* session_manager
)
64 : session_manager_(session_manager
),
68 config_is_set_(false),
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());
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
,
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();
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
);
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()));
167 // Send the session-accept message.
168 JingleMessage
message(peer_jid_
, JingleMessage::SESSION_ACCEPT
,
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
);
183 if (authenticator_
->state() == Authenticator::ACCEPTED
) {
184 SetState(AUTHENTICATED
);
186 DCHECK_EQ(authenticator_
->state(), Authenticator::WAITING_MESSAGE
);
187 if (authenticator_
->started()) {
188 SetState(AUTHENTICATING
);
193 const std::string
& JingleSession::jid() {
194 DCHECK(CalledOnValidThread());
198 const CandidateSessionConfig
* JingleSession::candidate_config() {
199 DCHECK(CalledOnValidThread());
200 return candidate_config_
.get();
203 const SessionConfig
& JingleSession::config() {
204 DCHECK(CalledOnValidThread());
208 void JingleSession::set_config(const SessionConfig
& config
) {
209 DCHECK(CalledOnValidThread());
210 DCHECK(!config_is_set_
);
212 config_is_set_
= true;
215 ChannelFactory
* JingleSession::GetTransportChannelFactory() {
216 DCHECK(CalledOnValidThread());
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());
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
);
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()) {
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
) {
302 event_handler_
->OnSessionRouteChange(transport
->name(), route
);
305 void JingleSession::OnTransportReady(Transport
* transport
, bool ready
) {
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
);
320 void JingleSession::SendMessage(const JingleMessage
& message
) {
321 scoped_ptr
<IqRequest
> request
= session_manager_
->iq_sender()->SendIq(
323 base::Bind(&JingleSession::OnMessageResponse
,
324 base::Unretained(this), message
.action
));
326 request
->SetTimeout(base::TimeDelta::FromSeconds(kDefaultMessageTimeout
));
327 pending_requests_
.insert(request
.release());
329 LOG(ERROR
) << "Failed to send a "
330 << JingleMessage::GetActionName(message
.action
) << " message";
334 void JingleSession::OnMessageResponse(
335 JingleMessage::ActionType request_type
,
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
);
344 // |response| will be NULL if the request timed out.
346 LOG(ERROR
) << type_str
<< " request timed out.";
347 CloseInternal(SIGNALING_TIMEOUT
);
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
);
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(
379 base::Bind(&JingleSession::OnTransportInfoResponse
,
380 base::Unretained(this)));
382 request
->SetTimeout(base::TimeDelta::FromSeconds(kTransportInfoTimeout
));
383 transport_info_requests_
.push_back(request
.release());
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());
403 transport_info_requests_
.pop_front();
405 // Ignore transport-info timeouts.
407 LOG(ERROR
) << "transport-info request has timed out.";
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
);
429 switch (message
.action
) {
430 case JingleMessage::SESSION_ACCEPT
:
431 OnAccept(message
, reply_callback
);
434 case JingleMessage::SESSION_INFO
:
435 OnSessionInfo(message
, reply_callback
);
438 case JingleMessage::TRANSPORT_INFO
:
439 reply_callback
.Run(JingleMessageReply::NONE
);
440 ProcessTransportInfo(message
);
443 case JingleMessage::SESSION_TERMINATE
:
444 OnTerminate(message
, reply_callback
);
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
);
459 reply_callback
.Run(JingleMessageReply::NONE
);
461 const buzz::XmlElement
* auth_message
=
462 message
.description
->authenticator_message();
464 DLOG(WARNING
) << "Received session-accept without authentication message ";
465 CloseInternal(INCOMPATIBLE_PROTOCOL
);
469 if (!InitializeConfigFromDescription(message
.description
.get())) {
470 CloseInternal(INCOMPATIBLE_PROTOCOL
);
474 // In case there is transport information in the accept message.
475 ProcessTransportInfo(message
);
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
);
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
);
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
);
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
);
530 reply_callback
.Run(JingleMessageReply::NONE
);
532 switch (message
.reason
) {
533 case JingleMessage::SUCCESS
:
534 if (state_
== CONNECTING
) {
535 error_
= SESSION_REJECTED
;
540 case JingleMessage::DECLINE
:
541 error_
= AUTHENTICATION_FAILED
;
543 case JingleMessage::CANCEL
:
544 error_
= HOST_OVERLOAD
;
546 case JingleMessage::GENERAL_ERROR
:
547 error_
= CHANNEL_CONNECTION_ERROR
;
549 case JingleMessage::INCOMPATIBLE_PARAMETERS
:
550 error_
= INCOMPATIBLE_PROTOCOL
;
553 error_
= UNKNOWN_ERROR
;
563 bool JingleSession::InitializeConfigFromDescription(
564 const ContentDescription
* description
) {
567 if (!description
->config()->GetFinalConfig(&config_
)) {
568 LOG(ERROR
) << "session-accept does not specify configuration";
571 if (!candidate_config()->IsSupported(config_
)) {
572 LOG(ERROR
) << "session-accept specifies an invalid configuration";
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.
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(
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
;
629 reason
= JingleMessage::SUCCESS
;
631 case SESSION_REJECTED
:
632 case AUTHENTICATION_FAILED
:
633 reason
= JingleMessage::DECLINE
;
635 case INCOMPATIBLE_PROTOCOL
:
636 reason
= JingleMessage::INCOMPATIBLE_PARAMETERS
;
639 reason
= JingleMessage::CANCEL
;
642 reason
= JingleMessage::GENERAL_ERROR
;
645 JingleMessage
message(peer_jid_
, JingleMessage::SESSION_TERMINATE
,
647 message
.reason
= reason
;
648 SendMessage(message
);
653 if (state_
!= FAILED
&& state_
!= 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
);
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