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/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" 23 24 using buzz::XmlElement; 25 26 namespace remoting { 27 namespace protocol { 28 29 namespace { 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 34 // process. 35 const int kTransportInfoSendDelayMs = 2; 36 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; 40 41 // Timeout for the transport-info messages. 42 const int kTransportInfoTimeout = 10 * 60; 43 44 // Name of the multiplexed channel. 45 const char kMuxChannelName[] = "mux"; 46 47 ErrorCode AuthRejectionReasonToErrorCode( 48 Authenticator::RejectionReason reason) { 49 switch (reason) { 50 case Authenticator::INVALID_CREDENTIALS: 51 return AUTHENTICATION_FAILED; 52 case Authenticator::PROTOCOL_ERROR: 53 return INCOMPATIBLE_PROTOCOL; 54 } 55 NOTREACHED(); 56 return UNKNOWN_ERROR; 57 } 58 59 } // namespace 60 61 JingleSession::JingleSession(JingleSessionManager* session_manager) 62 : session_manager_(session_manager), 63 event_handler_(NULL), 64 state_(INITIALIZING), 65 error_(OK), 66 config_is_set_(false) { 67 } 68 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); 77 } 78 79 void JingleSession::SetEventHandler(Session::EventHandler* event_handler) { 80 DCHECK(CalledOnValidThread()); 81 DCHECK(event_handler); 82 event_handler_ = event_handler; 83 } 84 85 ErrorCode JingleSession::error() { 86 DCHECK(CalledOnValidThread()); 87 return error_; 88 } 89 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); 97 98 peer_jid_ = peer_jid; 99 authenticator_ = authenticator.Pass(); 100 candidate_config_ = config.Pass(); 101 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)); 107 108 // Send session-initiate message. 109 JingleMessage message(peer_jid_, JingleMessage::SESSION_INITIATE, 110 session_id_); 111 message.initiator = session_manager_->signal_strategy_->GetLocalJid(); 112 message.description.reset( 113 new ContentDescription(candidate_config_->Clone(), 114 authenticator_->GetNextMessage())); 115 SendMessage(message); 116 117 SetState(CONNECTING); 118 } 119 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); 127 128 peer_jid_ = initiate_message.from; 129 authenticator_ = authenticator.Pass(); 130 session_id_ = initiate_message.sid; 131 candidate_config_ = initiate_message.description->config()->Clone(); 132 133 SetState(ACCEPTING); 134 } 135 136 void JingleSession::AcceptIncomingConnection( 137 const JingleMessage& initiate_message) { 138 DCHECK(config_is_set_); 139 140 // Process the first authentication message. 141 const buzz::XmlElement* first_auth_message = 142 initiate_message.description->authenticator_message(); 143 144 if (!first_auth_message) { 145 CloseInternal(INCOMPATIBLE_PROTOCOL); 146 return; 147 } 148 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))); 154 } 155 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())); 161 return; 162 } 163 164 // Send the session-accept message. 165 JingleMessage message(peer_jid_, JingleMessage::SESSION_ACCEPT, 166 session_id_); 167 168 scoped_ptr<buzz::XmlElement> auth_message; 169 if (authenticator_->state() == Authenticator::MESSAGE_READY) 170 auth_message = authenticator_->GetNextMessage(); 171 172 message.description.reset( 173 new ContentDescription(CandidateSessionConfig::CreateFrom(config_), 174 auth_message.Pass())); 175 SendMessage(message); 176 177 // Update state. 178 SetState(CONNECTED); 179 180 if (authenticator_->state() == Authenticator::ACCEPTED) { 181 SetState(AUTHENTICATED); 182 } else { 183 DCHECK_EQ(authenticator_->state(), Authenticator::WAITING_MESSAGE); 184 } 185 186 return; 187 } 188 189 const std::string& JingleSession::jid() { 190 DCHECK(CalledOnValidThread()); 191 return peer_jid_; 192 } 193 194 const CandidateSessionConfig* JingleSession::candidate_config() { 195 DCHECK(CalledOnValidThread()); 196 return candidate_config_.get(); 197 } 198 199 const SessionConfig& JingleSession::config() { 200 DCHECK(CalledOnValidThread()); 201 return config_; 202 } 203 204 void JingleSession::set_config(const SessionConfig& config) { 205 DCHECK(CalledOnValidThread()); 206 DCHECK(!config_is_set_); 207 config_ = config; 208 config_is_set_ = true; 209 } 210 211 ChannelFactory* JingleSession::GetTransportChannelFactory() { 212 DCHECK(CalledOnValidThread()); 213 return this; 214 } 215 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(); 221 } 222 223 void JingleSession::Close() { 224 DCHECK(CalledOnValidThread()); 225 226 CloseInternal(OK); 227 } 228 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); 237 } else { 238 ++it; 239 } 240 } 241 } 242 243 void JingleSession::CreateStreamChannel( 244 const std::string& name, 245 const StreamChannelCallback& callback) { 246 DCHECK(!channels_[name]); 247 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(); 256 } 257 258 void JingleSession::CreateDatagramChannel( 259 const std::string& name, 260 const DatagramChannelCallback& callback) { 261 DCHECK(!channels_[name]); 262 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(); 271 } 272 273 void JingleSession::CancelChannelCreation(const std::string& name) { 274 ChannelsMap::iterator it = channels_.find(name); 275 if (it != channels_.end() && !it->second->is_connected()) { 276 delete it->second; 277 DCHECK(!channels_[name]); 278 } 279 } 280 281 void JingleSession::OnTransportCandidate(Transport* transport, 282 const cricket::Candidate& candidate) { 283 pending_candidates_.push_back(JingleMessage::NamedCandidate( 284 transport->name(), candidate)); 285 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); 292 } 293 } 294 295 void JingleSession::OnTransportRouteChange(Transport* transport, 296 const TransportRoute& route) { 297 if (event_handler_) 298 event_handler_->OnSessionRouteChange(transport->name(), route); 299 } 300 301 void JingleSession::OnTransportReady(Transport* transport, bool ready) { 302 if (event_handler_) 303 event_handler_->OnSessionChannelReady(transport->name(), ready); 304 } 305 306 void JingleSession::OnTransportFailed(Transport* transport) { 307 CloseInternal(CHANNEL_CONNECTION_ERROR); 308 } 309 310 void JingleSession::OnTransportDeleted(Transport* transport) { 311 ChannelsMap::iterator it = channels_.find(transport->name()); 312 DCHECK_EQ(it->second, transport); 313 channels_.erase(it); 314 } 315 316 void JingleSession::SendMessage(const JingleMessage& message) { 317 scoped_ptr<IqRequest> request = session_manager_->iq_sender()->SendIq( 318 message.ToXml(), 319 base::Bind(&JingleSession::OnMessageResponse, 320 base::Unretained(this), message.action)); 321 if (request) { 322 request->SetTimeout(base::TimeDelta::FromSeconds(kDefaultMessageTimeout)); 323 pending_requests_.insert(request.release()); 324 } else { 325 LOG(ERROR) << "Failed to send a " 326 << JingleMessage::GetActionName(message.action) << " message"; 327 } 328 } 329 330 void JingleSession::OnMessageResponse( 331 JingleMessage::ActionType request_type, 332 IqRequest* request, 333 const buzz::XmlElement* response) { 334 std::string type_str = JingleMessage::GetActionName(request_type); 335 336 // Delete the request from the list of pending requests. 337 pending_requests_.erase(request); 338 delete request; 339 340 // |response| will be NULL if the request timed out. 341 if (!response) { 342 LOG(ERROR) << type_str << " request timed out."; 343 CloseInternal(SIGNALING_TIMEOUT); 344 return; 345 } else { 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."; 352 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); 358 break; 359 360 default: 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); 364 } 365 } 366 } 367 } 368 369 void JingleSession::SendTransportInfo() { 370 JingleMessage message(peer_jid_, JingleMessage::TRANSPORT_INFO, session_id_); 371 message.candidates.swap(pending_candidates_); 372 373 scoped_ptr<IqRequest> request = session_manager_->iq_sender()->SendIq( 374 message.ToXml(), 375 base::Bind(&JingleSession::OnTransportInfoResponse, 376 base::Unretained(this))); 377 if (request) { 378 request->SetTimeout(base::TimeDelta::FromSeconds(kTransportInfoTimeout)); 379 transport_info_requests_.push_back(request.release()); 380 } else { 381 LOG(ERROR) << "Failed to send a transport-info message"; 382 } 383 } 384 385 void JingleSession::OnTransportInfoResponse(IqRequest* request, 386 const buzz::XmlElement* response) { 387 DCHECK(!transport_info_requests_.empty()); 388 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(); 394 } 395 396 // Delete the |request| itself. 397 DCHECK_EQ(request, transport_info_requests_.front()); 398 delete request; 399 transport_info_requests_.pop_front(); 400 401 // Ignore transport-info timeouts. 402 if (!response) { 403 LOG(ERROR) << "transport-info request has timed out."; 404 return; 405 } 406 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); 412 } 413 } 414 415 void JingleSession::OnIncomingMessage(const JingleMessage& message, 416 const ReplyCallback& reply_callback) { 417 DCHECK(CalledOnValidThread()); 418 419 if (message.from != peer_jid_) { 420 // Ignore messages received from a different Jid. 421 reply_callback.Run(JingleMessageReply::INVALID_SID); 422 return; 423 } 424 425 switch (message.action) { 426 case JingleMessage::SESSION_ACCEPT: 427 OnAccept(message, reply_callback); 428 break; 429 430 case JingleMessage::SESSION_INFO: 431 OnSessionInfo(message, reply_callback); 432 break; 433 434 case JingleMessage::TRANSPORT_INFO: 435 reply_callback.Run(JingleMessageReply::NONE); 436 ProcessTransportInfo(message); 437 break; 438 439 case JingleMessage::SESSION_TERMINATE: 440 OnTerminate(message, reply_callback); 441 break; 442 443 default: 444 reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST); 445 } 446 } 447 448 void JingleSession::OnAccept(const JingleMessage& message, 449 const ReplyCallback& reply_callback) { 450 if (state_ != CONNECTING) { 451 reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST); 452 return; 453 } 454 455 reply_callback.Run(JingleMessageReply::NONE); 456 457 const buzz::XmlElement* auth_message = 458 message.description->authenticator_message(); 459 if (!auth_message) { 460 DLOG(WARNING) << "Received session-accept without authentication message "; 461 CloseInternal(INCOMPATIBLE_PROTOCOL); 462 return; 463 } 464 465 if (!InitializeConfigFromDescription(message.description.get())) { 466 CloseInternal(INCOMPATIBLE_PROTOCOL); 467 return; 468 } 469 470 // In case there is transport information in the accept message. 471 ProcessTransportInfo(message); 472 473 SetState(CONNECTED); 474 475 DCHECK(authenticator_->state() == Authenticator::WAITING_MESSAGE); 476 authenticator_->ProcessMessage(auth_message, base::Bind( 477 &JingleSession::ProcessAuthenticationStep,base::Unretained(this))); 478 } 479 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); 485 return; 486 } 487 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); 494 return; 495 } 496 497 reply_callback.Run(JingleMessageReply::NONE); 498 499 authenticator_->ProcessMessage(message.info.get(), base::Bind( 500 &JingleSession::ProcessAuthenticationStep, base::Unretained(this))); 501 } 502 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); 510 } else { 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); 514 } 515 } 516 } 517 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); 524 return; 525 } 526 527 reply_callback.Run(JingleMessageReply::NONE); 528 529 switch (message.reason) { 530 case JingleMessage::SUCCESS: 531 if (state_ == CONNECTING) { 532 error_ = SESSION_REJECTED; 533 } else { 534 error_ = OK; 535 } 536 break; 537 case JingleMessage::DECLINE: 538 error_ = AUTHENTICATION_FAILED; 539 break; 540 case JingleMessage::CANCEL: 541 error_ = HOST_OVERLOAD; 542 break; 543 case JingleMessage::GENERAL_ERROR: 544 error_ = CHANNEL_CONNECTION_ERROR; 545 break; 546 case JingleMessage::INCOMPATIBLE_PARAMETERS: 547 error_ = INCOMPATIBLE_PROTOCOL; 548 break; 549 default: 550 error_ = UNKNOWN_ERROR; 551 } 552 553 if (error_ != OK) { 554 SetState(FAILED); 555 } else { 556 SetState(CLOSED); 557 } 558 } 559 560 bool JingleSession::InitializeConfigFromDescription( 561 const ContentDescription* description) { 562 DCHECK(description); 563 564 if (!description->config()->GetFinalConfig(&config_)) { 565 LOG(ERROR) << "session-accept does not specify configuration"; 566 return false; 567 } 568 if (!candidate_config()->IsSupported(config_)) { 569 LOG(ERROR) << "session-accept specifies an invalid configuration"; 570 return false; 571 } 572 573 return true; 574 } 575 576 void JingleSession::ProcessAuthenticationStep() { 577 DCHECK(CalledOnValidThread()); 578 DCHECK_NE(authenticator_->state(), Authenticator::PROCESSING_MESSAGE); 579 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. 584 return; 585 } 586 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); 592 } 593 DCHECK_NE(authenticator_->state(), Authenticator::MESSAGE_READY); 594 595 if (authenticator_->state() == Authenticator::ACCEPTED) { 596 SetState(AUTHENTICATED); 597 } else if (authenticator_->state() == Authenticator::REJECTED) { 598 CloseInternal(AuthRejectionReasonToErrorCode( 599 authenticator_->rejection_reason())); 600 } 601 } 602 603 void JingleSession::CloseInternal(ErrorCode error) { 604 DCHECK(CalledOnValidThread()); 605 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; 610 switch (error) { 611 case OK: 612 reason = JingleMessage::SUCCESS; 613 break; 614 case SESSION_REJECTED: 615 case AUTHENTICATION_FAILED: 616 reason = JingleMessage::DECLINE; 617 break; 618 case INCOMPATIBLE_PROTOCOL: 619 reason = JingleMessage::INCOMPATIBLE_PARAMETERS; 620 break; 621 case HOST_OVERLOAD: 622 reason = JingleMessage::CANCEL; 623 break; 624 default: 625 reason = JingleMessage::GENERAL_ERROR; 626 } 627 628 JingleMessage message(peer_jid_, JingleMessage::SESSION_TERMINATE, 629 session_id_); 630 message.reason = reason; 631 SendMessage(message); 632 } 633 634 error_ = error; 635 636 if (state_ != FAILED && state_ != CLOSED) { 637 if (error != OK) { 638 SetState(FAILED); 639 } else { 640 SetState(CLOSED); 641 } 642 } 643 } 644 645 void JingleSession::SetState(State new_state) { 646 DCHECK(CalledOnValidThread()); 647 648 if (new_state != state_) { 649 DCHECK_NE(state_, CLOSED); 650 DCHECK_NE(state_, FAILED); 651 652 state_ = new_state; 653 if (event_handler_) 654 event_handler_->OnSessionStateChange(new_state); 655 } 656 } 657 658 } // namespace protocol 659 } // namespace remoting 660