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. 4 5#include "remoting/protocol/jingle_session.h" 6 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/libjingle/source/talk/p2p/base/candidate.h" 27#include "third_party/webrtc/libjingle/xmllite/xmlelement.h" 28 29using buzz::XmlElement; 30 31namespace remoting { 32namespace protocol { 33 34namespace { 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. 40const int kTransportInfoSendDelayMs = 2; 41 42// How long we should wait for a response from the other end. This value is used 43// for all requests except |transport-info|. 44const int kDefaultMessageTimeout = 10; 45 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. 50const int kSessionInitiateAndAcceptTimeout = kDefaultMessageTimeout * 3; 51 52// Timeout for the transport-info messages. 53const int kTransportInfoTimeout = 10 * 60; 54 55// Name of the multiplexed channel. 56const char kMuxChannelName[] = "mux"; 57 58ErrorCode 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; 65 } 66 NOTREACHED(); 67 return UNKNOWN_ERROR; 68} 69 70} // namespace 71 72JingleSession::JingleSession(JingleSessionManager* session_manager) 73 : session_manager_(session_manager), 74 event_handler_(NULL), 75 state_(INITIALIZING), 76 error_(OK), 77 config_is_set_(false), 78 weak_factory_(this) { 79} 80 81JingleSession::~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()); 87 88 DCHECK(channels_.empty()); 89 90 session_manager_->SessionDestroyed(this); 91} 92 93void JingleSession::SetEventHandler(Session::EventHandler* event_handler) { 94 DCHECK(CalledOnValidThread()); 95 DCHECK(event_handler); 96 event_handler_ = event_handler; 97} 98 99ErrorCode JingleSession::error() { 100 DCHECK(CalledOnValidThread()); 101 return error_; 102} 103 104void 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); 111 112 peer_jid_ = peer_jid; 113 authenticator_ = authenticator.Pass(); 114 candidate_config_ = config.Pass(); 115 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)); 121 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); 130 131 SetState(CONNECTING); 132} 133 134void 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); 141 142 peer_jid_ = initiate_message.from; 143 authenticator_ = authenticator.Pass(); 144 session_id_ = initiate_message.sid; 145 candidate_config_ = initiate_message.description->config()->Clone(); 146 147 SetState(ACCEPTING); 148} 149 150void JingleSession::AcceptIncomingConnection( 151 const JingleMessage& initiate_message) { 152 DCHECK(config_is_set_); 153 154 // Process the first authentication message. 155 const buzz::XmlElement* first_auth_message = 156 initiate_message.description->authenticator_message(); 157 158 if (!first_auth_message) { 159 CloseInternal(INCOMPATIBLE_PROTOCOL); 160 return; 161 } 162 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))); 168} 169 170void 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; 176 } 177 178 // Send the session-accept message. 179 JingleMessage message(peer_jid_, JingleMessage::SESSION_ACCEPT, 180 session_id_); 181 182 scoped_ptr<buzz::XmlElement> auth_message; 183 if (authenticator_->state() == Authenticator::MESSAGE_READY) 184 auth_message = authenticator_->GetNextMessage(); 185 186 message.description.reset( 187 new ContentDescription(CandidateSessionConfig::CreateFrom(config_), 188 auth_message.Pass())); 189 SendMessage(message); 190 191 // Update state. 192 SetState(CONNECTED); 193 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); 200 } 201 } 202} 203 204const std::string& JingleSession::jid() { 205 DCHECK(CalledOnValidThread()); 206 return peer_jid_; 207} 208 209const CandidateSessionConfig* JingleSession::candidate_config() { 210 DCHECK(CalledOnValidThread()); 211 return candidate_config_.get(); 212} 213 214const SessionConfig& JingleSession::config() { 215 DCHECK(CalledOnValidThread()); 216 return config_; 217} 218 219void JingleSession::set_config(const SessionConfig& config) { 220 DCHECK(CalledOnValidThread()); 221 DCHECK(!config_is_set_); 222 config_ = config; 223 config_is_set_ = true; 224} 225 226StreamChannelFactory* JingleSession::GetTransportChannelFactory() { 227 DCHECK(CalledOnValidThread()); 228 return secure_channel_factory_.get(); 229} 230 231StreamChannelFactory* JingleSession::GetMultiplexedChannelFactory() { 232 DCHECK(CalledOnValidThread()); 233 if (!channel_multiplexer_.get()) { 234 channel_multiplexer_.reset( 235 new ChannelMultiplexer(GetTransportChannelFactory(), kMuxChannelName)); 236 } 237 return channel_multiplexer_.get(); 238} 239 240void JingleSession::Close() { 241 DCHECK(CalledOnValidThread()); 242 243 CloseInternal(OK); 244} 245 246void 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; 256 } 257 } 258} 259 260void JingleSession::CreateChannel(const std::string& name, 261 const ChannelCreatedCallback& callback) { 262 DCHECK(!channels_[name]); 263 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(); 269} 270 271void 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()); 277 } 278} 279 280void JingleSession::OnTransportCandidate(Transport* transport, 281 const cricket::Candidate& candidate) { 282 pending_candidates_.push_back(JingleMessage::NamedCandidate( 283 transport->name(), candidate)); 284 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); 291 } 292} 293 294void JingleSession::OnTransportRouteChange(Transport* transport, 295 const TransportRoute& route) { 296 if (event_handler_) 297 event_handler_->OnSessionRouteChange(transport->name(), route); 298} 299 300void JingleSession::OnTransportFailed(Transport* transport) { 301 CloseInternal(CHANNEL_CONNECTION_ERROR); 302} 303 304void JingleSession::OnTransportDeleted(Transport* transport) { 305 ChannelsMap::iterator it = channels_.find(transport->name()); 306 DCHECK_EQ(it->second, transport); 307 channels_.erase(it); 308} 309 310void 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)); 316 317 int timeout = kDefaultMessageTimeout; 318 if (message.action == JingleMessage::SESSION_INITIATE || 319 message.action == JingleMessage::SESSION_ACCEPT) { 320 timeout = kSessionInitiateAndAcceptTimeout; 321 } 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"; 328 } 329} 330 331void 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; 338 339 // Ignore all responses after session was closed. 340 if (state_ == CLOSED || state_ == FAILED) 341 return; 342 343 std::string type_str = JingleMessage::GetActionName(request_type); 344 345 // |response| will be NULL 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."; 357 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); 361 } 362 } 363} 364 365void JingleSession::SendTransportInfo() { 366 JingleMessage message(peer_jid_, JingleMessage::TRANSPORT_INFO, session_id_); 367 message.candidates.swap(pending_candidates_); 368 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"; 378 } 379} 380 381void JingleSession::OnTransportInfoResponse(IqRequest* request, 382 const buzz::XmlElement* response) { 383 DCHECK(!transport_info_requests_.empty()); 384 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(); 390 } 391 392 // Delete the |request| itself. 393 DCHECK_EQ(request, transport_info_requests_.front()); 394 delete request; 395 transport_info_requests_.pop_front(); 396 397 // Ignore transport-info timeouts. 398 if (!response) { 399 LOG(ERROR) << "transport-info request has timed out."; 400 return; 401 } 402 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); 408 } 409} 410 411void JingleSession::OnIncomingMessage(const JingleMessage& message, 412 const ReplyCallback& reply_callback) { 413 DCHECK(CalledOnValidThread()); 414 415 if (message.from != peer_jid_) { 416 // Ignore messages received from a different Jid. 417 reply_callback.Run(JingleMessageReply::INVALID_SID); 418 return; 419 } 420 421 switch (message.action) { 422 case JingleMessage::SESSION_ACCEPT: 423 OnAccept(message, reply_callback); 424 break; 425 426 case JingleMessage::SESSION_INFO: 427 OnSessionInfo(message, reply_callback); 428 break; 429 430 case JingleMessage::TRANSPORT_INFO: 431 reply_callback.Run(JingleMessageReply::NONE); 432 ProcessTransportInfo(message); 433 break; 434 435 case JingleMessage::SESSION_TERMINATE: 436 OnTerminate(message, reply_callback); 437 break; 438 439 default: 440 reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST); 441 } 442} 443 444void JingleSession::OnAccept(const JingleMessage& message, 445 const ReplyCallback& reply_callback) { 446 if (state_ != CONNECTING) { 447 reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST); 448 return; 449 } 450 451 reply_callback.Run(JingleMessageReply::NONE); 452 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; 459 } 460 461 if (!InitializeConfigFromDescription(message.description.get())) { 462 CloseInternal(INCOMPATIBLE_PROTOCOL); 463 return; 464 } 465 466 // In case there is transport information in the accept message. 467 ProcessTransportInfo(message); 468 469 SetState(CONNECTED); 470 471 DCHECK(authenticator_->state() == Authenticator::WAITING_MESSAGE); 472 authenticator_->ProcessMessage(auth_message, base::Bind( 473 &JingleSession::ProcessAuthenticationStep,base::Unretained(this))); 474} 475 476void 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; 482 } 483 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; 491 } 492 493 reply_callback.Run(JingleMessageReply::NONE); 494 495 authenticator_->ProcessMessage(message.info.get(), base::Bind( 496 &JingleSession::ProcessAuthenticationStep, base::Unretained(this))); 497} 498 499void 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); 510 } 511 } 512} 513 514void 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; 520 } 521 522 reply_callback.Run(JingleMessageReply::NONE); 523 524 switch (message.reason) { 525 case JingleMessage::SUCCESS: 526 if (state_ == CONNECTING) { 527 error_ = SESSION_REJECTED; 528 } else { 529 error_ = OK; 530 } 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; 546 } 547 548 if (error_ != OK) { 549 SetState(FAILED); 550 } else { 551 SetState(CLOSED); 552 } 553} 554 555bool JingleSession::InitializeConfigFromDescription( 556 const ContentDescription* description) { 557 DCHECK(description); 558 559 if (!description->config()->GetFinalConfig(&config_)) { 560 LOG(ERROR) << "session-accept does not specify configuration"; 561 return false; 562 } 563 if (!candidate_config()->IsSupported(config_)) { 564 LOG(ERROR) << "session-accept specifies an invalid configuration"; 565 return false; 566 } 567 568 return true; 569} 570 571void JingleSession::ProcessAuthenticationStep() { 572 DCHECK(CalledOnValidThread()); 573 DCHECK_NE(authenticator_->state(), Authenticator::PROCESSING_MESSAGE); 574 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; 580 } 581 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); 587 } 588 DCHECK_NE(authenticator_->state(), Authenticator::MESSAGE_READY); 589 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())); 598 599 if (authenticator_->started()) { 600 SetState(AUTHENTICATING); 601 } 602} 603 604void JingleSession::ContinueAuthenticationStep() { 605 if (authenticator_->state() == Authenticator::ACCEPTED) { 606 OnAuthenticated(); 607 } else if (authenticator_->state() == Authenticator::REJECTED) { 608 CloseInternal(AuthRejectionReasonToErrorCode( 609 authenticator_->rejection_reason())); 610 } 611} 612 613void 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())); 618 619 SetState(AUTHENTICATED); 620} 621 622void JingleSession::CloseInternal(ErrorCode error) { 623 DCHECK(CalledOnValidThread()); 624 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; 644 } 645 646 JingleMessage message(peer_jid_, JingleMessage::SESSION_TERMINATE, 647 session_id_); 648 message.reason = reason; 649 SendMessage(message); 650 } 651 652 error_ = error; 653 654 if (state_ != FAILED && state_ != CLOSED) { 655 if (error != OK) { 656 SetState(FAILED); 657 } else { 658 SetState(CLOSED); 659 } 660 } 661} 662 663void JingleSession::SetState(State new_state) { 664 DCHECK(CalledOnValidThread()); 665 666 if (new_state != state_) { 667 DCHECK_NE(state_, CLOSED); 668 DCHECK_NE(state_, FAILED); 669 670 state_ = new_state; 671 if (event_handler_) 672 event_handler_->OnSessionStateChange(new_state); 673 } 674} 675 676bool JingleSession::is_session_active() { 677 return state_ == CONNECTING || state_ == ACCEPTING || state_ == CONNECTED || 678 state_ == AUTHENTICATING || state_ == AUTHENTICATED; 679} 680 681} // namespace protocol 682} // namespace remoting 683