1/* 2 * libjingle 3 * Copyright 2004--2005, Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28#include "talk/p2p/base/transport.h" 29 30#include "talk/base/bind.h" 31#include "talk/base/common.h" 32#include "talk/base/logging.h" 33#include "talk/p2p/base/candidate.h" 34#include "talk/p2p/base/constants.h" 35#include "talk/p2p/base/sessionmanager.h" 36#include "talk/p2p/base/parsing.h" 37#include "talk/p2p/base/transportchannelimpl.h" 38#include "talk/xmllite/xmlelement.h" 39#include "talk/xmpp/constants.h" 40 41namespace cricket { 42 43using talk_base::Bind; 44 45enum { 46 MSG_ONSIGNALINGREADY = 1, 47 MSG_ONREMOTECANDIDATE, 48 MSG_READSTATE, 49 MSG_WRITESTATE, 50 MSG_REQUESTSIGNALING, 51 MSG_CANDIDATEREADY, 52 MSG_ROUTECHANGE, 53 MSG_CONNECTING, 54 MSG_CANDIDATEALLOCATIONCOMPLETE, 55 MSG_ROLECONFLICT, 56}; 57 58struct ChannelParams : public talk_base::MessageData { 59 ChannelParams() : channel(NULL), candidate(NULL) {} 60 explicit ChannelParams(int component) 61 : component(component), channel(NULL), candidate(NULL) {} 62 explicit ChannelParams(Candidate* candidate) 63 : channel(NULL), candidate(candidate) { 64 } 65 66 ~ChannelParams() { 67 delete candidate; 68 } 69 70 std::string name; 71 int component; 72 TransportChannelImpl* channel; 73 Candidate* candidate; 74}; 75 76Transport::Transport(talk_base::Thread* signaling_thread, 77 talk_base::Thread* worker_thread, 78 const std::string& content_name, 79 const std::string& type, 80 PortAllocator* allocator) 81 : signaling_thread_(signaling_thread), 82 worker_thread_(worker_thread), 83 content_name_(content_name), 84 type_(type), 85 allocator_(allocator), 86 destroyed_(false), 87 readable_(TRANSPORT_STATE_NONE), 88 writable_(TRANSPORT_STATE_NONE), 89 was_writable_(false), 90 connect_requested_(false), 91 ice_role_(ICEROLE_UNKNOWN), 92 tiebreaker_(0), 93 protocol_(ICEPROTO_HYBRID), 94 remote_ice_mode_(ICEMODE_FULL) { 95} 96 97Transport::~Transport() { 98 ASSERT(signaling_thread_->IsCurrent()); 99 ASSERT(destroyed_); 100} 101 102void Transport::SetIceRole(IceRole role) { 103 worker_thread_->Invoke<void>(Bind(&Transport::SetIceRole_w, this, role)); 104} 105 106void Transport::SetIdentity(talk_base::SSLIdentity* identity) { 107 worker_thread_->Invoke<void>(Bind(&Transport::SetIdentity_w, this, identity)); 108} 109 110bool Transport::GetIdentity(talk_base::SSLIdentity** identity) { 111 // The identity is set on the worker thread, so for safety it must also be 112 // acquired on the worker thread. 113 return worker_thread_->Invoke<bool>( 114 Bind(&Transport::GetIdentity_w, this, identity)); 115} 116 117bool Transport::GetRemoteCertificate(talk_base::SSLCertificate** cert) { 118 // Channels can be deleted on the worker thread, so for safety the remote 119 // certificate is acquired on the worker thread. 120 return worker_thread_->Invoke<bool>( 121 Bind(&Transport::GetRemoteCertificate_w, this, cert)); 122} 123 124bool Transport::GetRemoteCertificate_w(talk_base::SSLCertificate** cert) { 125 ASSERT(worker_thread()->IsCurrent()); 126 if (channels_.empty()) 127 return false; 128 129 ChannelMap::iterator iter = channels_.begin(); 130 return iter->second->GetRemoteCertificate(cert); 131} 132 133bool Transport::SetLocalTransportDescription( 134 const TransportDescription& description, ContentAction action) { 135 return worker_thread_->Invoke<bool>(Bind( 136 &Transport::SetLocalTransportDescription_w, this, description, action)); 137} 138 139bool Transport::SetRemoteTransportDescription( 140 const TransportDescription& description, ContentAction action) { 141 return worker_thread_->Invoke<bool>(Bind( 142 &Transport::SetRemoteTransportDescription_w, this, description, action)); 143} 144 145TransportChannelImpl* Transport::CreateChannel(int component) { 146 return worker_thread_->Invoke<TransportChannelImpl*>(Bind( 147 &Transport::CreateChannel_w, this, component)); 148} 149 150TransportChannelImpl* Transport::CreateChannel_w(int component) { 151 ASSERT(worker_thread()->IsCurrent()); 152 TransportChannelImpl *impl; 153 talk_base::CritScope cs(&crit_); 154 155 // Create the entry if it does not exist. 156 bool impl_exists = false; 157 if (channels_.find(component) == channels_.end()) { 158 impl = CreateTransportChannel(component); 159 channels_[component] = ChannelMapEntry(impl); 160 } else { 161 impl = channels_[component].get(); 162 impl_exists = true; 163 } 164 165 // Increase the ref count. 166 channels_[component].AddRef(); 167 destroyed_ = false; 168 169 if (impl_exists) { 170 // If this is an existing channel, we should just return it without 171 // connecting to all the signal again. 172 return impl; 173 } 174 175 // Push down our transport state to the new channel. 176 impl->SetIceRole(ice_role_); 177 impl->SetIceTiebreaker(tiebreaker_); 178 if (local_description_) { 179 ApplyLocalTransportDescription_w(impl); 180 if (remote_description_) { 181 ApplyRemoteTransportDescription_w(impl); 182 ApplyNegotiatedTransportDescription_w(impl); 183 } 184 } 185 186 impl->SignalReadableState.connect(this, &Transport::OnChannelReadableState); 187 impl->SignalWritableState.connect(this, &Transport::OnChannelWritableState); 188 impl->SignalRequestSignaling.connect( 189 this, &Transport::OnChannelRequestSignaling); 190 impl->SignalCandidateReady.connect(this, &Transport::OnChannelCandidateReady); 191 impl->SignalRouteChange.connect(this, &Transport::OnChannelRouteChange); 192 impl->SignalCandidatesAllocationDone.connect( 193 this, &Transport::OnChannelCandidatesAllocationDone); 194 impl->SignalRoleConflict.connect(this, &Transport::OnRoleConflict); 195 196 if (connect_requested_) { 197 impl->Connect(); 198 if (channels_.size() == 1) { 199 // If this is the first channel, then indicate that we have started 200 // connecting. 201 signaling_thread()->Post(this, MSG_CONNECTING, NULL); 202 } 203 } 204 return impl; 205} 206 207TransportChannelImpl* Transport::GetChannel(int component) { 208 talk_base::CritScope cs(&crit_); 209 ChannelMap::iterator iter = channels_.find(component); 210 return (iter != channels_.end()) ? iter->second.get() : NULL; 211} 212 213bool Transport::HasChannels() { 214 talk_base::CritScope cs(&crit_); 215 return !channels_.empty(); 216} 217 218void Transport::DestroyChannel(int component) { 219 worker_thread_->Invoke<void>(Bind( 220 &Transport::DestroyChannel_w, this, component)); 221} 222 223void Transport::DestroyChannel_w(int component) { 224 ASSERT(worker_thread()->IsCurrent()); 225 226 TransportChannelImpl* impl = NULL; 227 { 228 talk_base::CritScope cs(&crit_); 229 ChannelMap::iterator iter = channels_.find(component); 230 if (iter == channels_.end()) 231 return; 232 233 iter->second.DecRef(); 234 if (!iter->second.ref()) { 235 impl = iter->second.get(); 236 channels_.erase(iter); 237 } 238 } 239 240 if (connect_requested_ && channels_.empty()) { 241 // We're no longer attempting to connect. 242 signaling_thread()->Post(this, MSG_CONNECTING, NULL); 243 } 244 245 if (impl) { 246 // Check in case the deleted channel was the only non-writable channel. 247 OnChannelWritableState(impl); 248 DestroyTransportChannel(impl); 249 } 250} 251 252void Transport::ConnectChannels() { 253 ASSERT(signaling_thread()->IsCurrent()); 254 worker_thread_->Invoke<void>(Bind(&Transport::ConnectChannels_w, this)); 255} 256 257void Transport::ConnectChannels_w() { 258 ASSERT(worker_thread()->IsCurrent()); 259 if (connect_requested_ || channels_.empty()) 260 return; 261 connect_requested_ = true; 262 signaling_thread()->Post( 263 this, MSG_CANDIDATEREADY, NULL); 264 265 if (!local_description_) { 266 // TOOD(mallinath) : TransportDescription(TD) shouldn't be generated here. 267 // As Transport must know TD is offer or answer and cricket::Transport 268 // doesn't have the capability to decide it. This should be set by the 269 // Session. 270 // Session must generate local TD before remote candidates pushed when 271 // initiate request initiated by the remote. 272 LOG(LS_INFO) << "Transport::ConnectChannels_w: No local description has " 273 << "been set. Will generate one."; 274 TransportDescription desc(NS_GINGLE_P2P, std::vector<std::string>(), 275 talk_base::CreateRandomString(ICE_UFRAG_LENGTH), 276 talk_base::CreateRandomString(ICE_PWD_LENGTH), 277 ICEMODE_FULL, CONNECTIONROLE_NONE, NULL, 278 Candidates()); 279 SetLocalTransportDescription_w(desc, CA_OFFER); 280 } 281 282 CallChannels_w(&TransportChannelImpl::Connect); 283 if (!channels_.empty()) { 284 signaling_thread()->Post(this, MSG_CONNECTING, NULL); 285 } 286} 287 288void Transport::OnConnecting_s() { 289 ASSERT(signaling_thread()->IsCurrent()); 290 SignalConnecting(this); 291} 292 293void Transport::DestroyAllChannels() { 294 ASSERT(signaling_thread()->IsCurrent()); 295 worker_thread_->Invoke<void>( 296 Bind(&Transport::DestroyAllChannels_w, this)); 297 worker_thread()->Clear(this); 298 signaling_thread()->Clear(this); 299 destroyed_ = true; 300} 301 302void Transport::DestroyAllChannels_w() { 303 ASSERT(worker_thread()->IsCurrent()); 304 std::vector<TransportChannelImpl*> impls; 305 { 306 talk_base::CritScope cs(&crit_); 307 for (ChannelMap::iterator iter = channels_.begin(); 308 iter != channels_.end(); 309 ++iter) { 310 iter->second.DecRef(); 311 if (!iter->second.ref()) 312 impls.push_back(iter->second.get()); 313 } 314 } 315 channels_.clear(); 316 317 318 for (size_t i = 0; i < impls.size(); ++i) 319 DestroyTransportChannel(impls[i]); 320} 321 322void Transport::ResetChannels() { 323 ASSERT(signaling_thread()->IsCurrent()); 324 worker_thread_->Invoke<void>(Bind(&Transport::ResetChannels_w, this)); 325} 326 327void Transport::ResetChannels_w() { 328 ASSERT(worker_thread()->IsCurrent()); 329 330 // We are no longer attempting to connect 331 connect_requested_ = false; 332 333 // Clear out the old messages, they aren't relevant 334 talk_base::CritScope cs(&crit_); 335 ready_candidates_.clear(); 336 337 // Reset all of the channels 338 CallChannels_w(&TransportChannelImpl::Reset); 339} 340 341void Transport::OnSignalingReady() { 342 ASSERT(signaling_thread()->IsCurrent()); 343 if (destroyed_) return; 344 345 worker_thread()->Post(this, MSG_ONSIGNALINGREADY, NULL); 346 347 // Notify the subclass. 348 OnTransportSignalingReady(); 349} 350 351void Transport::CallChannels_w(TransportChannelFunc func) { 352 ASSERT(worker_thread()->IsCurrent()); 353 talk_base::CritScope cs(&crit_); 354 for (ChannelMap::iterator iter = channels_.begin(); 355 iter != channels_.end(); 356 ++iter) { 357 ((iter->second.get())->*func)(); 358 } 359} 360 361bool Transport::VerifyCandidate(const Candidate& cand, std::string* error) { 362 // No address zero. 363 if (cand.address().IsNil() || cand.address().IsAny()) { 364 *error = "candidate has address of zero"; 365 return false; 366 } 367 368 // Disallow all ports below 1024, except for 80 and 443 on public addresses. 369 int port = cand.address().port(); 370 if (port < 1024) { 371 if ((port != 80) && (port != 443)) { 372 *error = "candidate has port below 1024, but not 80 or 443"; 373 return false; 374 } 375 376 if (cand.address().IsPrivateIP()) { 377 *error = "candidate has port of 80 or 443 with private IP address"; 378 return false; 379 } 380 } 381 382 return true; 383} 384 385 386bool Transport::GetStats(TransportStats* stats) { 387 ASSERT(signaling_thread()->IsCurrent()); 388 return worker_thread_->Invoke<bool>(Bind( 389 &Transport::GetStats_w, this, stats)); 390} 391 392bool Transport::GetStats_w(TransportStats* stats) { 393 ASSERT(worker_thread()->IsCurrent()); 394 stats->content_name = content_name(); 395 stats->channel_stats.clear(); 396 for (ChannelMap::iterator iter = channels_.begin(); 397 iter != channels_.end(); 398 ++iter) { 399 TransportChannelStats substats; 400 substats.component = iter->second->component(); 401 if (!iter->second->GetStats(&substats.connection_infos)) { 402 return false; 403 } 404 stats->channel_stats.push_back(substats); 405 } 406 return true; 407} 408 409bool Transport::GetSslRole(talk_base::SSLRole* ssl_role) const { 410 return worker_thread_->Invoke<bool>(Bind( 411 &Transport::GetSslRole_w, this, ssl_role)); 412} 413 414void Transport::OnRemoteCandidates(const std::vector<Candidate>& candidates) { 415 for (std::vector<Candidate>::const_iterator iter = candidates.begin(); 416 iter != candidates.end(); 417 ++iter) { 418 OnRemoteCandidate(*iter); 419 } 420} 421 422void Transport::OnRemoteCandidate(const Candidate& candidate) { 423 ASSERT(signaling_thread()->IsCurrent()); 424 if (destroyed_) return; 425 426 if (!HasChannel(candidate.component())) { 427 LOG(LS_WARNING) << "Ignoring candidate for unknown component " 428 << candidate.component(); 429 return; 430 } 431 432 ChannelParams* params = new ChannelParams(new Candidate(candidate)); 433 worker_thread()->Post(this, MSG_ONREMOTECANDIDATE, params); 434} 435 436void Transport::OnRemoteCandidate_w(const Candidate& candidate) { 437 ASSERT(worker_thread()->IsCurrent()); 438 ChannelMap::iterator iter = channels_.find(candidate.component()); 439 // It's ok for a channel to go away while this message is in transit. 440 if (iter != channels_.end()) { 441 iter->second->OnCandidate(candidate); 442 } 443} 444 445void Transport::OnChannelReadableState(TransportChannel* channel) { 446 ASSERT(worker_thread()->IsCurrent()); 447 signaling_thread()->Post(this, MSG_READSTATE, NULL); 448} 449 450void Transport::OnChannelReadableState_s() { 451 ASSERT(signaling_thread()->IsCurrent()); 452 TransportState readable = GetTransportState_s(true); 453 if (readable_ != readable) { 454 readable_ = readable; 455 SignalReadableState(this); 456 } 457} 458 459void Transport::OnChannelWritableState(TransportChannel* channel) { 460 ASSERT(worker_thread()->IsCurrent()); 461 signaling_thread()->Post(this, MSG_WRITESTATE, NULL); 462} 463 464void Transport::OnChannelWritableState_s() { 465 ASSERT(signaling_thread()->IsCurrent()); 466 TransportState writable = GetTransportState_s(false); 467 if (writable_ != writable) { 468 was_writable_ = (writable_ == TRANSPORT_STATE_ALL); 469 writable_ = writable; 470 SignalWritableState(this); 471 } 472} 473 474TransportState Transport::GetTransportState_s(bool read) { 475 ASSERT(signaling_thread()->IsCurrent()); 476 talk_base::CritScope cs(&crit_); 477 bool any = false; 478 bool all = !channels_.empty(); 479 for (ChannelMap::iterator iter = channels_.begin(); 480 iter != channels_.end(); 481 ++iter) { 482 bool b = (read ? iter->second->readable() : 483 iter->second->writable()); 484 any = any || b; 485 all = all && b; 486 } 487 if (all) { 488 return TRANSPORT_STATE_ALL; 489 } else if (any) { 490 return TRANSPORT_STATE_SOME; 491 } else { 492 return TRANSPORT_STATE_NONE; 493 } 494} 495 496void Transport::OnChannelRequestSignaling(TransportChannelImpl* channel) { 497 ASSERT(worker_thread()->IsCurrent()); 498 ChannelParams* params = new ChannelParams(channel->component()); 499 signaling_thread()->Post(this, MSG_REQUESTSIGNALING, params); 500} 501 502void Transport::OnChannelRequestSignaling_s(int component) { 503 ASSERT(signaling_thread()->IsCurrent()); 504 LOG(LS_INFO) << "Transport: " << content_name_ << ", allocating candidates"; 505 // Resetting ICE state for the channel. 506 { 507 talk_base::CritScope cs(&crit_); 508 ChannelMap::iterator iter = channels_.find(component); 509 if (iter != channels_.end()) 510 iter->second.set_candidates_allocated(false); 511 } 512 SignalRequestSignaling(this); 513} 514 515void Transport::OnChannelCandidateReady(TransportChannelImpl* channel, 516 const Candidate& candidate) { 517 ASSERT(worker_thread()->IsCurrent()); 518 talk_base::CritScope cs(&crit_); 519 ready_candidates_.push_back(candidate); 520 521 // We hold any messages until the client lets us connect. 522 if (connect_requested_) { 523 signaling_thread()->Post( 524 this, MSG_CANDIDATEREADY, NULL); 525 } 526} 527 528void Transport::OnChannelCandidateReady_s() { 529 ASSERT(signaling_thread()->IsCurrent()); 530 ASSERT(connect_requested_); 531 532 std::vector<Candidate> candidates; 533 { 534 talk_base::CritScope cs(&crit_); 535 candidates.swap(ready_candidates_); 536 } 537 538 // we do the deleting of Candidate* here to keep the new above and 539 // delete below close to each other 540 if (!candidates.empty()) { 541 SignalCandidatesReady(this, candidates); 542 } 543} 544 545void Transport::OnChannelRouteChange(TransportChannel* channel, 546 const Candidate& remote_candidate) { 547 ASSERT(worker_thread()->IsCurrent()); 548 ChannelParams* params = new ChannelParams(new Candidate(remote_candidate)); 549 params->channel = static_cast<cricket::TransportChannelImpl*>(channel); 550 signaling_thread()->Post(this, MSG_ROUTECHANGE, params); 551} 552 553void Transport::OnChannelRouteChange_s(const TransportChannel* channel, 554 const Candidate& remote_candidate) { 555 ASSERT(signaling_thread()->IsCurrent()); 556 SignalRouteChange(this, remote_candidate.component(), remote_candidate); 557} 558 559void Transport::OnChannelCandidatesAllocationDone( 560 TransportChannelImpl* channel) { 561 ASSERT(worker_thread()->IsCurrent()); 562 talk_base::CritScope cs(&crit_); 563 ChannelMap::iterator iter = channels_.find(channel->component()); 564 ASSERT(iter != channels_.end()); 565 LOG(LS_INFO) << "Transport: " << content_name_ << ", component " 566 << channel->component() << " allocation complete"; 567 iter->second.set_candidates_allocated(true); 568 569 // If all channels belonging to this Transport got signal, then 570 // forward this signal to upper layer. 571 // Can this signal arrive before all transport channels are created? 572 for (iter = channels_.begin(); iter != channels_.end(); ++iter) { 573 if (!iter->second.candidates_allocated()) 574 return; 575 } 576 signaling_thread_->Post(this, MSG_CANDIDATEALLOCATIONCOMPLETE); 577} 578 579void Transport::OnChannelCandidatesAllocationDone_s() { 580 ASSERT(signaling_thread()->IsCurrent()); 581 LOG(LS_INFO) << "Transport: " << content_name_ << " allocation complete"; 582 SignalCandidatesAllocationDone(this); 583} 584 585void Transport::OnRoleConflict(TransportChannelImpl* channel) { 586 signaling_thread_->Post(this, MSG_ROLECONFLICT); 587} 588 589void Transport::SetIceRole_w(IceRole role) { 590 talk_base::CritScope cs(&crit_); 591 ice_role_ = role; 592 for (ChannelMap::iterator iter = channels_.begin(); 593 iter != channels_.end(); ++iter) { 594 iter->second->SetIceRole(ice_role_); 595 } 596} 597 598void Transport::SetRemoteIceMode_w(IceMode mode) { 599 talk_base::CritScope cs(&crit_); 600 remote_ice_mode_ = mode; 601 // Shouldn't channels be created after this method executed? 602 for (ChannelMap::iterator iter = channels_.begin(); 603 iter != channels_.end(); ++iter) { 604 iter->second->SetRemoteIceMode(remote_ice_mode_); 605 } 606} 607 608bool Transport::SetLocalTransportDescription_w( 609 const TransportDescription& desc, ContentAction action) { 610 bool ret = true; 611 talk_base::CritScope cs(&crit_); 612 local_description_.reset(new TransportDescription(desc)); 613 614 for (ChannelMap::iterator iter = channels_.begin(); 615 iter != channels_.end(); ++iter) { 616 ret &= ApplyLocalTransportDescription_w(iter->second.get()); 617 } 618 if (!ret) 619 return false; 620 621 // If PRANSWER/ANSWER is set, we should decide transport protocol type. 622 if (action == CA_PRANSWER || action == CA_ANSWER) { 623 ret &= NegotiateTransportDescription_w(action); 624 } 625 return ret; 626} 627 628bool Transport::SetRemoteTransportDescription_w( 629 const TransportDescription& desc, ContentAction action) { 630 bool ret = true; 631 talk_base::CritScope cs(&crit_); 632 remote_description_.reset(new TransportDescription(desc)); 633 634 for (ChannelMap::iterator iter = channels_.begin(); 635 iter != channels_.end(); ++iter) { 636 ret &= ApplyRemoteTransportDescription_w(iter->second.get()); 637 } 638 639 // If PRANSWER/ANSWER is set, we should decide transport protocol type. 640 if (action == CA_PRANSWER || action == CA_ANSWER) { 641 ret = NegotiateTransportDescription_w(CA_OFFER); 642 } 643 return ret; 644} 645 646bool Transport::ApplyLocalTransportDescription_w(TransportChannelImpl* ch) { 647 ch->SetIceCredentials(local_description_->ice_ufrag, 648 local_description_->ice_pwd); 649 return true; 650} 651 652bool Transport::ApplyRemoteTransportDescription_w(TransportChannelImpl* ch) { 653 ch->SetRemoteIceCredentials(remote_description_->ice_ufrag, 654 remote_description_->ice_pwd); 655 return true; 656} 657 658bool Transport::ApplyNegotiatedTransportDescription_w( 659 TransportChannelImpl* channel) { 660 channel->SetIceProtocolType(protocol_); 661 channel->SetRemoteIceMode(remote_ice_mode_); 662 return true; 663} 664 665bool Transport::NegotiateTransportDescription_w(ContentAction local_role) { 666 // TODO(ekr@rtfm.com): This is ICE-specific stuff. Refactor into 667 // P2PTransport. 668 const TransportDescription* offer; 669 const TransportDescription* answer; 670 671 if (local_role == CA_OFFER) { 672 offer = local_description_.get(); 673 answer = remote_description_.get(); 674 } else { 675 offer = remote_description_.get(); 676 answer = local_description_.get(); 677 } 678 679 TransportProtocol offer_proto = TransportProtocolFromDescription(offer); 680 TransportProtocol answer_proto = TransportProtocolFromDescription(answer); 681 682 // If offered protocol is gice/ice, then we expect to receive matching 683 // protocol in answer, anything else is treated as an error. 684 // HYBRID is not an option when offered specific protocol. 685 // If offered protocol is HYBRID and answered protocol is HYBRID then 686 // gice is preferred protocol. 687 // TODO(mallinath) - Answer from local or remote should't have both ice 688 // and gice support. It should always pick which protocol it wants to use. 689 // Once WebRTC stops supporting gice (for backward compatibility), HYBRID in 690 // answer must be treated as error. 691 if ((offer_proto == ICEPROTO_GOOGLE || offer_proto == ICEPROTO_RFC5245) && 692 (offer_proto != answer_proto)) { 693 return false; 694 } 695 protocol_ = answer_proto == ICEPROTO_HYBRID ? ICEPROTO_GOOGLE : answer_proto; 696 697 // If transport is in ICEROLE_CONTROLLED and remote end point supports only 698 // ice_lite, this local end point should take CONTROLLING role. 699 if (ice_role_ == ICEROLE_CONTROLLED && 700 remote_description_->ice_mode == ICEMODE_LITE) { 701 SetIceRole_w(ICEROLE_CONTROLLING); 702 } 703 704 // Update remote ice_mode to all existing channels. 705 remote_ice_mode_ = remote_description_->ice_mode; 706 707 // Now that we have negotiated everything, push it downward. 708 // Note that we cache the result so that if we have race conditions 709 // between future SetRemote/SetLocal invocations and new channel 710 // creation, we have the negotiation state saved until a new 711 // negotiation happens. 712 for (ChannelMap::iterator iter = channels_.begin(); 713 iter != channels_.end(); 714 ++iter) { 715 if (!ApplyNegotiatedTransportDescription_w(iter->second.get())) 716 return false; 717 } 718 return true; 719} 720 721void Transport::OnMessage(talk_base::Message* msg) { 722 switch (msg->message_id) { 723 case MSG_ONSIGNALINGREADY: 724 CallChannels_w(&TransportChannelImpl::OnSignalingReady); 725 break; 726 case MSG_ONREMOTECANDIDATE: { 727 ChannelParams* params = static_cast<ChannelParams*>(msg->pdata); 728 OnRemoteCandidate_w(*params->candidate); 729 delete params; 730 } 731 break; 732 case MSG_CONNECTING: 733 OnConnecting_s(); 734 break; 735 case MSG_READSTATE: 736 OnChannelReadableState_s(); 737 break; 738 case MSG_WRITESTATE: 739 OnChannelWritableState_s(); 740 break; 741 case MSG_REQUESTSIGNALING: { 742 ChannelParams* params = static_cast<ChannelParams*>(msg->pdata); 743 OnChannelRequestSignaling_s(params->component); 744 delete params; 745 } 746 break; 747 case MSG_CANDIDATEREADY: 748 OnChannelCandidateReady_s(); 749 break; 750 case MSG_ROUTECHANGE: { 751 ChannelParams* params = static_cast<ChannelParams*>(msg->pdata); 752 OnChannelRouteChange_s(params->channel, *params->candidate); 753 delete params; 754 } 755 break; 756 case MSG_CANDIDATEALLOCATIONCOMPLETE: 757 OnChannelCandidatesAllocationDone_s(); 758 break; 759 case MSG_ROLECONFLICT: 760 SignalRoleConflict(); 761 break; 762 } 763} 764 765bool TransportParser::ParseAddress(const buzz::XmlElement* elem, 766 const buzz::QName& address_name, 767 const buzz::QName& port_name, 768 talk_base::SocketAddress* address, 769 ParseError* error) { 770 if (!elem->HasAttr(address_name)) 771 return BadParse("address does not have " + address_name.LocalPart(), error); 772 if (!elem->HasAttr(port_name)) 773 return BadParse("address does not have " + port_name.LocalPart(), error); 774 775 address->SetIP(elem->Attr(address_name)); 776 std::istringstream ist(elem->Attr(port_name)); 777 int port = 0; 778 ist >> port; 779 address->SetPort(port); 780 781 return true; 782} 783 784// We're GICE if the namespace is NS_GOOGLE_P2P, or if NS_JINGLE_ICE_UDP is 785// used and the GICE ice-option is set. 786TransportProtocol TransportProtocolFromDescription( 787 const TransportDescription* desc) { 788 ASSERT(desc != NULL); 789 if (desc->transport_type == NS_JINGLE_ICE_UDP) { 790 return (desc->HasOption(ICE_OPTION_GICE)) ? 791 ICEPROTO_HYBRID : ICEPROTO_RFC5245; 792 } 793 return ICEPROTO_GOOGLE; 794} 795 796} // namespace cricket 797