1 // Copyright 2015 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/ice_transport_channel.h"
6 
7 #include <algorithm>
8 #include <utility>
9 
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/threading/thread_task_runner_handle.h"
14 #include "jingle/glue/utils.h"
15 #include "net/base/net_errors.h"
16 #include "remoting/protocol/channel_socket_adapter.h"
17 #include "remoting/protocol/port_allocator_factory.h"
18 #include "remoting/protocol/transport_context.h"
19 #include "third_party/webrtc/p2p/base/p2p_constants.h"
20 #include "third_party/webrtc/p2p/base/p2p_transport_channel.h"
21 #include "third_party/webrtc/p2p/base/packet_transport_internal.h"
22 #include "third_party/webrtc/p2p/base/port.h"
23 #include "third_party/webrtc/rtc_base/network.h"
24 
25 namespace remoting {
26 namespace protocol {
27 
28 namespace {
29 
30 const int kIceUfragLength = 16;
31 
32 // Utility function to map a cricket::Candidate string type to a
33 // TransportRoute::RouteType enum value.
CandidateTypeToTransportRouteType(const std::string & candidate_type)34 TransportRoute::RouteType CandidateTypeToTransportRouteType(
35     const std::string& candidate_type) {
36   if (candidate_type == "local") {
37     return TransportRoute::DIRECT;
38   } else if (candidate_type == "stun" || candidate_type == "prflx") {
39     return TransportRoute::STUN;
40   } else if (candidate_type == "relay") {
41     return TransportRoute::RELAY;
42   } else {
43     LOG(FATAL) << "Unknown candidate type: " << candidate_type;
44     return TransportRoute::DIRECT;
45   }
46 }
47 
48 }  // namespace
49 
IceTransportChannel(scoped_refptr<TransportContext> transport_context)50 IceTransportChannel::IceTransportChannel(
51     scoped_refptr<TransportContext> transport_context)
52     : transport_context_(transport_context),
53       ice_username_fragment_(rtc::CreateRandomString(kIceUfragLength)),
54       connect_attempts_left_(
55           transport_context->network_settings().ice_reconnect_attempts) {
56   DCHECK(!ice_username_fragment_.empty());
57 }
58 
~IceTransportChannel()59 IceTransportChannel::~IceTransportChannel() {
60   DCHECK(delegate_);
61   DCHECK(thread_checker_.CalledOnValidThread());
62 
63   delegate_->OnChannelDeleted(this);
64 
65   auto task_runner = base::ThreadTaskRunnerHandle::Get();
66   if (channel_)
67     task_runner->DeleteSoon(FROM_HERE, channel_.release());
68   if (port_allocator_)
69     task_runner->DeleteSoon(FROM_HERE, port_allocator_.release());
70 }
71 
Connect(const std::string & name,Delegate * delegate,ConnectedCallback callback)72 void IceTransportChannel::Connect(const std::string& name,
73                                   Delegate* delegate,
74                                   ConnectedCallback callback) {
75   DCHECK(thread_checker_.CalledOnValidThread());
76   DCHECK(!name.empty());
77   DCHECK(delegate);
78   DCHECK(!callback.is_null());
79 
80   DCHECK(name_.empty());
81   name_ = name;
82   delegate_ = delegate;
83   callback_ = std::move(callback);
84 
85   port_allocator_ =
86       transport_context_->port_allocator_factory()->CreatePortAllocator(
87           transport_context_, nullptr);
88 
89   // Create P2PTransportChannel, attach signal handlers and connect it.
90   // TODO(sergeyu): Specify correct component ID for the channel.
91   channel_.reset(new cricket::P2PTransportChannel(
92       std::string(), 0, port_allocator_.get()));
93   std::string ice_password = rtc::CreateRandomString(cricket::ICE_PWD_LENGTH);
94   channel_->SetIceProtocolType(cricket::ICEPROTO_RFC5245);
95   channel_->SetIceRole((transport_context_->role() == TransportRole::CLIENT)
96                            ? cricket::ICEROLE_CONTROLLING
97                            : cricket::ICEROLE_CONTROLLED);
98   delegate_->OnChannelIceCredentials(this, ice_username_fragment_,
99                                      ice_password);
100   channel_->SetIceCredentials(ice_username_fragment_, ice_password);
101   channel_->SignalCandidateGathered.connect(
102       this, &IceTransportChannel::OnCandidateGathered);
103   channel_->SignalRouteChange.connect(
104       this, &IceTransportChannel::OnRouteChange);
105   channel_->SignalWritableState.connect(
106       this, &IceTransportChannel::OnWritableState);
107   channel_->set_incoming_only(!(transport_context_->network_settings().flags &
108                                 NetworkSettings::NAT_TRAVERSAL_OUTGOING));
109 
110   channel_->Connect();
111   channel_->MaybeStartGathering();
112 
113   // Pass pending ICE credentials and candidates to the channel.
114   if (!remote_ice_username_fragment_.empty()) {
115     channel_->SetRemoteIceCredentials(remote_ice_username_fragment_,
116                                       remote_ice_password_);
117   }
118 
119   while (!pending_candidates_.empty()) {
120     channel_->AddRemoteCandidate(pending_candidates_.front());
121     pending_candidates_.pop_front();
122   }
123 
124   --connect_attempts_left_;
125 
126   // Start reconnection timer.
127   reconnect_timer_.Start(FROM_HERE,
128                          transport_context_->network_settings().ice_timeout,
129                          this, &IceTransportChannel::TryReconnect);
130 
131   base::ThreadTaskRunnerHandle::Get()->PostTask(
132       FROM_HERE, base::BindOnce(&IceTransportChannel::NotifyConnected,
133                                 weak_factory_.GetWeakPtr()));
134 }
135 
NotifyConnected()136 void IceTransportChannel::NotifyConnected() {
137   // Create P2PDatagramSocket adapter for the P2PTransportChannel.
138   std::unique_ptr<TransportChannelSocketAdapter> socket(
139       new TransportChannelSocketAdapter(channel_.get()));
140   socket->SetOnDestroyedCallback(base::BindOnce(
141       &IceTransportChannel::OnChannelDestroyed, base::Unretained(this)));
142   std::move(callback_).Run(std::move(socket));
143 }
144 
SetRemoteCredentials(const std::string & ufrag,const std::string & password)145 void IceTransportChannel::SetRemoteCredentials(const std::string& ufrag,
146                                                const std::string& password) {
147   DCHECK(thread_checker_.CalledOnValidThread());
148 
149   remote_ice_username_fragment_ = ufrag;
150   remote_ice_password_ = password;
151 
152   if (channel_)
153     channel_->SetRemoteIceCredentials(ufrag, password);
154 }
155 
AddRemoteCandidate(const cricket::Candidate & candidate)156 void IceTransportChannel::AddRemoteCandidate(
157     const cricket::Candidate& candidate) {
158   DCHECK(thread_checker_.CalledOnValidThread());
159 
160   // To enforce the no-relay setting, it's not enough to not produce relay
161   // candidates. It's also necessary to discard remote relay candidates.
162   bool relay_allowed = (transport_context_->network_settings().flags &
163                         NetworkSettings::NAT_TRAVERSAL_RELAY) != 0;
164   if (!relay_allowed && candidate.type() == cricket::RELAY_PORT_TYPE)
165     return;
166 
167   if (channel_) {
168     channel_->AddRemoteCandidate(candidate);
169   } else {
170     pending_candidates_.push_back(candidate);
171   }
172 }
173 
name() const174 const std::string& IceTransportChannel::name() const {
175   DCHECK(thread_checker_.CalledOnValidThread());
176   return name_;
177 }
178 
is_connected() const179 bool IceTransportChannel::is_connected() const {
180   DCHECK(thread_checker_.CalledOnValidThread());
181   return callback_.is_null();
182 }
183 
OnCandidateGathered(cricket::IceTransportInternal * ice_transport,const cricket::Candidate & candidate)184 void IceTransportChannel::OnCandidateGathered(
185     cricket::IceTransportInternal* ice_transport,
186     const cricket::Candidate& candidate) {
187   DCHECK(thread_checker_.CalledOnValidThread());
188   delegate_->OnChannelCandidate(this, candidate);
189 }
190 
OnRouteChange(cricket::IceTransportInternal * ice_transport,const cricket::Candidate & candidate)191 void IceTransportChannel::OnRouteChange(
192     cricket::IceTransportInternal* ice_transport,
193     const cricket::Candidate& candidate) {
194   // Ignore notifications if the channel is not writable.
195   if (channel_->writable())
196     NotifyRouteChanged();
197 }
198 
OnWritableState(rtc::PacketTransportInternal * transport)199 void IceTransportChannel::OnWritableState(
200     rtc::PacketTransportInternal* transport) {
201   DCHECK_EQ(transport,
202             static_cast<rtc::PacketTransportInternal*>(channel_.get()));
203 
204   if (transport->writable()) {
205     connect_attempts_left_ =
206         transport_context_->network_settings().ice_reconnect_attempts;
207     reconnect_timer_.Stop();
208 
209     // Route change notifications are ignored when the |channel_| is not
210     // writable. Notify the event handler about the current route once the
211     // channel is writable.
212     NotifyRouteChanged();
213   } else {
214     reconnect_timer_.Reset();
215     TryReconnect();
216   }
217 }
218 
OnChannelDestroyed()219 void IceTransportChannel::OnChannelDestroyed() {
220   // The connection socket is being deleted, so delete the transport too.
221   delete this;
222 }
223 
NotifyRouteChanged()224 void IceTransportChannel::NotifyRouteChanged() {
225   TransportRoute route;
226 
227   DCHECK(channel_->best_connection());
228   const cricket::Connection* connection = channel_->best_connection();
229 
230   // A connection has both a local and a remote candidate. For our purposes, the
231   // route type is determined by the most indirect candidate type. For example:
232   // it's possible for the local candidate be a "relay" type, while the remote
233   // candidate is "local". In this case, we still want to report a RELAY route
234   // type.
235   static_assert(TransportRoute::DIRECT < TransportRoute::STUN &&
236                 TransportRoute::STUN < TransportRoute::RELAY,
237                 "Route type enum values are ordered by 'indirectness'");
238   route.type = std::max(
239       CandidateTypeToTransportRouteType(connection->local_candidate().type()),
240       CandidateTypeToTransportRouteType(connection->remote_candidate().type()));
241 
242   if (!jingle_glue::SocketAddressToIPEndPoint(
243           connection->remote_candidate().address(), &route.remote_address)) {
244     LOG(FATAL) << "Failed to convert peer IP address.";
245   }
246 
247   const cricket::Candidate& local_candidate =
248       channel_->best_connection()->local_candidate();
249   if (!jingle_glue::SocketAddressToIPEndPoint(
250           local_candidate.address(), &route.local_address)) {
251     LOG(FATAL) << "Failed to convert local IP address.";
252   }
253 
254   delegate_->OnChannelRouteChange(this, route);
255 }
256 
TryReconnect()257 void IceTransportChannel::TryReconnect() {
258   DCHECK(!channel_->writable());
259 
260   if (connect_attempts_left_ <= 0) {
261     reconnect_timer_.Stop();
262 
263     // Notify the caller that ICE connection has failed - normally that will
264     // terminate Jingle connection (i.e. the transport will be destroyed).
265     delegate_->OnChannelFailed(this);
266     return;
267   }
268   --connect_attempts_left_;
269 
270   // Restart ICE by resetting ICE password.
271   std::string ice_password = rtc::CreateRandomString(cricket::ICE_PWD_LENGTH);
272   delegate_->OnChannelIceCredentials(this, ice_username_fragment_,
273                                      ice_password);
274   channel_->SetIceCredentials(ice_username_fragment_, ice_password);
275 }
276 
277 }  // namespace protocol
278 }  // namespace remoting
279