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 "base/callback.h"
8 #include "base/single_thread_task_runner.h"
9 #include "base/thread_task_runner_handle.h"
10 #include "base/timer/timer.h"
11 #include "jingle/glue/channel_socket_adapter.h"
12 #include "jingle/glue/utils.h"
13 #include "net/base/net_errors.h"
14 #include "remoting/protocol/network_settings.h"
15 #include "remoting/signaling/jingle_info_request.h"
16 #include "third_party/webrtc/base/network.h"
17 #include "third_party/webrtc/p2p/base/constants.h"
18 #include "third_party/webrtc/p2p/base/p2ptransportchannel.h"
19 #include "third_party/webrtc/p2p/base/port.h"
20 #include "third_party/webrtc/p2p/client/basicportallocator.h"
21 #include "third_party/webrtc/p2p/client/httpportallocator.h"
28 // Try connecting ICE twice with timeout of 15 seconds for each attempt.
29 const int kMaxReconnectAttempts
= 2;
30 const int kReconnectDelaySeconds
= 15;
32 // Get fresh STUN/Relay configuration every hour.
33 const int kJingleInfoUpdatePeriodSeconds
= 3600;
35 class LibjingleTransport
37 public base::SupportsWeakPtr
<LibjingleTransport
>,
38 public sigslot::has_slots
<> {
40 LibjingleTransport(cricket::PortAllocator
* port_allocator
,
41 const NetworkSettings
& network_settings
);
42 ~LibjingleTransport() override
;
44 // Called by JingleTransportFactory when it has fresh Jingle info.
47 // Transport interface.
48 void Connect(const std::string
& name
,
49 Transport::EventHandler
* event_handler
,
50 const Transport::ConnectedCallback
& callback
) override
;
51 void AddRemoteCandidate(const cricket::Candidate
& candidate
) override
;
52 const std::string
& name() const override
;
53 bool is_connected() const override
;
57 void NotifyConnected();
59 // Signal handlers for cricket::TransportChannel.
60 void OnRequestSignaling(cricket::TransportChannelImpl
* channel
);
61 void OnCandidateReady(cricket::TransportChannelImpl
* channel
,
62 const cricket::Candidate
& candidate
);
63 void OnRouteChange(cricket::TransportChannel
* channel
,
64 const cricket::Candidate
& candidate
);
65 void OnWritableState(cricket::TransportChannel
* channel
);
67 // Callback for jingle_glue::TransportChannelSocketAdapter to notify when the
68 // socket is destroyed.
69 void OnChannelDestroyed();
71 // Tries to connect by restarting ICE. Called by |reconnect_timer_|.
74 cricket::PortAllocator
* port_allocator_
;
75 NetworkSettings network_settings_
;
78 EventHandler
* event_handler_
;
79 Transport::ConnectedCallback callback_
;
80 std::string ice_username_fragment_
;
81 std::string ice_password_
;
85 std::list
<cricket::Candidate
> pending_candidates_
;
86 scoped_ptr
<cricket::P2PTransportChannel
> channel_
;
87 bool channel_was_writable_
;
88 int connect_attempts_left_
;
89 base::RepeatingTimer
<LibjingleTransport
> reconnect_timer_
;
91 base::WeakPtrFactory
<LibjingleTransport
> weak_factory_
;
93 DISALLOW_COPY_AND_ASSIGN(LibjingleTransport
);
96 LibjingleTransport::LibjingleTransport(cricket::PortAllocator
* port_allocator
,
97 const NetworkSettings
& network_settings
)
98 : port_allocator_(port_allocator
),
99 network_settings_(network_settings
),
100 event_handler_(NULL
),
101 ice_username_fragment_(
102 rtc::CreateRandomString(cricket::ICE_UFRAG_LENGTH
)),
103 ice_password_(rtc::CreateRandomString(cricket::ICE_PWD_LENGTH
)),
105 channel_was_writable_(false),
106 connect_attempts_left_(kMaxReconnectAttempts
),
107 weak_factory_(this) {
108 DCHECK(!ice_username_fragment_
.empty());
109 DCHECK(!ice_password_
.empty());
112 LibjingleTransport::~LibjingleTransport() {
113 DCHECK(event_handler_
);
115 event_handler_
->OnTransportDeleted(this);
117 if (channel_
.get()) {
118 base::ThreadTaskRunnerHandle::Get()->DeleteSoon(
119 FROM_HERE
, channel_
.release());
123 void LibjingleTransport::OnCanStart() {
124 DCHECK(CalledOnValidThread());
129 // If Connect() has been called then start connection.
130 if (!callback_
.is_null())
133 while (!pending_candidates_
.empty()) {
134 channel_
->OnCandidate(pending_candidates_
.front());
135 pending_candidates_
.pop_front();
139 void LibjingleTransport::Connect(
140 const std::string
& name
,
141 Transport::EventHandler
* event_handler
,
142 const Transport::ConnectedCallback
& callback
) {
143 DCHECK(CalledOnValidThread());
144 DCHECK(!name
.empty());
145 DCHECK(event_handler
);
146 DCHECK(!callback
.is_null());
148 DCHECK(name_
.empty());
150 event_handler_
= event_handler
;
151 callback_
= callback
;
157 void LibjingleTransport::DoStart() {
158 DCHECK(!channel_
.get());
160 // Create P2PTransportChannel, attach signal handlers and connect it.
161 // TODO(sergeyu): Specify correct component ID for the channel.
162 channel_
.reset(new cricket::P2PTransportChannel(
163 std::string(), 0, NULL
, port_allocator_
));
164 channel_
->SetIceProtocolType(cricket::ICEPROTO_GOOGLE
);
165 channel_
->SetIceCredentials(ice_username_fragment_
, ice_password_
);
166 channel_
->SignalRequestSignaling
.connect(
167 this, &LibjingleTransport::OnRequestSignaling
);
168 channel_
->SignalCandidateReady
.connect(
169 this, &LibjingleTransport::OnCandidateReady
);
170 channel_
->SignalRouteChange
.connect(
171 this, &LibjingleTransport::OnRouteChange
);
172 channel_
->SignalWritableState
.connect(
173 this, &LibjingleTransport::OnWritableState
);
174 channel_
->set_incoming_only(
175 !(network_settings_
.flags
& NetworkSettings::NAT_TRAVERSAL_OUTGOING
));
179 --connect_attempts_left_
;
181 // Start reconnection timer.
182 reconnect_timer_
.Start(
183 FROM_HERE
, base::TimeDelta::FromSeconds(kReconnectDelaySeconds
),
184 this, &LibjingleTransport::TryReconnect
);
187 void LibjingleTransport::NotifyConnected() {
188 // Create net::Socket adapter for the P2PTransportChannel.
189 scoped_ptr
<jingle_glue::TransportChannelSocketAdapter
> socket(
190 new jingle_glue::TransportChannelSocketAdapter(channel_
.get()));
191 socket
->SetOnDestroyedCallback(base::Bind(
192 &LibjingleTransport::OnChannelDestroyed
, base::Unretained(this)));
194 Transport::ConnectedCallback callback
= callback_
;
196 callback
.Run(socket
.Pass());
199 void LibjingleTransport::AddRemoteCandidate(
200 const cricket::Candidate
& candidate
) {
201 DCHECK(CalledOnValidThread());
203 // To enforce the no-relay setting, it's not enough to not produce relay
204 // candidates. It's also necessary to discard remote relay candidates.
205 bool relay_allowed
= (network_settings_
.flags
&
206 NetworkSettings::NAT_TRAVERSAL_RELAY
) != 0;
207 if (!relay_allowed
&& candidate
.type() == cricket::RELAY_PORT_TYPE
)
211 channel_
->OnCandidate(candidate
);
213 pending_candidates_
.push_back(candidate
);
217 const std::string
& LibjingleTransport::name() const {
218 DCHECK(CalledOnValidThread());
222 bool LibjingleTransport::is_connected() const {
223 DCHECK(CalledOnValidThread());
224 return callback_
.is_null();
227 void LibjingleTransport::OnRequestSignaling(
228 cricket::TransportChannelImpl
* channel
) {
229 DCHECK(CalledOnValidThread());
230 channel_
->OnSignalingReady();
233 void LibjingleTransport::OnCandidateReady(
234 cricket::TransportChannelImpl
* channel
,
235 const cricket::Candidate
& candidate
) {
236 DCHECK(CalledOnValidThread());
237 event_handler_
->OnTransportCandidate(this, candidate
);
240 void LibjingleTransport::OnRouteChange(
241 cricket::TransportChannel
* channel
,
242 const cricket::Candidate
& candidate
) {
243 TransportRoute route
;
245 if (candidate
.type() == "local") {
246 route
.type
= TransportRoute::DIRECT
;
247 } else if (candidate
.type() == "stun") {
248 route
.type
= TransportRoute::STUN
;
249 } else if (candidate
.type() == "relay") {
250 route
.type
= TransportRoute::RELAY
;
252 LOG(FATAL
) << "Unknown candidate type: " << candidate
.type();
255 if (!jingle_glue::SocketAddressToIPEndPoint(
256 candidate
.address(), &route
.remote_address
)) {
257 LOG(FATAL
) << "Failed to convert peer IP address.";
260 DCHECK(channel_
->best_connection());
261 const cricket::Candidate
& local_candidate
=
262 channel_
->best_connection()->local_candidate();
263 if (!jingle_glue::SocketAddressToIPEndPoint(
264 local_candidate
.address(), &route
.local_address
)) {
265 LOG(FATAL
) << "Failed to convert local IP address.";
268 event_handler_
->OnTransportRouteChange(this, route
);
271 void LibjingleTransport::OnWritableState(
272 cricket::TransportChannel
* channel
) {
273 DCHECK_EQ(channel
, channel_
.get());
275 if (channel
->writable()) {
276 if (!channel_was_writable_
) {
277 channel_was_writable_
= true;
278 base::ThreadTaskRunnerHandle::Get()->PostTask(
280 base::Bind(&LibjingleTransport::NotifyConnected
,
281 weak_factory_
.GetWeakPtr()));
283 connect_attempts_left_
= kMaxReconnectAttempts
;
284 reconnect_timer_
.Stop();
285 } else if (!channel
->writable() && channel_was_writable_
) {
286 reconnect_timer_
.Reset();
291 void LibjingleTransport::OnChannelDestroyed() {
292 if (is_connected()) {
293 // The connection socket is being deleted, so delete the transport too.
298 void LibjingleTransport::TryReconnect() {
299 DCHECK(!channel_
->writable());
301 if (connect_attempts_left_
<= 0) {
302 reconnect_timer_
.Stop();
304 // Notify the caller that ICE connection has failed - normally that will
305 // terminate Jingle connection (i.e. the transport will be destroyed).
306 event_handler_
->OnTransportFailed(this);
309 --connect_attempts_left_
;
311 // Restart ICE by resetting ICE password.
312 ice_password_
= rtc::CreateRandomString(cricket::ICE_PWD_LENGTH
);
313 channel_
->SetIceCredentials(ice_username_fragment_
, ice_password_
);
318 LibjingleTransportFactory::LibjingleTransportFactory(
319 SignalStrategy
* signal_strategy
,
320 scoped_ptr
<cricket::HttpPortAllocatorBase
> port_allocator
,
321 const NetworkSettings
& network_settings
)
322 : signal_strategy_(signal_strategy
),
323 port_allocator_(port_allocator
.Pass()),
324 network_settings_(network_settings
) {
327 LibjingleTransportFactory::~LibjingleTransportFactory() {
328 // This method may be called in response to a libjingle signal, so
329 // libjingle objects must be deleted asynchronously.
330 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner
=
331 base::ThreadTaskRunnerHandle::Get();
332 task_runner
->DeleteSoon(FROM_HERE
, port_allocator_
.release());
335 void LibjingleTransportFactory::PrepareTokens() {
336 EnsureFreshJingleInfo();
339 scoped_ptr
<Transport
> LibjingleTransportFactory::CreateTransport() {
340 scoped_ptr
<LibjingleTransport
> result(
341 new LibjingleTransport(port_allocator_
.get(), network_settings_
));
343 EnsureFreshJingleInfo();
345 // If there is a pending |jingle_info_request_| delay starting the new
346 // transport until the request is finished.
347 if (jingle_info_request_
) {
348 on_jingle_info_callbacks_
.push_back(
349 base::Bind(&LibjingleTransport::OnCanStart
,
350 result
->AsWeakPtr()));
352 result
->OnCanStart();
355 return result
.Pass();
358 void LibjingleTransportFactory::EnsureFreshJingleInfo() {
359 uint32 stun_or_relay_flags
= NetworkSettings::NAT_TRAVERSAL_STUN
|
360 NetworkSettings::NAT_TRAVERSAL_RELAY
;
361 if (!(network_settings_
.flags
& stun_or_relay_flags
) ||
362 jingle_info_request_
) {
366 if (base::TimeTicks::Now() - last_jingle_info_update_time_
>
367 base::TimeDelta::FromSeconds(kJingleInfoUpdatePeriodSeconds
)) {
368 jingle_info_request_
.reset(new JingleInfoRequest(signal_strategy_
));
369 jingle_info_request_
->Send(base::Bind(
370 &LibjingleTransportFactory::OnJingleInfo
, base::Unretained(this)));
374 void LibjingleTransportFactory::OnJingleInfo(
375 const std::string
& relay_token
,
376 const std::vector
<std::string
>& relay_hosts
,
377 const std::vector
<rtc::SocketAddress
>& stun_hosts
) {
378 if (!relay_token
.empty() && !relay_hosts
.empty()) {
379 port_allocator_
->SetRelayHosts(relay_hosts
);
380 port_allocator_
->SetRelayToken(relay_token
);
382 if (!stun_hosts
.empty()) {
383 port_allocator_
->SetStunHosts(stun_hosts
);
386 jingle_info_request_
.reset();
387 if ((!relay_token
.empty() && !relay_hosts
.empty()) || !stun_hosts
.empty())
388 last_jingle_info_update_time_
= base::TimeTicks::Now();
390 while (!on_jingle_info_callbacks_
.empty()) {
391 on_jingle_info_callbacks_
.begin()->Run();
392 on_jingle_info_callbacks_
.pop_front();
396 } // namespace protocol
397 } // namespace remoting