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/single_thread_task_runner.h"
8 #include "base/thread_task_runner_handle.h"
9 #include "base/timer/timer.h"
10 #include "jingle/glue/channel_socket_adapter.h"
11 #include "jingle/glue/pseudotcp_adapter.h"
12 #include "jingle/glue/thread_wrapper.h"
13 #include "jingle/glue/utils.h"
14 #include "net/base/net_errors.h"
15 #include "remoting/base/constants.h"
16 #include "remoting/jingle_glue/chromium_port_allocator.h"
17 #include "remoting/jingle_glue/chromium_socket_factory.h"
18 #include "remoting/jingle_glue/network_settings.h"
19 #include "remoting/protocol/channel_authenticator.h"
20 #include "remoting/protocol/transport_config.h"
21 #include "third_party/libjingle/source/talk/base/network.h"
22 #include "third_party/libjingle/source/talk/p2p/base/constants.h"
23 #include "third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.h"
24 #include "third_party/libjingle/source/talk/p2p/client/basicportallocator.h"
25 #include "third_party/libjingle/source/talk/p2p/client/httpportallocator.h"
32 // Value is chosen to balance the extra latency against the reduced
33 // load due to ACK traffic.
34 const int kTcpAckDelayMilliseconds
= 10;
36 // Values for the TCP send and receive buffer size. This should be tuned to
37 // accommodate high latency network but not backlog the decoding pipeline.
38 const int kTcpReceiveBufferSize
= 256 * 1024;
39 const int kTcpSendBufferSize
= kTcpReceiveBufferSize
+ 30 * 1024;
41 // Try connecting ICE twice with timeout of 15 seconds for each attempt.
42 const int kMaxReconnectAttempts
= 2;
43 const int kReconnectDelaySeconds
= 15;
45 class LibjingleStreamTransport
: public StreamTransport
,
46 public sigslot::has_slots
<> {
48 LibjingleStreamTransport(cricket::PortAllocator
* port_allocator
,
50 virtual ~LibjingleStreamTransport();
52 // StreamTransport interface.
53 virtual void Initialize(
54 const std::string
& name
,
55 Transport::EventHandler
* event_handler
,
56 scoped_ptr
<ChannelAuthenticator
> authenticator
) OVERRIDE
;
58 const StreamTransport::ConnectedCallback
& callback
) OVERRIDE
;
59 virtual void AddRemoteCandidate(const cricket::Candidate
& candidate
) OVERRIDE
;
60 virtual const std::string
& name() const OVERRIDE
;
61 virtual bool is_connected() const OVERRIDE
;
64 // Signal handlers for cricket::TransportChannel.
65 void OnRequestSignaling(cricket::TransportChannelImpl
* channel
);
66 void OnCandidateReady(cricket::TransportChannelImpl
* channel
,
67 const cricket::Candidate
& candidate
);
68 void OnRouteChange(cricket::TransportChannel
* channel
,
69 const cricket::Candidate
& candidate
);
70 void OnWritableState(cricket::TransportChannel
* channel
);
72 // Callback for PseudoTcpAdapter::Connect().
73 void OnTcpConnected(int result
);
75 // Callback for Authenticator::SecureAndAuthenticate();
76 void OnAuthenticationDone(net::Error error
,
77 scoped_ptr
<net::StreamSocket
> socket
);
79 // Callback for jingle_glue::TransportChannelSocketAdapter to notify when the
80 // socket is destroyed.
81 void OnChannelDestroyed();
83 // Tries to connect by restarting ICE. Called by |reconnect_timer_|.
86 // Helper methods to call |callback_|.
87 void NotifyConnected(scoped_ptr
<net::StreamSocket
> socket
);
88 void NotifyConnectFailed();
90 cricket::PortAllocator
* port_allocator_
;
94 EventHandler
* event_handler_
;
95 StreamTransport::ConnectedCallback callback_
;
96 scoped_ptr
<ChannelAuthenticator
> authenticator_
;
97 std::string ice_username_fragment_
;
98 std::string ice_password_
;
100 scoped_ptr
<cricket::P2PTransportChannel
> channel_
;
101 bool channel_was_writable_
;
102 int connect_attempts_left_
;
103 base::RepeatingTimer
<LibjingleStreamTransport
> reconnect_timer_
;
105 // We own |socket_| until it is connected.
106 scoped_ptr
<jingle_glue::PseudoTcpAdapter
> socket_
;
108 DISALLOW_COPY_AND_ASSIGN(LibjingleStreamTransport
);
111 LibjingleStreamTransport::LibjingleStreamTransport(
112 cricket::PortAllocator
* port_allocator
,
114 : port_allocator_(port_allocator
),
115 incoming_only_(incoming_only
),
116 event_handler_(NULL
),
117 ice_username_fragment_(
118 talk_base::CreateRandomString(cricket::ICE_UFRAG_LENGTH
)),
119 ice_password_(talk_base::CreateRandomString(cricket::ICE_PWD_LENGTH
)),
120 channel_was_writable_(false),
121 connect_attempts_left_(kMaxReconnectAttempts
) {
124 LibjingleStreamTransport::~LibjingleStreamTransport() {
125 DCHECK(event_handler_
);
126 event_handler_
->OnTransportDeleted(this);
127 // Channel should be already destroyed if we were connected.
128 DCHECK(!is_connected() || socket_
.get() == NULL
);
130 if (channel_
.get()) {
131 base::ThreadTaskRunnerHandle::Get()->DeleteSoon(
132 FROM_HERE
, channel_
.release());
136 void LibjingleStreamTransport::Initialize(
137 const std::string
& name
,
138 Transport::EventHandler
* event_handler
,
139 scoped_ptr
<ChannelAuthenticator
> authenticator
) {
140 DCHECK(CalledOnValidThread());
142 DCHECK(!name
.empty());
143 DCHECK(event_handler
);
145 // Can be initialized only once.
146 DCHECK(name_
.empty());
149 event_handler_
= event_handler
;
150 authenticator_
= authenticator
.Pass();
153 void LibjingleStreamTransport::Connect(
154 const StreamTransport::ConnectedCallback
& callback
) {
155 DCHECK(CalledOnValidThread());
157 callback_
= callback
;
159 DCHECK(!channel_
.get());
161 // Create P2PTransportChannel, attach signal handlers and connect it.
162 // TODO(sergeyu): Specify correct component ID for the channel.
163 channel_
.reset(new cricket::P2PTransportChannel(
164 std::string(), 0, NULL
, port_allocator_
));
165 channel_
->SetIceCredentials(ice_username_fragment_
, ice_password_
);
166 channel_
->SignalRequestSignaling
.connect(
167 this, &LibjingleStreamTransport::OnRequestSignaling
);
168 channel_
->SignalCandidateReady
.connect(
169 this, &LibjingleStreamTransport::OnCandidateReady
);
170 channel_
->SignalRouteChange
.connect(
171 this, &LibjingleStreamTransport::OnRouteChange
);
172 channel_
->SignalWritableState
.connect(
173 this, &LibjingleStreamTransport::OnWritableState
);
174 channel_
->set_incoming_only(incoming_only_
);
178 --connect_attempts_left_
;
180 // Start reconnection timer.
181 reconnect_timer_
.Start(
182 FROM_HERE
, base::TimeDelta::FromSeconds(kReconnectDelaySeconds
),
183 this, &LibjingleStreamTransport::TryReconnect
);
185 // Create net::Socket adapter for the P2PTransportChannel.
186 scoped_ptr
<jingle_glue::TransportChannelSocketAdapter
> channel_adapter(
187 new jingle_glue::TransportChannelSocketAdapter(channel_
.get()));
189 channel_adapter
->SetOnDestroyedCallback(base::Bind(
190 &LibjingleStreamTransport::OnChannelDestroyed
, base::Unretained(this)));
192 // Configure and connect PseudoTCP adapter.
194 new jingle_glue::PseudoTcpAdapter(channel_adapter
.release()));
195 socket_
->SetSendBufferSize(kTcpSendBufferSize
);
196 socket_
->SetReceiveBufferSize(kTcpReceiveBufferSize
);
197 socket_
->SetNoDelay(true);
198 socket_
->SetAckDelay(kTcpAckDelayMilliseconds
);
200 // TODO(sergeyu): This is a hack to improve latency of the video
201 // channel. Consider removing it once we have better flow control
203 if (name_
== kVideoChannelName
)
204 socket_
->SetWriteWaitsForSend(true);
206 int result
= socket_
->Connect(
207 base::Bind(&LibjingleStreamTransport::OnTcpConnected
,
208 base::Unretained(this)));
209 if (result
!= net::ERR_IO_PENDING
)
210 OnTcpConnected(result
);
213 void LibjingleStreamTransport::AddRemoteCandidate(
214 const cricket::Candidate
& candidate
) {
215 DCHECK(CalledOnValidThread());
216 channel_
->OnCandidate(candidate
);
219 const std::string
& LibjingleStreamTransport::name() const {
220 DCHECK(CalledOnValidThread());
224 bool LibjingleStreamTransport::is_connected() const {
225 DCHECK(CalledOnValidThread());
226 return callback_
.is_null();
229 void LibjingleStreamTransport::OnRequestSignaling(
230 cricket::TransportChannelImpl
* channel
) {
231 DCHECK(CalledOnValidThread());
232 channel_
->OnSignalingReady();
235 void LibjingleStreamTransport::OnCandidateReady(
236 cricket::TransportChannelImpl
* channel
,
237 const cricket::Candidate
& candidate
) {
238 DCHECK(CalledOnValidThread());
239 event_handler_
->OnTransportCandidate(this, candidate
);
242 void LibjingleStreamTransport::OnRouteChange(
243 cricket::TransportChannel
* channel
,
244 const cricket::Candidate
& candidate
) {
245 TransportRoute route
;
247 if (candidate
.type() == "local") {
248 route
.type
= TransportRoute::DIRECT
;
249 } else if (candidate
.type() == "stun") {
250 route
.type
= TransportRoute::STUN
;
251 } else if (candidate
.type() == "relay") {
252 route
.type
= TransportRoute::RELAY
;
254 LOG(FATAL
) << "Unknown candidate type: " << candidate
.type();
257 if (!jingle_glue::SocketAddressToIPEndPoint(
258 candidate
.address(), &route
.remote_address
)) {
259 LOG(FATAL
) << "Failed to convert peer IP address.";
262 DCHECK(channel_
->best_connection());
263 const cricket::Candidate
& local_candidate
=
264 channel_
->best_connection()->local_candidate();
265 if (!jingle_glue::SocketAddressToIPEndPoint(
266 local_candidate
.address(), &route
.local_address
)) {
267 LOG(FATAL
) << "Failed to convert local IP address.";
270 event_handler_
->OnTransportRouteChange(this, route
);
273 void LibjingleStreamTransport::OnWritableState(
274 cricket::TransportChannel
* channel
) {
275 DCHECK_EQ(channel
, channel_
.get());
277 event_handler_
->OnTransportReady(this, channel
->writable());
279 if (channel
->writable()) {
280 channel_was_writable_
= true;
281 connect_attempts_left_
= kMaxReconnectAttempts
;
282 reconnect_timer_
.Stop();
283 } else if (!channel
->writable() && channel_was_writable_
) {
284 reconnect_timer_
.Reset();
289 void LibjingleStreamTransport::OnTcpConnected(int result
) {
290 DCHECK(CalledOnValidThread());
292 if (result
!= net::OK
) {
293 NotifyConnectFailed();
297 authenticator_
->SecureAndAuthenticate(
298 socket_
.PassAs
<net::StreamSocket
>(),
299 base::Bind(&LibjingleStreamTransport::OnAuthenticationDone
,
300 base::Unretained(this)));
303 void LibjingleStreamTransport::OnAuthenticationDone(
305 scoped_ptr
<net::StreamSocket
> socket
) {
306 if (error
!= net::OK
) {
307 NotifyConnectFailed();
311 NotifyConnected(socket
.Pass());
314 void LibjingleStreamTransport::OnChannelDestroyed() {
315 if (is_connected()) {
316 // The connection socket is being deleted, so delete the transport too.
321 void LibjingleStreamTransport::TryReconnect() {
322 DCHECK(!channel_
->writable());
324 if (connect_attempts_left_
<= 0) {
325 reconnect_timer_
.Stop();
327 // Notify the caller that ICE connection has failed - normally that will
328 // terminate Jingle connection (i.e. the transport will be destroyed).
329 event_handler_
->OnTransportFailed(this);
332 --connect_attempts_left_
;
334 // Restart ICE by resetting ICE password.
335 ice_password_
= talk_base::CreateRandomString(cricket::ICE_PWD_LENGTH
);
336 channel_
->SetIceCredentials(ice_username_fragment_
, ice_password_
);
339 void LibjingleStreamTransport::NotifyConnected(
340 scoped_ptr
<net::StreamSocket
> socket
) {
341 DCHECK(!is_connected());
342 StreamTransport::ConnectedCallback callback
= callback_
;
344 callback
.Run(socket
.Pass());
347 void LibjingleStreamTransport::NotifyConnectFailed() {
348 DCHECK(!is_connected());
352 // This method may be called in response to a libjingle signal, so
353 // libjingle objects must be deleted asynchronously.
354 if (channel_
.get()) {
355 base::ThreadTaskRunnerHandle::Get()->DeleteSoon(
356 FROM_HERE
, channel_
.release());
359 authenticator_
.reset();
361 NotifyConnected(scoped_ptr
<net::StreamSocket
>());
366 scoped_ptr
<LibjingleTransportFactory
> LibjingleTransportFactory::Create(
367 const NetworkSettings
& network_settings
,
368 const scoped_refptr
<net::URLRequestContextGetter
>&
369 url_request_context_getter
) {
370 // Use Chrome's network stack to allocate ports for peer-to-peer channels.
371 scoped_ptr
<ChromiumPortAllocator
> port_allocator(
372 ChromiumPortAllocator::Create(url_request_context_getter
,
375 bool incoming_only
= network_settings
.nat_traversal_mode
==
376 NetworkSettings::NAT_TRAVERSAL_DISABLED
;
378 // Use libjingle for negotiation of peer-to-peer channels over
379 // NativePortAllocator allocated ports.
380 scoped_ptr
<LibjingleTransportFactory
> transport_factory(
381 new LibjingleTransportFactory(
382 port_allocator
.PassAs
<cricket::HttpPortAllocatorBase
>(),
384 return transport_factory
.Pass();
387 LibjingleTransportFactory::LibjingleTransportFactory(
388 scoped_ptr
<cricket::HttpPortAllocatorBase
> port_allocator
,
390 : http_port_allocator_(port_allocator
.get()),
391 port_allocator_(port_allocator
.Pass()),
392 incoming_only_(incoming_only
) {
393 jingle_glue::JingleThreadWrapper::EnsureForCurrentMessageLoop();
396 LibjingleTransportFactory::LibjingleTransportFactory()
397 : network_manager_(new talk_base::BasicNetworkManager()),
398 socket_factory_(new remoting::ChromiumPacketSocketFactory()),
399 http_port_allocator_(NULL
),
400 port_allocator_(new cricket::BasicPortAllocator(
401 network_manager_
.get(), socket_factory_
.get())),
402 incoming_only_(false) {
403 jingle_glue::JingleThreadWrapper::EnsureForCurrentMessageLoop();
404 port_allocator_
->set_flags(
405 cricket::PORTALLOCATOR_DISABLE_TCP
|
406 cricket::PORTALLOCATOR_DISABLE_STUN
|
407 cricket::PORTALLOCATOR_DISABLE_RELAY
|
408 cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG
|
409 cricket::PORTALLOCATOR_ENABLE_IPV6
);
412 LibjingleTransportFactory::~LibjingleTransportFactory() {
413 // This method may be called in response to a libjingle signal, so
414 // libjingle objects must be deleted asynchronously.
415 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner
=
416 base::ThreadTaskRunnerHandle::Get();
417 task_runner
->DeleteSoon(FROM_HERE
, port_allocator_
.release());
418 task_runner
->DeleteSoon(FROM_HERE
, socket_factory_
.release());
419 task_runner
->DeleteSoon(FROM_HERE
, network_manager_
.release());
422 void LibjingleTransportFactory::SetTransportConfig(
423 const TransportConfig
& config
) {
424 if (http_port_allocator_
) {
425 std::vector
<talk_base::SocketAddress
> stun_hosts
;
426 talk_base::SocketAddress stun_address
;
427 if (stun_address
.FromString(config
.stun_server
)) {
428 stun_hosts
.push_back(stun_address
);
429 http_port_allocator_
->SetStunHosts(stun_hosts
);
431 LOG(ERROR
) << "Failed to parse stun server address: "
432 << config
.stun_server
;
435 std::vector
<std::string
> relay_hosts
;
436 relay_hosts
.push_back(config
.relay_server
);
437 http_port_allocator_
->SetRelayHosts(relay_hosts
);
438 http_port_allocator_
->SetRelayToken(config
.relay_token
);
442 scoped_ptr
<StreamTransport
> LibjingleTransportFactory::CreateStreamTransport() {
443 return scoped_ptr
<StreamTransport
>(
444 new LibjingleStreamTransport(port_allocator_
.get(), incoming_only_
));
447 scoped_ptr
<DatagramTransport
>
448 LibjingleTransportFactory::CreateDatagramTransport() {
450 return scoped_ptr
<DatagramTransport
>();
453 } // namespace protocol
454 } // namespace remoting