| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "remoting/protocol/jingle_session.h" | 5 #include "remoting/protocol/jingle_session.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/rand_util.h" | 8 #include "base/rand_util.h" |
| 9 #include "base/stl_util.h" | 9 #include "base/stl_util.h" |
| 10 #include "base/string_number_conversions.h" | 10 #include "base/string_number_conversions.h" |
| (...skipping 14 matching lines...) Expand all Loading... |
| 25 namespace protocol { | 25 namespace protocol { |
| 26 | 26 |
| 27 namespace { | 27 namespace { |
| 28 // Delay after candidate creation before sending transport-info | 28 // Delay after candidate creation before sending transport-info |
| 29 // message. This is neccessary to be able to pack multiple candidates | 29 // message. This is neccessary to be able to pack multiple candidates |
| 30 // into one transport-info messages. The value needs to be greater | 30 // into one transport-info messages. The value needs to be greater |
| 31 // than zero because ports are opened asynchronously in the browser | 31 // than zero because ports are opened asynchronously in the browser |
| 32 // process. | 32 // process. |
| 33 const int kTransportInfoSendDelayMs = 2; | 33 const int kTransportInfoSendDelayMs = 2; |
| 34 | 34 |
| 35 // How long we should wait for a response from the other end. This | |
| 36 // value is used for all requests include |session-initiate| and | |
| 37 // |transport-info|. | |
| 38 const int kMessageResponseTimeoutSeconds = 10; | |
| 39 | |
| 40 Session::Error AuthRejectionReasonToError( | 35 Session::Error AuthRejectionReasonToError( |
| 41 Authenticator::RejectionReason reason) { | 36 Authenticator::RejectionReason reason) { |
| 42 switch (reason) { | 37 switch (reason) { |
| 43 case Authenticator::INVALID_CREDENTIALS: | 38 case Authenticator::INVALID_CREDENTIALS: |
| 44 return Session::AUTHENTICATION_FAILED; | 39 return Session::AUTHENTICATION_FAILED; |
| 45 case Authenticator::PROTOCOL_ERROR: | 40 case Authenticator::PROTOCOL_ERROR: |
| 46 return Session::INCOMPATIBLE_PROTOCOL; | 41 return Session::INCOMPATIBLE_PROTOCOL; |
| 47 } | 42 } |
| 48 NOTREACHED(); | 43 NOTREACHED(); |
| 49 return Session::UNKNOWN_ERROR; | 44 return Session::UNKNOWN_ERROR; |
| 50 } | 45 } |
| 51 | 46 |
| 52 } // namespace | 47 } // namespace |
| 53 | 48 |
| 54 JingleSession::JingleSession(JingleSessionManager* session_manager) | 49 JingleSession::JingleSession(JingleSessionManager* session_manager) |
| 55 : session_manager_(session_manager), | 50 : session_manager_(session_manager), |
| 56 state_(INITIALIZING), | 51 state_(INITIALIZING), |
| 57 error_(OK), | 52 error_(OK), |
| 58 config_is_set_(false) { | 53 config_is_set_(false) { |
| 59 } | 54 } |
| 60 | 55 |
| 61 JingleSession::~JingleSession() { | 56 JingleSession::~JingleSession() { |
| 62 STLDeleteContainerPointers(pending_requests_.begin(), | |
| 63 pending_requests_.end()); | |
| 64 STLDeleteContainerPairSecondPointers(channels_.begin(), channels_.end()); | 57 STLDeleteContainerPairSecondPointers(channels_.begin(), channels_.end()); |
| 65 session_manager_->SessionDestroyed(this); | 58 session_manager_->SessionDestroyed(this); |
| 66 } | 59 } |
| 67 | 60 |
| 68 void JingleSession::SetStateChangeCallback( | 61 void JingleSession::SetStateChangeCallback( |
| 69 const StateChangeCallback& callback) { | 62 const StateChangeCallback& callback) { |
| 70 DCHECK(CalledOnValidThread()); | 63 DCHECK(CalledOnValidThread()); |
| 71 DCHECK(!callback.is_null()); | 64 DCHECK(!callback.is_null()); |
| 72 state_change_callback_ = callback; | 65 state_change_callback_ = callback; |
| 73 } | 66 } |
| (...skipping 28 matching lines...) Expand all Loading... |
| 102 // enough entropy. In the worst case connection will fail when two | 95 // enough entropy. In the worst case connection will fail when two |
| 103 // clients generate the same session ID concurrently. | 96 // clients generate the same session ID concurrently. |
| 104 session_id_ = base::Int64ToString(base::RandGenerator(kint64max)); | 97 session_id_ = base::Int64ToString(base::RandGenerator(kint64max)); |
| 105 | 98 |
| 106 // Send session-initiate message. | 99 // Send session-initiate message. |
| 107 JingleMessage message(peer_jid_, JingleMessage::SESSION_INITIATE, | 100 JingleMessage message(peer_jid_, JingleMessage::SESSION_INITIATE, |
| 108 session_id_); | 101 session_id_); |
| 109 message.description.reset( | 102 message.description.reset( |
| 110 new ContentDescription(candidate_config_->Clone(), | 103 new ContentDescription(candidate_config_->Clone(), |
| 111 authenticator_->GetNextMessage())); | 104 authenticator_->GetNextMessage())); |
| 112 SendMessage(message); | 105 initiate_request_ = session_manager_->iq_sender()->SendIq( |
| 106 message.ToXml(), |
| 107 base::Bind(&JingleSession::OnSessionInitiateResponse, |
| 108 base::Unretained(this))); |
| 113 | 109 |
| 114 SetState(CONNECTING); | 110 SetState(CONNECTING); |
| 115 } | 111 } |
| 116 | 112 |
| 117 void JingleSession::InitializeIncomingConnection( | 113 void JingleSession::InitializeIncomingConnection( |
| 118 const JingleMessage& initiate_message, | 114 const JingleMessage& initiate_message, |
| 119 scoped_ptr<Authenticator> authenticator) { | 115 scoped_ptr<Authenticator> authenticator) { |
| 120 DCHECK(CalledOnValidThread()); | 116 DCHECK(CalledOnValidThread()); |
| 121 DCHECK(initiate_message.description.get()); | 117 DCHECK(initiate_message.description.get()); |
| 122 DCHECK(authenticator.get()); | 118 DCHECK(authenticator.get()); |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 155 JingleMessage message(peer_jid_, JingleMessage::SESSION_ACCEPT, | 151 JingleMessage message(peer_jid_, JingleMessage::SESSION_ACCEPT, |
| 156 session_id_); | 152 session_id_); |
| 157 | 153 |
| 158 scoped_ptr<buzz::XmlElement> auth_message; | 154 scoped_ptr<buzz::XmlElement> auth_message; |
| 159 if (authenticator_->state() == Authenticator::MESSAGE_READY) | 155 if (authenticator_->state() == Authenticator::MESSAGE_READY) |
| 160 auth_message = authenticator_->GetNextMessage(); | 156 auth_message = authenticator_->GetNextMessage(); |
| 161 | 157 |
| 162 message.description.reset( | 158 message.description.reset( |
| 163 new ContentDescription(CandidateSessionConfig::CreateFrom(config_), | 159 new ContentDescription(CandidateSessionConfig::CreateFrom(config_), |
| 164 auth_message.Pass())); | 160 auth_message.Pass())); |
| 165 SendMessage(message); | 161 initiate_request_ = session_manager_->iq_sender()->SendIq( |
| 162 message.ToXml(), |
| 163 base::Bind(&JingleSession::OnSessionInitiateResponse, |
| 164 base::Unretained(this))); |
| 166 | 165 |
| 167 // Update state. | 166 // Update state. |
| 168 SetState(CONNECTED); | 167 SetState(CONNECTED); |
| 169 | 168 |
| 170 if (authenticator_->state() == Authenticator::ACCEPTED) { | 169 if (authenticator_->state() == Authenticator::ACCEPTED) { |
| 171 SetState(AUTHENTICATED); | 170 SetState(AUTHENTICATED); |
| 172 } else { | 171 } else { |
| 173 DCHECK_EQ(authenticator_->state(), Authenticator::WAITING_MESSAGE); | 172 DCHECK_EQ(authenticator_->state(), Authenticator::WAITING_MESSAGE); |
| 174 } | 173 } |
| 175 | 174 |
| 176 return; | 175 return; |
| 177 } | 176 } |
| 178 | 177 |
| 178 void JingleSession::OnSessionInitiateResponse( |
| 179 const buzz::XmlElement* response) { |
| 180 const std::string& type = response->Attr(buzz::QName("", "type")); |
| 181 if (type != "result") { |
| 182 LOG(ERROR) << "Received error in response to session-initiate message: \"" |
| 183 << response->Str() |
| 184 << "\". Terminating the session."; |
| 185 |
| 186 // TODO(sergeyu): There may be different reasons for error |
| 187 // here. Parse the response stanza to find failure reason. |
| 188 CloseInternal(PEER_IS_OFFLINE); |
| 189 } |
| 190 } |
| 191 |
| 179 void JingleSession::CreateStreamChannel( | 192 void JingleSession::CreateStreamChannel( |
| 180 const std::string& name, | 193 const std::string& name, |
| 181 const StreamChannelCallback& callback) { | 194 const StreamChannelCallback& callback) { |
| 182 DCHECK(!channels_[name]); | 195 DCHECK(!channels_[name]); |
| 183 | 196 |
| 184 scoped_ptr<ChannelAuthenticator> channel_authenticator = | 197 scoped_ptr<ChannelAuthenticator> channel_authenticator = |
| 185 authenticator_->CreateChannelAuthenticator(); | 198 authenticator_->CreateChannelAuthenticator(); |
| 186 scoped_ptr<StreamTransport> channel = | 199 scoped_ptr<StreamTransport> channel = |
| 187 session_manager_->transport_factory_->CreateStreamTransport(); | 200 session_manager_->transport_factory_->CreateStreamTransport(); |
| 188 channel->Initialize(name, session_manager_->transport_config_, | 201 channel->Initialize(name, session_manager_->transport_config_, |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 262 route.local_address); | 275 route.local_address); |
| 263 } | 276 } |
| 264 } | 277 } |
| 265 | 278 |
| 266 void JingleSession::OnTransportDeleted(Transport* transport) { | 279 void JingleSession::OnTransportDeleted(Transport* transport) { |
| 267 ChannelsMap::iterator it = channels_.find(transport->name()); | 280 ChannelsMap::iterator it = channels_.find(transport->name()); |
| 268 DCHECK_EQ(it->second, transport); | 281 DCHECK_EQ(it->second, transport); |
| 269 channels_.erase(it); | 282 channels_.erase(it); |
| 270 } | 283 } |
| 271 | 284 |
| 272 void JingleSession::SendMessage(const JingleMessage& message) { | |
| 273 scoped_ptr<IqRequest> request = session_manager_->iq_sender()->SendIq( | |
| 274 message.ToXml(), | |
| 275 base::Bind(&JingleSession::OnMessageResponse, | |
| 276 base::Unretained(this), message.action)); | |
| 277 if (request.get()) { | |
| 278 request->SetTimeout( | |
| 279 base::TimeDelta::FromSeconds(kMessageResponseTimeoutSeconds)); | |
| 280 pending_requests_.push_back(request.release()); | |
| 281 } else { | |
| 282 LOG(ERROR) << "Failed to send a " | |
| 283 << JingleMessage::GetActionName(message.action) << " message"; | |
| 284 } | |
| 285 } | |
| 286 | |
| 287 void JingleSession::OnMessageResponse( | |
| 288 JingleMessage::ActionType request_type, | |
| 289 IqRequest* request, | |
| 290 const buzz::XmlElement* response) { | |
| 291 Error error = OK; | |
| 292 | |
| 293 std::string type_str = JingleMessage::GetActionName(request_type); | |
| 294 | |
| 295 if (!response) { | |
| 296 LOG(ERROR) << type_str << " request timed out."; | |
| 297 // Most likely the session-initiate timeout indicates a problem | |
| 298 // with the signaling. | |
| 299 error = UNKNOWN_ERROR; | |
| 300 } else { | |
| 301 const std::string& type = response->Attr(buzz::QName("", "type")); | |
| 302 if (type != "result") { | |
| 303 LOG(ERROR) << "Received error in response to " << type_str | |
| 304 << " message: \"" << response->Str() | |
| 305 << "\". Terminating the session."; | |
| 306 | |
| 307 switch (request_type) { | |
| 308 case JingleMessage::SESSION_INFO: | |
| 309 // session-info is used for the new authentication protocol, | |
| 310 // and wasn't previously supported. | |
| 311 error = INCOMPATIBLE_PROTOCOL; | |
| 312 | |
| 313 default: | |
| 314 // TODO(sergeyu): There may be different reasons for error | |
| 315 // here. Parse the response stanza to find failure reason. | |
| 316 error = PEER_IS_OFFLINE; | |
| 317 } | |
| 318 } | |
| 319 } | |
| 320 | |
| 321 CleanupPendingRequests(request); | |
| 322 | |
| 323 if (error != OK) { | |
| 324 CloseInternal(error); | |
| 325 } | |
| 326 } | |
| 327 | |
| 328 void JingleSession::CleanupPendingRequests(IqRequest* request) { | |
| 329 DCHECK(!pending_requests_.empty()); | |
| 330 DCHECK(request); | |
| 331 | |
| 332 // This method is called whenever a response to |request| is | |
| 333 // received. Here we delete that request and all requests that were | |
| 334 // sent before it. The idea here is that if we send messages A, B | |
| 335 // and C and then suddenly receive response to C then it means that | |
| 336 // either A and B messages or the corresponding response messages | |
| 337 // were somehow lost. E.g. that may happen when the client switches | |
| 338 // from one network to another. The best way to handle that case is | |
| 339 // to ignore errors and timeouts for A and B by deleting the | |
| 340 // corresponding IqRequest objects. | |
| 341 while (!pending_requests_.empty() && pending_requests_.front() != request) { | |
| 342 delete pending_requests_.front(); | |
| 343 pending_requests_.pop_front(); | |
| 344 } | |
| 345 | |
| 346 // Delete the |request| itself. | |
| 347 DCHECK_EQ(request, pending_requests_.front()); | |
| 348 delete request; | |
| 349 if (!pending_requests_.empty()) | |
| 350 pending_requests_.pop_front(); | |
| 351 } | |
| 352 | |
| 353 void JingleSession::OnIncomingMessage(const JingleMessage& message, | 285 void JingleSession::OnIncomingMessage(const JingleMessage& message, |
| 354 const ReplyCallback& reply_callback) { | 286 const ReplyCallback& reply_callback) { |
| 355 DCHECK(CalledOnValidThread()); | 287 DCHECK(CalledOnValidThread()); |
| 356 | 288 |
| 357 if (message.from != peer_jid_) { | 289 if (message.from != peer_jid_) { |
| 358 // Ignore messages received from a different Jid. | 290 // Ignore messages received from a different Jid. |
| 359 reply_callback.Run(JingleMessageReply::INVALID_SID); | 291 reply_callback.Run(JingleMessageReply::INVALID_SID); |
| 360 return; | 292 return; |
| 361 } | 293 } |
| 362 | 294 |
| (...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 512 return true; | 444 return true; |
| 513 } | 445 } |
| 514 | 446 |
| 515 void JingleSession::ProcessAuthenticationStep() { | 447 void JingleSession::ProcessAuthenticationStep() { |
| 516 DCHECK_EQ(state_, CONNECTED); | 448 DCHECK_EQ(state_, CONNECTED); |
| 517 | 449 |
| 518 if (authenticator_->state() == Authenticator::MESSAGE_READY) { | 450 if (authenticator_->state() == Authenticator::MESSAGE_READY) { |
| 519 JingleMessage message(peer_jid_, JingleMessage::SESSION_INFO, session_id_); | 451 JingleMessage message(peer_jid_, JingleMessage::SESSION_INFO, session_id_); |
| 520 message.info = authenticator_->GetNextMessage(); | 452 message.info = authenticator_->GetNextMessage(); |
| 521 DCHECK(message.info.get()); | 453 DCHECK(message.info.get()); |
| 522 SendMessage(message); | 454 |
| 455 session_info_request_ = session_manager_->iq_sender()->SendIq( |
| 456 message.ToXml(), base::Bind( |
| 457 &JingleSession::OnSessionInfoResponse, |
| 458 base::Unretained(this))); |
| 523 } | 459 } |
| 524 DCHECK_NE(authenticator_->state(), Authenticator::MESSAGE_READY); | 460 DCHECK_NE(authenticator_->state(), Authenticator::MESSAGE_READY); |
| 525 | 461 |
| 526 if (authenticator_->state() == Authenticator::ACCEPTED) { | 462 if (authenticator_->state() == Authenticator::ACCEPTED) { |
| 527 SetState(AUTHENTICATED); | 463 SetState(AUTHENTICATED); |
| 528 } else if (authenticator_->state() == Authenticator::REJECTED) { | 464 } else if (authenticator_->state() == Authenticator::REJECTED) { |
| 529 CloseInternal(AuthRejectionReasonToError( | 465 CloseInternal(AuthRejectionReasonToError( |
| 530 authenticator_->rejection_reason())); | 466 authenticator_->rejection_reason())); |
| 531 } | 467 } |
| 532 } | 468 } |
| 533 | 469 |
| 470 void JingleSession::OnSessionInfoResponse(const buzz::XmlElement* response) { |
| 471 const std::string& type = response->Attr(buzz::QName("", "type")); |
| 472 if (type != "result") { |
| 473 LOG(ERROR) << "Received error in response to session-info message: \"" |
| 474 << response->Str() |
| 475 << "\". Terminating the session."; |
| 476 CloseInternal(INCOMPATIBLE_PROTOCOL); |
| 477 } |
| 478 } |
| 479 |
| 480 void JingleSession::OnTransportInfoResponse(const buzz::XmlElement* response) { |
| 481 const std::string& type = response->Attr(buzz::QName("", "type")); |
| 482 if (type != "result") { |
| 483 LOG(ERROR) << "Received error in response to session-initiate message: \"" |
| 484 << response->Str() |
| 485 << "\". Terminating the session."; |
| 486 |
| 487 if (state_ == CONNECTING) { |
| 488 CloseInternal(PEER_IS_OFFLINE); |
| 489 } else { |
| 490 // Host has disconnected without sending session-terminate message. |
| 491 CloseInternal(OK); |
| 492 } |
| 493 } |
| 494 } |
| 495 |
| 534 void JingleSession::SendTransportInfo() { | 496 void JingleSession::SendTransportInfo() { |
| 535 JingleMessage message(peer_jid_, JingleMessage::TRANSPORT_INFO, session_id_); | 497 JingleMessage message(peer_jid_, JingleMessage::TRANSPORT_INFO, session_id_); |
| 536 message.candidates.swap(pending_candidates_); | 498 message.candidates.swap(pending_candidates_); |
| 537 SendMessage(message); | 499 transport_info_request_ = session_manager_->iq_sender()->SendIq( |
| 500 message.ToXml(), base::Bind( |
| 501 &JingleSession::OnTransportInfoResponse, |
| 502 base::Unretained(this))); |
| 538 } | 503 } |
| 539 | 504 |
| 505 |
| 540 void JingleSession::CloseInternal(Error error) { | 506 void JingleSession::CloseInternal(Error error) { |
| 541 DCHECK(CalledOnValidThread()); | 507 DCHECK(CalledOnValidThread()); |
| 542 | 508 |
| 543 if (state_ == CONNECTING || state_ == CONNECTED || state_ == AUTHENTICATED) { | 509 if (state_ == CONNECTING || state_ == CONNECTED || state_ == AUTHENTICATED) { |
| 544 // Send session-terminate message with the appropriate error code. | 510 // Send session-terminate message with the appropriate error code. |
| 545 JingleMessage::Reason reason; | 511 JingleMessage::Reason reason; |
| 546 switch (error) { | 512 switch (error) { |
| 547 case OK: | 513 case OK: |
| 548 reason = JingleMessage::SUCCESS; | 514 reason = JingleMessage::SUCCESS; |
| 549 break; | 515 break; |
| 550 case SESSION_REJECTED: | 516 case SESSION_REJECTED: |
| 551 case AUTHENTICATION_FAILED: | 517 case AUTHENTICATION_FAILED: |
| 552 reason = JingleMessage::DECLINE; | 518 reason = JingleMessage::DECLINE; |
| 553 break; | 519 break; |
| 554 case INCOMPATIBLE_PROTOCOL: | 520 case INCOMPATIBLE_PROTOCOL: |
| 555 reason = JingleMessage::INCOMPATIBLE_PARAMETERS; | 521 reason = JingleMessage::INCOMPATIBLE_PARAMETERS; |
| 556 break; | 522 break; |
| 557 default: | 523 default: |
| 558 reason = JingleMessage::GENERAL_ERROR; | 524 reason = JingleMessage::GENERAL_ERROR; |
| 559 } | 525 } |
| 560 | 526 |
| 561 JingleMessage message(peer_jid_, JingleMessage::SESSION_TERMINATE, | 527 JingleMessage message(peer_jid_, JingleMessage::SESSION_TERMINATE, |
| 562 session_id_); | 528 session_id_); |
| 563 message.reason = reason; | 529 message.reason = reason; |
| 564 SendMessage(message); | 530 session_manager_->iq_sender()->SendIq( |
| 531 message.ToXml(), IqSender::ReplyCallback()); |
| 565 } | 532 } |
| 566 | 533 |
| 567 error_ = error; | 534 error_ = error; |
| 568 | 535 |
| 569 if (state_ != FAILED && state_ != CLOSED) { | 536 if (state_ != FAILED && state_ != CLOSED) { |
| 570 if (error != OK) { | 537 if (error != OK) { |
| 571 SetState(FAILED); | 538 SetState(FAILED); |
| 572 } else { | 539 } else { |
| 573 SetState(CLOSED); | 540 SetState(CLOSED); |
| 574 } | 541 } |
| 575 } | 542 } |
| 576 } | 543 } |
| 577 | 544 |
| 578 void JingleSession::SetState(State new_state) { | 545 void JingleSession::SetState(State new_state) { |
| 579 DCHECK(CalledOnValidThread()); | 546 DCHECK(CalledOnValidThread()); |
| 580 | 547 |
| 581 if (new_state != state_) { | 548 if (new_state != state_) { |
| 582 DCHECK_NE(state_, CLOSED); | 549 DCHECK_NE(state_, CLOSED); |
| 583 DCHECK_NE(state_, FAILED); | 550 DCHECK_NE(state_, FAILED); |
| 584 | 551 |
| 585 state_ = new_state; | 552 state_ = new_state; |
| 586 if (!state_change_callback_.is_null()) | 553 if (!state_change_callback_.is_null()) |
| 587 state_change_callback_.Run(new_state); | 554 state_change_callback_.Run(new_state); |
| 588 } | 555 } |
| 589 } | 556 } |
| 590 | 557 |
| 591 } // namespace protocol | 558 } // namespace protocol |
| 592 } // namespace remoting | 559 } // namespace remoting |
| OLD | NEW |