jingle_session.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
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 24using buzz::XmlElement; 25 26namespace remoting { 27namespace protocol { 28 29namespace { 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. 35const 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|. 39const int kDefaultMessageTimeout = 10; 40 41// Timeout for the transport-info messages. 42const int kTransportInfoTimeout = 10 * 60; 43 44// Name of the multiplexed channel. 45const char kMuxChannelName[] = "mux"; 46 47ErrorCode 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 61JingleSession::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 69JingleSession::~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 79void JingleSession::SetEventHandler(Session::EventHandler* event_handler) { 80 DCHECK(CalledOnValidThread()); 81 DCHECK(event_handler); 82 event_handler_ = event_handler; 83} 84 85ErrorCode JingleSession::error() { 86 DCHECK(CalledOnValidThread()); 87 return error_; 88} 89 90void 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 120void 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 136void 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 156void 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 189const std::string& JingleSession::jid() { 190 DCHECK(CalledOnValidThread()); 191 return peer_jid_; 192} 193 194const CandidateSessionConfig* JingleSession::candidate_config() { 195 DCHECK(CalledOnValidThread()); 196 return candidate_config_.get(); 197} 198 199const SessionConfig& JingleSession::config() { 200 DCHECK(CalledOnValidThread()); 201 return config_; 202} 203 204void JingleSession::set_config(const SessionConfig& config) { 205 DCHECK(CalledOnValidThread()); 206 DCHECK(!config_is_set_); 207 config_ = config; 208 config_is_set_ = true; 209} 210 211ChannelFactory* JingleSession::GetTransportChannelFactory() { 212 DCHECK(CalledOnValidThread()); 213 return this; 214} 215 216ChannelFactory* 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 223void JingleSession::Close() { 224 DCHECK(CalledOnValidThread()); 225 226 CloseInternal(OK); 227} 228 229void JingleSession::CreateStreamChannel( 230 const std::string& name, 231 const StreamChannelCallback& callback) { 232 DCHECK(!channels_[name]); 233 234 scoped_ptr<ChannelAuthenticator> channel_authenticator = 235 authenticator_->CreateChannelAuthenticator(); 236 scoped_ptr<StreamTransport> channel = 237 session_manager_->transport_factory_->CreateStreamTransport(); 238 channel->Initialize(name, this, channel_authenticator.Pass()); 239 channel->Connect(callback); 240 channels_[name] = channel.release(); 241} 242 243void JingleSession::CreateDatagramChannel( 244 const std::string& name, 245 const DatagramChannelCallback& callback) { 246 DCHECK(!channels_[name]); 247 248 scoped_ptr<ChannelAuthenticator> channel_authenticator = 249 authenticator_->CreateChannelAuthenticator(); 250 scoped_ptr<DatagramTransport> channel = 251 session_manager_->transport_factory_->CreateDatagramTransport(); 252 channel->Initialize(name, this, channel_authenticator.Pass()); 253 channel->Connect(callback); 254 channels_[name] = channel.release(); 255} 256 257void JingleSession::CancelChannelCreation(const std::string& name) { 258 ChannelsMap::iterator it = channels_.find(name); 259 if (it != channels_.end() && !it->second->is_connected()) { 260 delete it->second; 261 DCHECK(!channels_[name]); 262 } 263} 264 265void JingleSession::OnTransportCandidate(Transport* transport, 266 const cricket::Candidate& candidate) { 267 pending_candidates_.push_back(JingleMessage::NamedCandidate( 268 transport->name(), candidate)); 269 270 if (!transport_infos_timer_.IsRunning()) { 271 // Delay sending the new candidates in case we get more candidates 272 // that we can send in one message. 273 transport_infos_timer_.Start( 274 FROM_HERE, base::TimeDelta::FromMilliseconds(kTransportInfoSendDelayMs), 275 this, &JingleSession::SendTransportInfo); 276 } 277} 278 279void JingleSession::OnTransportRouteChange(Transport* transport, 280 const TransportRoute& route) { 281 if (event_handler_) 282 event_handler_->OnSessionRouteChange(transport->name(), route); 283} 284 285void JingleSession::OnTransportReady(Transport* transport, bool ready) { 286 if (event_handler_) 287 event_handler_->OnSessionChannelReady(transport->name(), ready); 288} 289 290void JingleSession::OnTransportFailed(Transport* transport) { 291 CloseInternal(CHANNEL_CONNECTION_ERROR); 292} 293 294void JingleSession::OnTransportDeleted(Transport* transport) { 295 ChannelsMap::iterator it = channels_.find(transport->name()); 296 DCHECK_EQ(it->second, transport); 297 channels_.erase(it); 298} 299 300void JingleSession::SendMessage(const JingleMessage& message) { 301 scoped_ptr<IqRequest> request = session_manager_->iq_sender()->SendIq( 302 message.ToXml(), 303 base::Bind(&JingleSession::OnMessageResponse, 304 base::Unretained(this), message.action)); 305 if (request) { 306 request->SetTimeout(base::TimeDelta::FromSeconds(kDefaultMessageTimeout)); 307 pending_requests_.insert(request.release()); 308 } else { 309 LOG(ERROR) << "Failed to send a " 310 << JingleMessage::GetActionName(message.action) << " message"; 311 } 312} 313 314void JingleSession::OnMessageResponse( 315 JingleMessage::ActionType request_type, 316 IqRequest* request, 317 const buzz::XmlElement* response) { 318 std::string type_str = JingleMessage::GetActionName(request_type); 319 320 // Delete the request from the list of pending requests. 321 pending_requests_.erase(request); 322 delete request; 323 324 // |response| will be NULL if the request timed out. 325 if (!response) { 326 LOG(ERROR) << type_str << " request timed out."; 327 CloseInternal(SIGNALING_TIMEOUT); 328 return; 329 } else { 330 const std::string& type = 331 response->Attr(buzz::QName(std::string(), "type")); 332 if (type != "result") { 333 LOG(ERROR) << "Received error in response to " << type_str 334 << " message: \"" << response->Str() 335 << "\". Terminating the session."; 336 337 switch (request_type) { 338 case JingleMessage::SESSION_INFO: 339 // session-info is used for the new authentication protocol, 340 // and wasn't previously supported. 341 CloseInternal(INCOMPATIBLE_PROTOCOL); 342 break; 343 344 default: 345 // TODO(sergeyu): There may be different reasons for error 346 // here. Parse the response stanza to find failure reason. 347 CloseInternal(PEER_IS_OFFLINE); 348 } 349 } 350 } 351} 352 353void JingleSession::SendTransportInfo() { 354 JingleMessage message(peer_jid_, JingleMessage::TRANSPORT_INFO, session_id_); 355 message.candidates.swap(pending_candidates_); 356 357 scoped_ptr<IqRequest> request = session_manager_->iq_sender()->SendIq( 358 message.ToXml(), 359 base::Bind(&JingleSession::OnTransportInfoResponse, 360 base::Unretained(this))); 361 if (request) { 362 request->SetTimeout(base::TimeDelta::FromSeconds(kTransportInfoTimeout)); 363 transport_info_requests_.push_back(request.release()); 364 } else { 365 LOG(ERROR) << "Failed to send a transport-info message"; 366 } 367} 368 369void JingleSession::OnTransportInfoResponse(IqRequest* request, 370 const buzz::XmlElement* response) { 371 DCHECK(!transport_info_requests_.empty()); 372 373 // Consider transport-info requests sent before this one lost and delete 374 // corresponding IqRequest objects. 375 while (transport_info_requests_.front() != request) { 376 delete transport_info_requests_.front(); 377 transport_info_requests_.pop_front(); 378 } 379 380 // Delete the |request| itself. 381 DCHECK_EQ(request, transport_info_requests_.front()); 382 delete request; 383 transport_info_requests_.pop_front(); 384 385 // Ignore transport-info timeouts. 386 if (!response) { 387 LOG(ERROR) << "transport-info request has timed out."; 388 return; 389 } 390 391 const std::string& type = response->Attr(buzz::QName(std::string(), "type")); 392 if (type != "result") { 393 LOG(ERROR) << "Received error in response to transport-info message: \"" 394 << response->Str() << "\". Terminating the session."; 395 CloseInternal(PEER_IS_OFFLINE); 396 } 397} 398 399void JingleSession::OnIncomingMessage(const JingleMessage& message, 400 const ReplyCallback& reply_callback) { 401 DCHECK(CalledOnValidThread()); 402 403 if (message.from != peer_jid_) { 404 // Ignore messages received from a different Jid. 405 reply_callback.Run(JingleMessageReply::INVALID_SID); 406 return; 407 } 408 409 switch (message.action) { 410 case JingleMessage::SESSION_ACCEPT: 411 OnAccept(message, reply_callback); 412 break; 413 414 case JingleMessage::SESSION_INFO: 415 OnSessionInfo(message, reply_callback); 416 break; 417 418 case JingleMessage::TRANSPORT_INFO: 419 reply_callback.Run(JingleMessageReply::NONE); 420 ProcessTransportInfo(message); 421 break; 422 423 case JingleMessage::SESSION_TERMINATE: 424 OnTerminate(message, reply_callback); 425 break; 426 427 default: 428 reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST); 429 } 430} 431 432void JingleSession::OnAccept(const JingleMessage& message, 433 const ReplyCallback& reply_callback) { 434 if (state_ != CONNECTING) { 435 reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST); 436 return; 437 } 438 439 reply_callback.Run(JingleMessageReply::NONE); 440 441 const buzz::XmlElement* auth_message = 442 message.description->authenticator_message(); 443 if (!auth_message) { 444 DLOG(WARNING) << "Received session-accept without authentication message "; 445 CloseInternal(INCOMPATIBLE_PROTOCOL); 446 return; 447 } 448 449 if (!InitializeConfigFromDescription(message.description.get())) { 450 CloseInternal(INCOMPATIBLE_PROTOCOL); 451 return; 452 } 453 454 // In case there is transport information in the accept message. 455 ProcessTransportInfo(message); 456 457 SetState(CONNECTED); 458 459 DCHECK(authenticator_->state() == Authenticator::WAITING_MESSAGE); 460 authenticator_->ProcessMessage(auth_message, base::Bind( 461 &JingleSession::ProcessAuthenticationStep,base::Unretained(this))); 462} 463 464void JingleSession::OnSessionInfo(const JingleMessage& message, 465 const ReplyCallback& reply_callback) { 466 if (!message.info.get() || 467 !Authenticator::IsAuthenticatorMessage(message.info.get())) { 468 reply_callback.Run(JingleMessageReply::UNSUPPORTED_INFO); 469 return; 470 } 471 472 if (state_ != CONNECTED || 473 authenticator_->state() != Authenticator::WAITING_MESSAGE) { 474 LOG(WARNING) << "Received unexpected authenticator message " 475 << message.info->Str(); 476 reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST); 477 CloseInternal(INCOMPATIBLE_PROTOCOL); 478 return; 479 } 480 481 reply_callback.Run(JingleMessageReply::NONE); 482 483 authenticator_->ProcessMessage(message.info.get(), base::Bind( 484 &JingleSession::ProcessAuthenticationStep, base::Unretained(this))); 485} 486 487void JingleSession::ProcessTransportInfo(const JingleMessage& message) { 488 for (std::list<JingleMessage::NamedCandidate>::const_iterator it = 489 message.candidates.begin(); 490 it != message.candidates.end(); ++it) { 491 ChannelsMap::iterator channel = channels_.find(it->name); 492 if (channel == channels_.end()) { 493 LOG(WARNING) << "Received candidate for unknown channel " << it->name; 494 continue; 495 } 496 channel->second->AddRemoteCandidate(it->candidate); 497 } 498} 499 500void JingleSession::OnTerminate(const JingleMessage& message, 501 const ReplyCallback& reply_callback) { 502 if (state_ != CONNECTING && state_ != ACCEPTING && state_ != CONNECTED && 503 state_ != AUTHENTICATED) { 504 LOG(WARNING) << "Received unexpected session-terminate message."; 505 reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST); 506 return; 507 } 508 509 reply_callback.Run(JingleMessageReply::NONE); 510 511 switch (message.reason) { 512 case JingleMessage::SUCCESS: 513 if (state_ == CONNECTING) { 514 error_ = SESSION_REJECTED; 515 } else { 516 error_ = OK; 517 } 518 break; 519 case JingleMessage::DECLINE: 520 error_ = AUTHENTICATION_FAILED; 521 break; 522 case JingleMessage::CANCEL: 523 error_ = HOST_OVERLOAD; 524 break; 525 case JingleMessage::GENERAL_ERROR: 526 error_ = CHANNEL_CONNECTION_ERROR; 527 break; 528 case JingleMessage::INCOMPATIBLE_PARAMETERS: 529 error_ = INCOMPATIBLE_PROTOCOL; 530 break; 531 default: 532 error_ = UNKNOWN_ERROR; 533 } 534 535 if (error_ != OK) { 536 SetState(FAILED); 537 } else { 538 SetState(CLOSED); 539 } 540} 541 542bool JingleSession::InitializeConfigFromDescription( 543 const ContentDescription* description) { 544 DCHECK(description); 545 546 if (!description->config()->GetFinalConfig(&config_)) { 547 LOG(ERROR) << "session-accept does not specify configuration"; 548 return false; 549 } 550 if (!candidate_config()->IsSupported(config_)) { 551 LOG(ERROR) << "session-accept specifies an invalid configuration"; 552 return false; 553 } 554 555 return true; 556} 557 558void JingleSession::ProcessAuthenticationStep() { 559 DCHECK(CalledOnValidThread()); 560 DCHECK_NE(authenticator_->state(), Authenticator::PROCESSING_MESSAGE); 561 562 if (state_ != CONNECTED) { 563 DCHECK(state_ == FAILED || state_ == CLOSED); 564 // The remote host closed the connection while the authentication was being 565 // processed asynchronously, nothing to do. 566 return; 567 } 568 569 if (authenticator_->state() == Authenticator::MESSAGE_READY) { 570 JingleMessage message(peer_jid_, JingleMessage::SESSION_INFO, session_id_); 571 message.info = authenticator_->GetNextMessage(); 572 DCHECK(message.info.get()); 573 SendMessage(message); 574 } 575 DCHECK_NE(authenticator_->state(), Authenticator::MESSAGE_READY); 576 577 if (authenticator_->state() == Authenticator::ACCEPTED) { 578 SetState(AUTHENTICATED); 579 } else if (authenticator_->state() == Authenticator::REJECTED) { 580 CloseInternal(AuthRejectionReasonToErrorCode( 581 authenticator_->rejection_reason())); 582 } 583} 584 585void JingleSession::CloseInternal(ErrorCode error) { 586 DCHECK(CalledOnValidThread()); 587 588 if (state_ == CONNECTING || state_ == ACCEPTING || state_ == CONNECTED || 589 state_ == AUTHENTICATED) { 590 // Send session-terminate message with the appropriate error code. 591 JingleMessage::Reason reason; 592 switch (error) { 593 case OK: 594 reason = JingleMessage::SUCCESS; 595 break; 596 case SESSION_REJECTED: 597 case AUTHENTICATION_FAILED: 598 reason = JingleMessage::DECLINE; 599 break; 600 case INCOMPATIBLE_PROTOCOL: 601 reason = JingleMessage::INCOMPATIBLE_PARAMETERS; 602 break; 603 case HOST_OVERLOAD: 604 reason = JingleMessage::CANCEL; 605 break; 606 default: 607 reason = JingleMessage::GENERAL_ERROR; 608 } 609 610 JingleMessage message(peer_jid_, JingleMessage::SESSION_TERMINATE, 611 session_id_); 612 message.reason = reason; 613 SendMessage(message); 614 } 615 616 error_ = error; 617 618 if (state_ != FAILED && state_ != CLOSED) { 619 if (error != OK) { 620 SetState(FAILED); 621 } else { 622 SetState(CLOSED); 623 } 624 } 625} 626 627void JingleSession::SetState(State new_state) { 628 DCHECK(CalledOnValidThread()); 629 630 if (new_state != state_) { 631 DCHECK_NE(state_, CLOSED); 632 DCHECK_NE(state_, FAILED); 633 634 state_ = new_state; 635 if (event_handler_) 636 event_handler_->OnSessionStateChange(new_state); 637 } 638} 639 640} // namespace protocol 641} // namespace remoting 642