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. 4 5 #include "remoting/protocol/libjingle_transport_factory.h" 6 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" 26 27 namespace remoting { 28 namespace protocol { 29 30 namespace { 31 32 // Value is chosen to balance the extra latency against the reduced 33 // load due to ACK traffic. 34 const int kTcpAckDelayMilliseconds = 10; 35 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; 40 41 // Try connecting ICE twice with timeout of 15 seconds for each attempt. 42 const int kMaxReconnectAttempts = 2; 43 const int kReconnectDelaySeconds = 15; 44 45 class LibjingleStreamTransport : public StreamTransport, 46 public sigslot::has_slots<> { 47 public: 48 LibjingleStreamTransport(cricket::PortAllocator* port_allocator, 49 bool incoming_only); 50 virtual ~LibjingleStreamTransport(); 51 52 // StreamTransport interface. 53 virtual void Initialize( 54 const std::string& name, 55 Transport::EventHandler* event_handler, 56 scoped_ptr<ChannelAuthenticator> authenticator) OVERRIDE; 57 virtual void Connect( 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; 62 63 private: 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); 71 72 // Callback for PseudoTcpAdapter::Connect(). 73 void OnTcpConnected(int result); 74 75 // Callback for Authenticator::SecureAndAuthenticate(); 76 void OnAuthenticationDone(net::Error error, 77 scoped_ptr<net::StreamSocket> socket); 78 79 // Callback for jingle_glue::TransportChannelSocketAdapter to notify when the 80 // socket is destroyed. 81 void OnChannelDestroyed(); 82 83 // Tries to connect by restarting ICE. Called by |reconnect_timer_|. 84 void TryReconnect(); 85 86 // Helper methods to call |callback_|. 87 void NotifyConnected(scoped_ptr<net::StreamSocket> socket); 88 void NotifyConnectFailed(); 89 90 cricket::PortAllocator* port_allocator_; 91 bool incoming_only_; 92 93 std::string name_; 94 EventHandler* event_handler_; 95 StreamTransport::ConnectedCallback callback_; 96 scoped_ptr<ChannelAuthenticator> authenticator_; 97 std::string ice_username_fragment_; 98 std::string ice_password_; 99 100 scoped_ptr<cricket::P2PTransportChannel> channel_; 101 bool channel_was_writable_; 102 int connect_attempts_left_; 103 base::RepeatingTimer<LibjingleStreamTransport> reconnect_timer_; 104 105 // We own |socket_| until it is connected. 106 scoped_ptr<jingle_glue::PseudoTcpAdapter> socket_; 107 108 DISALLOW_COPY_AND_ASSIGN(LibjingleStreamTransport); 109 }; 110 111 LibjingleStreamTransport::LibjingleStreamTransport( 112 cricket::PortAllocator* port_allocator, 113 bool incoming_only) 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) { 122 } 123 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); 129 130 if (channel_.get()) { 131 base::ThreadTaskRunnerHandle::Get()->DeleteSoon( 132 FROM_HERE, channel_.release()); 133 } 134 } 135 136 void LibjingleStreamTransport::Initialize( 137 const std::string& name, 138 Transport::EventHandler* event_handler, 139 scoped_ptr<ChannelAuthenticator> authenticator) { 140 DCHECK(CalledOnValidThread()); 141 142 DCHECK(!name.empty()); 143 DCHECK(event_handler); 144 145 // Can be initialized only once. 146 DCHECK(name_.empty()); 147 148 name_ = name; 149 event_handler_ = event_handler; 150 authenticator_ = authenticator.Pass(); 151 } 152 153 void LibjingleStreamTransport::Connect( 154 const StreamTransport::ConnectedCallback& callback) { 155 DCHECK(CalledOnValidThread()); 156 157 callback_ = callback; 158 159 DCHECK(!channel_.get()); 160 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_); 175 176 channel_->Connect(); 177 178 --connect_attempts_left_; 179 180 // Start reconnection timer. 181 reconnect_timer_.Start( 182 FROM_HERE, base::TimeDelta::FromSeconds(kReconnectDelaySeconds), 183 this, &LibjingleStreamTransport::TryReconnect); 184 185 // Create net::Socket adapter for the P2PTransportChannel. 186 scoped_ptr<jingle_glue::TransportChannelSocketAdapter> channel_adapter( 187 new jingle_glue::TransportChannelSocketAdapter(channel_.get())); 188 189 channel_adapter->SetOnDestroyedCallback(base::Bind( 190 &LibjingleStreamTransport::OnChannelDestroyed, base::Unretained(this))); 191 192 // Configure and connect PseudoTCP adapter. 193 socket_.reset( 194 new jingle_glue::PseudoTcpAdapter(channel_adapter.release())); 195 socket_->SetSendBufferSize(kTcpSendBufferSize); 196 socket_->SetReceiveBufferSize(kTcpReceiveBufferSize); 197 socket_->SetNoDelay(true); 198 socket_->SetAckDelay(kTcpAckDelayMilliseconds); 199 200 // TODO(sergeyu): This is a hack to improve latency of the video 201 // channel. Consider removing it once we have better flow control 202 // implemented. 203 if (name_ == kVideoChannelName) 204 socket_->SetWriteWaitsForSend(true); 205 206 int result = socket_->Connect( 207 base::Bind(&LibjingleStreamTransport::OnTcpConnected, 208 base::Unretained(this))); 209 if (result != net::ERR_IO_PENDING) 210 OnTcpConnected(result); 211 } 212 213 void LibjingleStreamTransport::AddRemoteCandidate( 214 const cricket::Candidate& candidate) { 215 DCHECK(CalledOnValidThread()); 216 channel_->OnCandidate(candidate); 217 } 218 219 const std::string& LibjingleStreamTransport::name() const { 220 DCHECK(CalledOnValidThread()); 221 return name_; 222 } 223 224 bool LibjingleStreamTransport::is_connected() const { 225 DCHECK(CalledOnValidThread()); 226 return callback_.is_null(); 227 } 228 229 void LibjingleStreamTransport::OnRequestSignaling( 230 cricket::TransportChannelImpl* channel) { 231 DCHECK(CalledOnValidThread()); 232 channel_->OnSignalingReady(); 233 } 234 235 void LibjingleStreamTransport::OnCandidateReady( 236 cricket::TransportChannelImpl* channel, 237 const cricket::Candidate& candidate) { 238 DCHECK(CalledOnValidThread()); 239 event_handler_->OnTransportCandidate(this, candidate); 240 } 241 242 void LibjingleStreamTransport::OnRouteChange( 243 cricket::TransportChannel* channel, 244 const cricket::Candidate& candidate) { 245 TransportRoute route; 246 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; 253 } else { 254 LOG(FATAL) << "Unknown candidate type: " << candidate.type(); 255 } 256 257 if (!jingle_glue::SocketAddressToIPEndPoint( 258 candidate.address(), &route.remote_address)) { 259 LOG(FATAL) << "Failed to convert peer IP address."; 260 } 261 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."; 268 } 269 270 event_handler_->OnTransportRouteChange(this, route); 271 } 272 273 void LibjingleStreamTransport::OnWritableState( 274 cricket::TransportChannel* channel) { 275 DCHECK_EQ(channel, channel_.get()); 276 277 event_handler_->OnTransportReady(this, channel->writable()); 278 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(); 285 TryReconnect(); 286 } 287 } 288 289 void LibjingleStreamTransport::OnTcpConnected(int result) { 290 DCHECK(CalledOnValidThread()); 291 292 if (result != net::OK) { 293 NotifyConnectFailed(); 294 return; 295 } 296 297 authenticator_->SecureAndAuthenticate( 298 socket_.PassAs<net::StreamSocket>(), 299 base::Bind(&LibjingleStreamTransport::OnAuthenticationDone, 300 base::Unretained(this))); 301 } 302 303 void LibjingleStreamTransport::OnAuthenticationDone( 304 net::Error error, 305 scoped_ptr<net::StreamSocket> socket) { 306 if (error != net::OK) { 307 NotifyConnectFailed(); 308 return; 309 } 310 311 NotifyConnected(socket.Pass()); 312 } 313 314 void LibjingleStreamTransport::OnChannelDestroyed() { 315 if (is_connected()) { 316 // The connection socket is being deleted, so delete the transport too. 317 delete this; 318 } 319 } 320 321 void LibjingleStreamTransport::TryReconnect() { 322 DCHECK(!channel_->writable()); 323 324 if (connect_attempts_left_ <= 0) { 325 reconnect_timer_.Stop(); 326 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); 330 return; 331 } 332 --connect_attempts_left_; 333 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_); 337 } 338 339 void LibjingleStreamTransport::NotifyConnected( 340 scoped_ptr<net::StreamSocket> socket) { 341 DCHECK(!is_connected()); 342 StreamTransport::ConnectedCallback callback = callback_; 343 callback_.Reset(); 344 callback.Run(socket.Pass()); 345 } 346 347 void LibjingleStreamTransport::NotifyConnectFailed() { 348 DCHECK(!is_connected()); 349 350 socket_.reset(); 351 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()); 357 } 358 359 authenticator_.reset(); 360 361 NotifyConnected(scoped_ptr<net::StreamSocket>()); 362 } 363 364 } // namespace 365 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, 373 network_settings)); 374 375 bool incoming_only = network_settings.nat_traversal_mode == 376 NetworkSettings::NAT_TRAVERSAL_DISABLED; 377 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>(), 383 incoming_only)); 384 return transport_factory.Pass(); 385 } 386 387 LibjingleTransportFactory::LibjingleTransportFactory( 388 scoped_ptr<cricket::HttpPortAllocatorBase> port_allocator, 389 bool incoming_only) 390 : http_port_allocator_(port_allocator.get()), 391 port_allocator_(port_allocator.Pass()), 392 incoming_only_(incoming_only) { 393 jingle_glue::JingleThreadWrapper::EnsureForCurrentMessageLoop(); 394 } 395 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 } 410 411 LibjingleTransportFactory::~LibjingleTransportFactory() { 412 // This method may be called in response to a libjingle signal, so 413 // libjingle objects must be deleted asynchronously. 414 scoped_refptr<base::SingleThreadTaskRunner> task_runner = 415 base::ThreadTaskRunnerHandle::Get(); 416 task_runner->DeleteSoon(FROM_HERE, port_allocator_.release()); 417 task_runner->DeleteSoon(FROM_HERE, socket_factory_.release()); 418 task_runner->DeleteSoon(FROM_HERE, network_manager_.release()); 419 } 420 421 void LibjingleTransportFactory::SetTransportConfig( 422 const TransportConfig& config) { 423 if (http_port_allocator_) { 424 std::vector<talk_base::SocketAddress> stun_hosts; 425 talk_base::SocketAddress stun_address; 426 if (stun_address.FromString(config.stun_server)) { 427 stun_hosts.push_back(stun_address); 428 http_port_allocator_->SetStunHosts(stun_hosts); 429 } else { 430 LOG(ERROR) << "Failed to parse stun server address: " 431 << config.stun_server; 432 } 433 434 std::vector<std::string> relay_hosts; 435 relay_hosts.push_back(config.relay_server); 436 http_port_allocator_->SetRelayHosts(relay_hosts); 437 http_port_allocator_->SetRelayToken(config.relay_token); 438 } 439 } 440 441 scoped_ptr<StreamTransport> LibjingleTransportFactory::CreateStreamTransport() { 442 return scoped_ptr<StreamTransport>( 443 new LibjingleStreamTransport(port_allocator_.get(), incoming_only_)); 444 } 445 446 scoped_ptr<DatagramTransport> 447 LibjingleTransportFactory::CreateDatagramTransport() { 448 NOTIMPLEMENTED(); 449 return scoped_ptr<DatagramTransport>(); 450 } 451 452 } // namespace protocol 453 } // namespace remoting 454