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