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/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
;
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
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
) {
50 case Authenticator::INVALID_CREDENTIALS
:
51 return AUTHENTICATION_FAILED
;
52 case Authenticator::PROTOCOL_ERROR
:
53 return INCOMPATIBLE_PROTOCOL
;
61 JingleSession::JingleSession(JingleSessionManager
* session_manager
)
62 : session_manager_(session_manager
),
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());
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
);
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
,
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();
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
);
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()));
164 // Send the session-accept message.
165 JingleMessage
message(peer_jid_
, JingleMessage::SESSION_ACCEPT
,
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
);
180 if (authenticator_
->state() == Authenticator::ACCEPTED
) {
181 SetState(AUTHENTICATED
);
183 DCHECK_EQ(authenticator_
->state(), Authenticator::WAITING_MESSAGE
);
189 const std::string
& JingleSession::jid() {
190 DCHECK(CalledOnValidThread());
194 const CandidateSessionConfig
* JingleSession::candidate_config() {
195 DCHECK(CalledOnValidThread());
196 return candidate_config_
.get();
199 const SessionConfig
& JingleSession::config() {
200 DCHECK(CalledOnValidThread());
204 void JingleSession::set_config(const SessionConfig
& config
) {
205 DCHECK(CalledOnValidThread());
206 DCHECK(!config_is_set_
);
208 config_is_set_
= true;
211 ChannelFactory
* JingleSession::GetTransportChannelFactory() {
212 DCHECK(CalledOnValidThread());
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());
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
);
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()) {
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
) {
298 event_handler_
->OnSessionRouteChange(transport
->name(), route
);
301 void JingleSession::OnTransportReady(Transport
* transport
, bool ready
) {
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
);
316 void JingleSession::SendMessage(const JingleMessage
& message
) {
317 scoped_ptr
<IqRequest
> request
= session_manager_
->iq_sender()->SendIq(
319 base::Bind(&JingleSession::OnMessageResponse
,
320 base::Unretained(this), message
.action
));
322 request
->SetTimeout(base::TimeDelta::FromSeconds(kDefaultMessageTimeout
));
323 pending_requests_
.insert(request
.release());
325 LOG(ERROR
) << "Failed to send a "
326 << JingleMessage::GetActionName(message
.action
) << " message";
330 void JingleSession::OnMessageResponse(
331 JingleMessage::ActionType request_type
,
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
);
340 // |response| will be NULL if the request timed out.
342 LOG(ERROR
) << type_str
<< " request timed out.";
343 CloseInternal(SIGNALING_TIMEOUT
);
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
);
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(
375 base::Bind(&JingleSession::OnTransportInfoResponse
,
376 base::Unretained(this)));
378 request
->SetTimeout(base::TimeDelta::FromSeconds(kTransportInfoTimeout
));
379 transport_info_requests_
.push_back(request
.release());
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());
399 transport_info_requests_
.pop_front();
401 // Ignore transport-info timeouts.
403 LOG(ERROR
) << "transport-info request has timed out.";
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
);
425 switch (message
.action
) {
426 case JingleMessage::SESSION_ACCEPT
:
427 OnAccept(message
, reply_callback
);
430 case JingleMessage::SESSION_INFO
:
431 OnSessionInfo(message
, reply_callback
);
434 case JingleMessage::TRANSPORT_INFO
:
435 reply_callback
.Run(JingleMessageReply::NONE
);
436 ProcessTransportInfo(message
);
439 case JingleMessage::SESSION_TERMINATE
:
440 OnTerminate(message
, reply_callback
);
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
);
455 reply_callback
.Run(JingleMessageReply::NONE
);
457 const buzz::XmlElement
* auth_message
=
458 message
.description
->authenticator_message();
460 DLOG(WARNING
) << "Received session-accept without authentication message ";
461 CloseInternal(INCOMPATIBLE_PROTOCOL
);
465 if (!InitializeConfigFromDescription(message
.description
.get())) {
466 CloseInternal(INCOMPATIBLE_PROTOCOL
);
470 // In case there is transport information in the accept message.
471 ProcessTransportInfo(message
);
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
);
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
);
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
);
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
);
527 reply_callback
.Run(JingleMessageReply::NONE
);
529 switch (message
.reason
) {
530 case JingleMessage::SUCCESS
:
531 if (state_
== CONNECTING
) {
532 error_
= SESSION_REJECTED
;
537 case JingleMessage::DECLINE
:
538 error_
= AUTHENTICATION_FAILED
;
540 case JingleMessage::CANCEL
:
541 error_
= HOST_OVERLOAD
;
543 case JingleMessage::GENERAL_ERROR
:
544 error_
= CHANNEL_CONNECTION_ERROR
;
546 case JingleMessage::INCOMPATIBLE_PARAMETERS
:
547 error_
= INCOMPATIBLE_PROTOCOL
;
550 error_
= UNKNOWN_ERROR
;
560 bool JingleSession::InitializeConfigFromDescription(
561 const ContentDescription
* description
) {
564 if (!description
->config()->GetFinalConfig(&config_
)) {
565 LOG(ERROR
) << "session-accept does not specify configuration";
568 if (!candidate_config()->IsSupported(config_
)) {
569 LOG(ERROR
) << "session-accept specifies an invalid configuration";
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.
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
;
612 reason
= JingleMessage::SUCCESS
;
614 case SESSION_REJECTED
:
615 case AUTHENTICATION_FAILED
:
616 reason
= JingleMessage::DECLINE
;
618 case INCOMPATIBLE_PROTOCOL
:
619 reason
= JingleMessage::INCOMPATIBLE_PARAMETERS
;
622 reason
= JingleMessage::CANCEL
;
625 reason
= JingleMessage::GENERAL_ERROR
;
628 JingleMessage
message(peer_jid_
, JingleMessage::SESSION_TERMINATE
,
630 message
.reason
= reason
;
631 SendMessage(message
);
636 if (state_
!= FAILED
&& state_
!= 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
);
654 event_handler_
->OnSessionStateChange(new_state
);
658 } // namespace protocol
659 } // namespace remoting