Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / remoting / protocol / libjingle_transport_factory.cc
blob50557fdb86f4f4d6be95f151ec3cdd976d0aed43
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
3 // found in the LICENSE file.
5 #include "remoting/protocol/libjingle_transport_factory.h"
7 #include <algorithm>
9 #include "base/callback.h"
10 #include "base/callback_helpers.h"
11 #include "base/single_thread_task_runner.h"
12 #include "base/thread_task_runner_handle.h"
13 #include "base/timer/timer.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/network_settings.h"
18 #include "remoting/signaling/jingle_info_request.h"
19 #include "third_party/webrtc/base/network.h"
20 #include "third_party/webrtc/p2p/base/constants.h"
21 #include "third_party/webrtc/p2p/base/p2ptransportchannel.h"
22 #include "third_party/webrtc/p2p/base/port.h"
23 #include "third_party/webrtc/p2p/client/basicportallocator.h"
24 #include "third_party/webrtc/p2p/client/httpportallocator.h"
26 namespace remoting {
27 namespace protocol {
29 namespace {
31 // Try connecting ICE twice with timeout of 15 seconds for each attempt.
32 const int kMaxReconnectAttempts = 2;
33 const int kReconnectDelaySeconds = 15;
35 // Get fresh STUN/Relay configuration every hour.
36 const int kJingleInfoUpdatePeriodSeconds = 3600;
38 // Utility function to map a cricket::Candidate string type to a
39 // TransportRoute::RouteType enum value.
40 TransportRoute::RouteType CandidateTypeToTransportRouteType(
41 const std::string& candidate_type) {
42 if (candidate_type == "local") {
43 return TransportRoute::DIRECT;
44 } else if (candidate_type == "stun" || candidate_type == "prflx") {
45 return TransportRoute::STUN;
46 } else if (candidate_type == "relay") {
47 return TransportRoute::RELAY;
48 } else {
49 LOG(FATAL) << "Unknown candidate type: " << candidate_type;
50 return TransportRoute::DIRECT;
54 class LibjingleTransport
55 : public Transport,
56 public base::SupportsWeakPtr<LibjingleTransport>,
57 public sigslot::has_slots<> {
58 public:
59 LibjingleTransport(cricket::PortAllocator* port_allocator,
60 const NetworkSettings& network_settings,
61 TransportRole role);
62 ~LibjingleTransport() override;
64 // Called by JingleTransportFactory when it has fresh Jingle info.
65 void OnCanStart();
67 // Transport interface.
68 void Connect(const std::string& name,
69 Transport::EventHandler* event_handler,
70 const Transport::ConnectedCallback& callback) override;
71 void SetRemoteCredentials(const std::string& ufrag,
72 const std::string& password) override;
73 void AddRemoteCandidate(const cricket::Candidate& candidate) override;
74 const std::string& name() const override;
75 bool is_connected() const override;
76 void SetUseStandardIce(bool use_standard_ice) override;
78 private:
79 void DoStart();
80 void NotifyConnected();
82 // Signal handlers for cricket::TransportChannel.
83 void OnRequestSignaling(cricket::TransportChannelImpl* channel);
84 void OnCandidateReady(cricket::TransportChannelImpl* channel,
85 const cricket::Candidate& candidate);
86 void OnRouteChange(cricket::TransportChannel* channel,
87 const cricket::Candidate& candidate);
88 void OnWritableState(cricket::TransportChannel* channel);
90 // Callback for TransportChannelSocketAdapter to notify when the socket is
91 // destroyed.
92 void OnChannelDestroyed();
94 void NotifyRouteChanged();
96 // Tries to connect by restarting ICE. Called by |reconnect_timer_|.
97 void TryReconnect();
99 cricket::PortAllocator* port_allocator_;
100 NetworkSettings network_settings_;
101 TransportRole role_;
103 bool use_standard_ice_ = true;
105 std::string name_;
106 EventHandler* event_handler_;
107 Transport::ConnectedCallback callback_;
108 std::string ice_username_fragment_;
110 bool can_start_;
112 std::string remote_ice_username_fragment_;
113 std::string remote_ice_password_;
114 std::list<cricket::Candidate> pending_candidates_;
115 scoped_ptr<cricket::P2PTransportChannel> channel_;
116 int connect_attempts_left_;
117 base::RepeatingTimer<LibjingleTransport> reconnect_timer_;
119 base::WeakPtrFactory<LibjingleTransport> weak_factory_;
121 DISALLOW_COPY_AND_ASSIGN(LibjingleTransport);
124 LibjingleTransport::LibjingleTransport(cricket::PortAllocator* port_allocator,
125 const NetworkSettings& network_settings,
126 TransportRole role)
127 : port_allocator_(port_allocator),
128 network_settings_(network_settings),
129 role_(role),
130 event_handler_(nullptr),
131 ice_username_fragment_(
132 rtc::CreateRandomString(cricket::ICE_UFRAG_LENGTH)),
133 can_start_(false),
134 connect_attempts_left_(kMaxReconnectAttempts),
135 weak_factory_(this) {
136 DCHECK(!ice_username_fragment_.empty());
139 LibjingleTransport::~LibjingleTransport() {
140 DCHECK(event_handler_);
142 event_handler_->OnTransportDeleted(this);
144 if (channel_.get()) {
145 base::ThreadTaskRunnerHandle::Get()->DeleteSoon(
146 FROM_HERE, channel_.release());
150 void LibjingleTransport::OnCanStart() {
151 DCHECK(CalledOnValidThread());
153 DCHECK(!can_start_);
154 can_start_ = true;
156 // If Connect() has been called then start connection.
157 if (!callback_.is_null())
158 DoStart();
160 // Pass pending ICE credentials and candidates to the channel.
161 if (!remote_ice_username_fragment_.empty()) {
162 channel_->SetRemoteIceCredentials(remote_ice_username_fragment_,
163 remote_ice_password_);
166 while (!pending_candidates_.empty()) {
167 if (!use_standard_ice_) {
168 channel_->SetRemoteIceCredentials(pending_candidates_.front().username(),
169 pending_candidates_.front().password());
171 channel_->OnCandidate(pending_candidates_.front());
172 pending_candidates_.pop_front();
176 void LibjingleTransport::Connect(
177 const std::string& name,
178 Transport::EventHandler* event_handler,
179 const Transport::ConnectedCallback& callback) {
180 DCHECK(CalledOnValidThread());
181 DCHECK(!name.empty());
182 DCHECK(event_handler);
183 DCHECK(!callback.is_null());
185 DCHECK(name_.empty());
186 name_ = name;
187 event_handler_ = event_handler;
188 callback_ = callback;
190 if (can_start_)
191 DoStart();
194 void LibjingleTransport::DoStart() {
195 DCHECK(!channel_.get());
197 // Create P2PTransportChannel, attach signal handlers and connect it.
198 // TODO(sergeyu): Specify correct component ID for the channel.
199 channel_.reset(new cricket::P2PTransportChannel(
200 std::string(), 0, nullptr, port_allocator_));
201 std::string ice_password = rtc::CreateRandomString(cricket::ICE_PWD_LENGTH);
202 if (use_standard_ice_) {
203 channel_->SetIceProtocolType(cricket::ICEPROTO_RFC5245);
204 channel_->SetIceRole((role_ == TransportRole::CLIENT)
205 ? cricket::ICEROLE_CONTROLLING
206 : cricket::ICEROLE_CONTROLLED);
207 event_handler_->OnTransportIceCredentials(this, ice_username_fragment_,
208 ice_password);
209 } else {
210 channel_->SetIceProtocolType(cricket::ICEPROTO_GOOGLE);
212 channel_->SetIceCredentials(ice_username_fragment_, ice_password);
213 channel_->SignalRequestSignaling.connect(
214 this, &LibjingleTransport::OnRequestSignaling);
215 channel_->SignalCandidateReady.connect(
216 this, &LibjingleTransport::OnCandidateReady);
217 channel_->SignalRouteChange.connect(
218 this, &LibjingleTransport::OnRouteChange);
219 channel_->SignalWritableState.connect(
220 this, &LibjingleTransport::OnWritableState);
221 channel_->set_incoming_only(
222 !(network_settings_.flags & NetworkSettings::NAT_TRAVERSAL_OUTGOING));
224 channel_->Connect();
226 --connect_attempts_left_;
228 // Start reconnection timer.
229 reconnect_timer_.Start(
230 FROM_HERE, base::TimeDelta::FromSeconds(kReconnectDelaySeconds),
231 this, &LibjingleTransport::TryReconnect);
233 base::ThreadTaskRunnerHandle::Get()->PostTask(
234 FROM_HERE, base::Bind(&LibjingleTransport::NotifyConnected,
235 weak_factory_.GetWeakPtr()));
238 void LibjingleTransport::NotifyConnected() {
239 // Create P2PDatagramSocket adapter for the P2PTransportChannel.
240 scoped_ptr<TransportChannelSocketAdapter> socket(
241 new TransportChannelSocketAdapter(channel_.get()));
242 socket->SetOnDestroyedCallback(base::Bind(
243 &LibjingleTransport::OnChannelDestroyed, base::Unretained(this)));
244 base::ResetAndReturn(&callback_).Run(socket.Pass());
247 void LibjingleTransport::SetRemoteCredentials(const std::string& ufrag,
248 const std::string& password) {
249 DCHECK(CalledOnValidThread());
251 remote_ice_username_fragment_ = ufrag;
252 remote_ice_password_ = password;
254 if (channel_)
255 channel_->SetRemoteIceCredentials(ufrag, password);
258 void LibjingleTransport::AddRemoteCandidate(
259 const cricket::Candidate& candidate) {
260 DCHECK(CalledOnValidThread());
262 // To enforce the no-relay setting, it's not enough to not produce relay
263 // candidates. It's also necessary to discard remote relay candidates.
264 bool relay_allowed = (network_settings_.flags &
265 NetworkSettings::NAT_TRAVERSAL_RELAY) != 0;
266 if (!relay_allowed && candidate.type() == cricket::RELAY_PORT_TYPE)
267 return;
269 if (channel_) {
270 if (!use_standard_ice_) {
271 channel_->SetRemoteIceCredentials(candidate.username(),
272 candidate.password());
274 channel_->OnCandidate(candidate);
275 } else {
276 pending_candidates_.push_back(candidate);
280 const std::string& LibjingleTransport::name() const {
281 DCHECK(CalledOnValidThread());
282 return name_;
285 bool LibjingleTransport::is_connected() const {
286 DCHECK(CalledOnValidThread());
287 return callback_.is_null();
290 void LibjingleTransport::SetUseStandardIce(bool use_standard_ice) {
291 DCHECK(CalledOnValidThread());
292 DCHECK(!channel_);
293 use_standard_ice_ = use_standard_ice;
296 void LibjingleTransport::OnRequestSignaling(
297 cricket::TransportChannelImpl* channel) {
298 DCHECK(CalledOnValidThread());
299 channel_->OnSignalingReady();
302 void LibjingleTransport::OnCandidateReady(
303 cricket::TransportChannelImpl* channel,
304 const cricket::Candidate& candidate) {
305 DCHECK(CalledOnValidThread());
306 event_handler_->OnTransportCandidate(this, candidate);
309 void LibjingleTransport::OnRouteChange(
310 cricket::TransportChannel* channel,
311 const cricket::Candidate& candidate) {
312 // Ignore notifications if the channel is not writable.
313 if (channel_->writable())
314 NotifyRouteChanged();
317 void LibjingleTransport::OnWritableState(
318 cricket::TransportChannel* channel) {
319 DCHECK_EQ(channel, channel_.get());
321 if (channel->writable()) {
322 connect_attempts_left_ = kMaxReconnectAttempts;
323 reconnect_timer_.Stop();
325 // Route change notifications are ignored when the |channel_| is not
326 // writable. Notify the event handler about the current route once the
327 // channel is writable.
328 NotifyRouteChanged();
329 } else {
330 reconnect_timer_.Reset();
331 TryReconnect();
335 void LibjingleTransport::OnChannelDestroyed() {
336 // The connection socket is being deleted, so delete the transport too.
337 delete this;
340 void LibjingleTransport::NotifyRouteChanged() {
341 TransportRoute route;
343 DCHECK(channel_->best_connection());
344 const cricket::Connection* connection = channel_->best_connection();
346 // A connection has both a local and a remote candidate. For our purposes, the
347 // route type is determined by the most indirect candidate type. For example:
348 // it's possible for the local candidate be a "relay" type, while the remote
349 // candidate is "local". In this case, we still want to report a RELAY route
350 // type.
351 static_assert(TransportRoute::DIRECT < TransportRoute::STUN &&
352 TransportRoute::STUN < TransportRoute::RELAY,
353 "Route type enum values are ordered by 'indirectness'");
354 route.type = std::max(
355 CandidateTypeToTransportRouteType(connection->local_candidate().type()),
356 CandidateTypeToTransportRouteType(connection->remote_candidate().type()));
358 if (!jingle_glue::SocketAddressToIPEndPoint(
359 connection->remote_candidate().address(), &route.remote_address)) {
360 LOG(FATAL) << "Failed to convert peer IP address.";
363 const cricket::Candidate& local_candidate =
364 channel_->best_connection()->local_candidate();
365 if (!jingle_glue::SocketAddressToIPEndPoint(
366 local_candidate.address(), &route.local_address)) {
367 LOG(FATAL) << "Failed to convert local IP address.";
370 event_handler_->OnTransportRouteChange(this, route);
373 void LibjingleTransport::TryReconnect() {
374 DCHECK(!channel_->writable());
376 if (connect_attempts_left_ <= 0) {
377 reconnect_timer_.Stop();
379 // Notify the caller that ICE connection has failed - normally that will
380 // terminate Jingle connection (i.e. the transport will be destroyed).
381 event_handler_->OnTransportFailed(this);
382 return;
384 --connect_attempts_left_;
386 // Restart ICE by resetting ICE password.
387 std::string ice_password = rtc::CreateRandomString(cricket::ICE_PWD_LENGTH);
388 event_handler_->OnTransportIceCredentials(this, ice_username_fragment_,
389 ice_password);
390 channel_->SetIceCredentials(ice_username_fragment_, ice_password);
393 } // namespace
395 LibjingleTransportFactory::LibjingleTransportFactory(
396 SignalStrategy* signal_strategy,
397 scoped_ptr<cricket::HttpPortAllocatorBase> port_allocator,
398 const NetworkSettings& network_settings,
399 TransportRole role)
400 : signal_strategy_(signal_strategy),
401 port_allocator_(port_allocator.Pass()),
402 network_settings_(network_settings),
403 role_(role) {
406 LibjingleTransportFactory::~LibjingleTransportFactory() {
407 // This method may be called in response to a libjingle signal, so
408 // libjingle objects must be deleted asynchronously.
409 scoped_refptr<base::SingleThreadTaskRunner> task_runner =
410 base::ThreadTaskRunnerHandle::Get();
411 task_runner->DeleteSoon(FROM_HERE, port_allocator_.release());
414 void LibjingleTransportFactory::PrepareTokens() {
415 EnsureFreshJingleInfo();
418 scoped_ptr<Transport> LibjingleTransportFactory::CreateTransport() {
419 scoped_ptr<LibjingleTransport> result(
420 new LibjingleTransport(port_allocator_.get(), network_settings_, role_));
422 EnsureFreshJingleInfo();
424 // If there is a pending |jingle_info_request_| delay starting the new
425 // transport until the request is finished.
426 if (jingle_info_request_) {
427 on_jingle_info_callbacks_.push_back(
428 base::Bind(&LibjingleTransport::OnCanStart,
429 result->AsWeakPtr()));
430 } else {
431 result->OnCanStart();
434 return result.Pass();
437 void LibjingleTransportFactory::EnsureFreshJingleInfo() {
438 uint32 stun_or_relay_flags = NetworkSettings::NAT_TRAVERSAL_STUN |
439 NetworkSettings::NAT_TRAVERSAL_RELAY;
440 if (!(network_settings_.flags & stun_or_relay_flags) ||
441 jingle_info_request_) {
442 return;
445 if (base::TimeTicks::Now() - last_jingle_info_update_time_ >
446 base::TimeDelta::FromSeconds(kJingleInfoUpdatePeriodSeconds)) {
447 jingle_info_request_.reset(new JingleInfoRequest(signal_strategy_));
448 jingle_info_request_->Send(base::Bind(
449 &LibjingleTransportFactory::OnJingleInfo, base::Unretained(this)));
453 void LibjingleTransportFactory::OnJingleInfo(
454 const std::string& relay_token,
455 const std::vector<std::string>& relay_hosts,
456 const std::vector<rtc::SocketAddress>& stun_hosts) {
457 if (!relay_token.empty() && !relay_hosts.empty()) {
458 port_allocator_->SetRelayHosts(relay_hosts);
459 port_allocator_->SetRelayToken(relay_token);
461 if (!stun_hosts.empty()) {
462 port_allocator_->SetStunHosts(stun_hosts);
465 jingle_info_request_.reset();
466 if ((!relay_token.empty() && !relay_hosts.empty()) || !stun_hosts.empty())
467 last_jingle_info_update_time_ = base::TimeTicks::Now();
469 while (!on_jingle_info_callbacks_.empty()) {
470 on_jingle_info_callbacks_.begin()->Run();
471 on_jingle_info_callbacks_.pop_front();
475 } // namespace protocol
476 } // namespace remoting