123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278 |
- // Copyright 2015 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "remoting/protocol/ice_transport_channel.h"
- #include <algorithm>
- #include <memory>
- #include <utility>
- #include "base/bind.h"
- #include "base/callback.h"
- #include "base/task/single_thread_task_runner.h"
- #include "base/threading/thread_task_runner_handle.h"
- #include "components/webrtc/net_address_utils.h"
- #include "net/base/net_errors.h"
- #include "remoting/protocol/channel_socket_adapter.h"
- #include "remoting/protocol/port_allocator_factory.h"
- #include "remoting/protocol/transport_context.h"
- #include "third_party/webrtc/p2p/base/p2p_constants.h"
- #include "third_party/webrtc/p2p/base/p2p_transport_channel.h"
- #include "third_party/webrtc/p2p/base/packet_transport_internal.h"
- #include "third_party/webrtc/p2p/base/port.h"
- namespace remoting {
- namespace protocol {
- namespace {
- const int kIceUfragLength = 16;
- // Utility function to map a cricket::Candidate string type to a
- // TransportRoute::RouteType enum value.
- TransportRoute::RouteType CandidateTypeToTransportRouteType(
- const std::string& candidate_type) {
- if (candidate_type == "local") {
- return TransportRoute::DIRECT;
- } else if (candidate_type == "stun" || candidate_type == "prflx") {
- return TransportRoute::STUN;
- } else if (candidate_type == "relay") {
- return TransportRoute::RELAY;
- } else {
- LOG(FATAL) << "Unknown candidate type: " << candidate_type;
- return TransportRoute::DIRECT;
- }
- }
- } // namespace
- IceTransportChannel::IceTransportChannel(
- scoped_refptr<TransportContext> transport_context)
- : transport_context_(transport_context),
- ice_username_fragment_(rtc::CreateRandomString(kIceUfragLength)),
- connect_attempts_left_(
- transport_context->network_settings().ice_reconnect_attempts) {
- DCHECK(!ice_username_fragment_.empty());
- }
- IceTransportChannel::~IceTransportChannel() {
- DCHECK(delegate_);
- DCHECK(thread_checker_.CalledOnValidThread());
- delegate_->OnChannelDeleted(this);
- auto task_runner = base::ThreadTaskRunnerHandle::Get();
- if (channel_)
- task_runner->DeleteSoon(FROM_HERE, channel_.release());
- if (port_allocator_)
- task_runner->DeleteSoon(FROM_HERE, port_allocator_.release());
- }
- void IceTransportChannel::Connect(const std::string& name,
- Delegate* delegate,
- ConnectedCallback callback) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(!name.empty());
- DCHECK(delegate);
- DCHECK(!callback.is_null());
- DCHECK(name_.empty());
- name_ = name;
- delegate_ = delegate;
- callback_ = std::move(callback);
- port_allocator_ =
- transport_context_->port_allocator_factory()->CreatePortAllocator(
- transport_context_, nullptr);
- // Create P2PTransportChannel, attach signal handlers and connect it.
- // TODO(sergeyu): Specify correct component ID for the channel.
- channel_ = std::make_unique<cricket::P2PTransportChannel>(
- std::string(), 0, port_allocator_.get());
- std::string ice_password = rtc::CreateRandomString(cricket::ICE_PWD_LENGTH);
- channel_->SetIceProtocolType(cricket::ICEPROTO_RFC5245);
- channel_->SetIceRole((transport_context_->role() == TransportRole::CLIENT)
- ? cricket::ICEROLE_CONTROLLING
- : cricket::ICEROLE_CONTROLLED);
- delegate_->OnChannelIceCredentials(this, ice_username_fragment_,
- ice_password);
- channel_->SetIceCredentials(ice_username_fragment_, ice_password);
- channel_->SignalCandidateGathered.connect(
- this, &IceTransportChannel::OnCandidateGathered);
- channel_->SignalRouteChange.connect(
- this, &IceTransportChannel::OnRouteChange);
- channel_->SignalWritableState.connect(
- this, &IceTransportChannel::OnWritableState);
- channel_->set_incoming_only(!(transport_context_->network_settings().flags &
- NetworkSettings::NAT_TRAVERSAL_OUTGOING));
- channel_->Connect();
- channel_->MaybeStartGathering();
- // Pass pending ICE credentials and candidates to the channel.
- if (!remote_ice_username_fragment_.empty()) {
- channel_->SetRemoteIceCredentials(remote_ice_username_fragment_,
- remote_ice_password_);
- }
- while (!pending_candidates_.empty()) {
- channel_->AddRemoteCandidate(pending_candidates_.front());
- pending_candidates_.pop_front();
- }
- --connect_attempts_left_;
- // Start reconnection timer.
- reconnect_timer_.Start(FROM_HERE,
- transport_context_->network_settings().ice_timeout,
- this, &IceTransportChannel::TryReconnect);
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(&IceTransportChannel::NotifyConnected,
- weak_factory_.GetWeakPtr()));
- }
- void IceTransportChannel::NotifyConnected() {
- // Create P2PDatagramSocket adapter for the P2PTransportChannel.
- std::unique_ptr<TransportChannelSocketAdapter> socket(
- new TransportChannelSocketAdapter(channel_.get()));
- socket->SetOnDestroyedCallback(base::BindOnce(
- &IceTransportChannel::OnChannelDestroyed, base::Unretained(this)));
- std::move(callback_).Run(std::move(socket));
- }
- void IceTransportChannel::SetRemoteCredentials(const std::string& ufrag,
- const std::string& password) {
- DCHECK(thread_checker_.CalledOnValidThread());
- remote_ice_username_fragment_ = ufrag;
- remote_ice_password_ = password;
- if (channel_)
- channel_->SetRemoteIceCredentials(ufrag, password);
- }
- void IceTransportChannel::AddRemoteCandidate(
- const cricket::Candidate& candidate) {
- DCHECK(thread_checker_.CalledOnValidThread());
- // To enforce the no-relay setting, it's not enough to not produce relay
- // candidates. It's also necessary to discard remote relay candidates.
- bool relay_allowed = (transport_context_->network_settings().flags &
- NetworkSettings::NAT_TRAVERSAL_RELAY) != 0;
- if (!relay_allowed && candidate.type() == cricket::RELAY_PORT_TYPE)
- return;
- if (channel_) {
- channel_->AddRemoteCandidate(candidate);
- } else {
- pending_candidates_.push_back(candidate);
- }
- }
- const std::string& IceTransportChannel::name() const {
- DCHECK(thread_checker_.CalledOnValidThread());
- return name_;
- }
- bool IceTransportChannel::is_connected() const {
- DCHECK(thread_checker_.CalledOnValidThread());
- return callback_.is_null();
- }
- void IceTransportChannel::OnCandidateGathered(
- cricket::IceTransportInternal* ice_transport,
- const cricket::Candidate& candidate) {
- DCHECK(thread_checker_.CalledOnValidThread());
- delegate_->OnChannelCandidate(this, candidate);
- }
- void IceTransportChannel::OnRouteChange(
- cricket::IceTransportInternal* ice_transport,
- const cricket::Candidate& candidate) {
- // Ignore notifications if the channel is not writable.
- if (channel_->writable())
- NotifyRouteChanged();
- }
- void IceTransportChannel::OnWritableState(
- rtc::PacketTransportInternal* transport) {
- DCHECK_EQ(transport,
- static_cast<rtc::PacketTransportInternal*>(channel_.get()));
- if (transport->writable()) {
- connect_attempts_left_ =
- transport_context_->network_settings().ice_reconnect_attempts;
- reconnect_timer_.Stop();
- // Route change notifications are ignored when the |channel_| is not
- // writable. Notify the event handler about the current route once the
- // channel is writable.
- NotifyRouteChanged();
- } else {
- reconnect_timer_.Reset();
- TryReconnect();
- }
- }
- void IceTransportChannel::OnChannelDestroyed() {
- // The connection socket is being deleted, so delete the transport too.
- delete this;
- }
- void IceTransportChannel::NotifyRouteChanged() {
- TransportRoute route;
- DCHECK(channel_->best_connection());
- const cricket::Connection* connection = channel_->best_connection();
- // A connection has both a local and a remote candidate. For our purposes, the
- // route type is determined by the most indirect candidate type. For example:
- // it's possible for the local candidate be a "relay" type, while the remote
- // candidate is "local". In this case, we still want to report a RELAY route
- // type.
- static_assert(TransportRoute::DIRECT < TransportRoute::STUN &&
- TransportRoute::STUN < TransportRoute::RELAY,
- "Route type enum values are ordered by 'indirectness'");
- route.type = std::max(
- CandidateTypeToTransportRouteType(connection->local_candidate().type()),
- CandidateTypeToTransportRouteType(connection->remote_candidate().type()));
- if (!webrtc::SocketAddressToIPEndPoint(
- connection->remote_candidate().address(), &route.remote_address)) {
- LOG(FATAL) << "Failed to convert peer IP address.";
- }
- const cricket::Candidate& local_candidate =
- channel_->best_connection()->local_candidate();
- if (!webrtc::SocketAddressToIPEndPoint(local_candidate.address(),
- &route.local_address)) {
- LOG(FATAL) << "Failed to convert local IP address.";
- }
- delegate_->OnChannelRouteChange(this, route);
- }
- void IceTransportChannel::TryReconnect() {
- DCHECK(!channel_->writable());
- if (connect_attempts_left_ <= 0) {
- reconnect_timer_.Stop();
- // Notify the caller that ICE connection has failed - normally that will
- // terminate Jingle connection (i.e. the transport will be destroyed).
- delegate_->OnChannelFailed(this);
- return;
- }
- --connect_attempts_left_;
- // Restart ICE by resetting ICE password.
- std::string ice_password = rtc::CreateRandomString(cricket::ICE_PWD_LENGTH);
- delegate_->OnChannelIceCredentials(this, ice_username_fragment_,
- ice_password);
- channel_->SetIceCredentials(ice_username_fragment_, ice_password);
- }
- } // namespace protocol
- } // namespace remoting
|