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/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/pseudotcp_adapter.h" 13 #include "jingle/glue/thread_wrapper.h" 14 #include "jingle/glue/utils.h" 15 #include "net/base/net_errors.h" 16 #include "remoting/base/constants.h" 17 #include "remoting/jingle_glue/jingle_info_request.h" 18 #include "remoting/jingle_glue/network_settings.h" 19 #include "remoting/protocol/channel_authenticator.h" 20 #include "third_party/libjingle/source/talk/base/network.h" 21 #include "third_party/libjingle/source/talk/p2p/base/constants.h" 22 #include "third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.h" 23 #include "third_party/libjingle/source/talk/p2p/client/basicportallocator.h" 24 #include "third_party/libjingle/source/talk/p2p/client/httpportallocator.h" 25 26 namespace remoting { 27 namespace protocol { 28 29 namespace { 30 31 // Value is chosen to balance the extra latency against the reduced 32 // load due to ACK traffic. 33 const int kTcpAckDelayMilliseconds = 10; 34 35 // Values for the TCP send and receive buffer size. This should be tuned to 36 // accommodate high latency network but not backlog the decoding pipeline. 37 const int kTcpReceiveBufferSize = 256 * 1024; 38 const int kTcpSendBufferSize = kTcpReceiveBufferSize + 30 * 1024; 39 40 // Try connecting ICE twice with timeout of 15 seconds for each attempt. 41 const int kMaxReconnectAttempts = 2; 42 const int kReconnectDelaySeconds = 15; 43 44 // Get fresh STUN/Relay configuration every hour. 45 const int kJingleInfoUpdatePeriodSeconds = 3600; 46 47 class LibjingleStreamTransport 48 : public StreamTransport, 49 public base::SupportsWeakPtr<LibjingleStreamTransport>, 50 public sigslot::has_slots<> { 51 public: 52 LibjingleStreamTransport(cricket::PortAllocator* port_allocator, 53 const NetworkSettings& network_settings); 54 virtual ~LibjingleStreamTransport(); 55 56 // Called by JingleTransportFactory when it has fresh Jingle info. 57 void OnCanStart(); 58 59 // StreamTransport interface. 60 virtual void Initialize( 61 const std::string& name, 62 Transport::EventHandler* event_handler, 63 scoped_ptr<ChannelAuthenticator> authenticator) OVERRIDE; 64 virtual void Connect( 65 const StreamTransport::ConnectedCallback& callback) OVERRIDE; 66 virtual void AddRemoteCandidate(const cricket::Candidate& candidate) OVERRIDE; 67 virtual const std::string& name() const OVERRIDE; 68 virtual bool is_connected() const OVERRIDE; 69 70 private: 71 void DoStart(); 72 73 // Signal handlers for cricket::TransportChannel. 74 void OnRequestSignaling(cricket::TransportChannelImpl* channel); 75 void OnCandidateReady(cricket::TransportChannelImpl* channel, 76 const cricket::Candidate& candidate); 77 void OnRouteChange(cricket::TransportChannel* channel, 78 const cricket::Candidate& candidate); 79 void OnWritableState(cricket::TransportChannel* channel); 80 81 // Callback for PseudoTcpAdapter::Connect(). 82 void OnTcpConnected(int result); 83 84 // Callback for Authenticator::SecureAndAuthenticate(); 85 void OnAuthenticationDone(net::Error error, 86 scoped_ptr<net::StreamSocket> socket); 87 88 // Callback for jingle_glue::TransportChannelSocketAdapter to notify when the 89 // socket is destroyed. 90 void OnChannelDestroyed(); 91 92 // Tries to connect by restarting ICE. Called by |reconnect_timer_|. 93 void TryReconnect(); 94 95 // Helper methods to call |callback_|. 96 void NotifyConnected(scoped_ptr<net::StreamSocket> socket); 97 void NotifyConnectFailed(); 98 99 cricket::PortAllocator* port_allocator_; 100 NetworkSettings network_settings_; 101 102 std::string name_; 103 EventHandler* event_handler_; 104 StreamTransport::ConnectedCallback callback_; 105 scoped_ptr<ChannelAuthenticator> authenticator_; 106 std::string ice_username_fragment_; 107 std::string ice_password_; 108 109 bool can_start_; 110 111 std::list<cricket::Candidate> pending_candidates_; 112 scoped_ptr<cricket::P2PTransportChannel> channel_; 113 bool channel_was_writable_; 114 int connect_attempts_left_; 115 base::RepeatingTimer<LibjingleStreamTransport> reconnect_timer_; 116 117 // We own |socket_| until it is connected. 118 scoped_ptr<jingle_glue::PseudoTcpAdapter> socket_; 119 120 DISALLOW_COPY_AND_ASSIGN(LibjingleStreamTransport); 121 }; 122 123 LibjingleStreamTransport::LibjingleStreamTransport( 124 cricket::PortAllocator* port_allocator, 125 const NetworkSettings& network_settings) 126 : port_allocator_(port_allocator), 127 network_settings_(network_settings), 128 event_handler_(NULL), 129 ice_username_fragment_( 130 talk_base::CreateRandomString(cricket::ICE_UFRAG_LENGTH)), 131 ice_password_(talk_base::CreateRandomString(cricket::ICE_PWD_LENGTH)), 132 can_start_(false), 133 channel_was_writable_(false), 134 connect_attempts_left_(kMaxReconnectAttempts) { 135 } 136 137 LibjingleStreamTransport::~LibjingleStreamTransport() { 138 DCHECK(event_handler_); 139 event_handler_->OnTransportDeleted(this); 140 // Channel should be already destroyed if we were connected. 141 DCHECK(!is_connected() || socket_.get() == NULL); 142 143 if (channel_.get()) { 144 base::ThreadTaskRunnerHandle::Get()->DeleteSoon( 145 FROM_HERE, channel_.release()); 146 } 147 } 148 149 void LibjingleStreamTransport::OnCanStart() { 150 DCHECK(CalledOnValidThread()); 151 152 DCHECK(!can_start_); 153 can_start_ = true; 154 155 // If Connect() has been called then start connection. 156 if (!callback_.is_null()) 157 DoStart(); 158 159 while (!pending_candidates_.empty()) { 160 channel_->OnCandidate(pending_candidates_.front()); 161 pending_candidates_.pop_front(); 162 } 163 } 164 165 void LibjingleStreamTransport::Initialize( 166 const std::string& name, 167 Transport::EventHandler* event_handler, 168 scoped_ptr<ChannelAuthenticator> authenticator) { 169 DCHECK(CalledOnValidThread()); 170 171 DCHECK(!name.empty()); 172 DCHECK(event_handler); 173 174 // Can be initialized only once. 175 DCHECK(name_.empty()); 176 177 name_ = name; 178 event_handler_ = event_handler; 179 authenticator_ = authenticator.Pass(); 180 } 181 182 void LibjingleStreamTransport::Connect( 183 const StreamTransport::ConnectedCallback& callback) { 184 DCHECK(CalledOnValidThread()); 185 callback_ = callback; 186 187 if (can_start_) 188 DoStart(); 189 } 190 191 void LibjingleStreamTransport::DoStart() { 192 DCHECK(!channel_.get()); 193 194 // Create P2PTransportChannel, attach signal handlers and connect it. 195 // TODO(sergeyu): Specify correct component ID for the channel. 196 channel_.reset(new cricket::P2PTransportChannel( 197 std::string(), 0, NULL, port_allocator_)); 198 channel_->SetIceCredentials(ice_username_fragment_, ice_password_); 199 channel_->SignalRequestSignaling.connect( 200 this, &LibjingleStreamTransport::OnRequestSignaling); 201 channel_->SignalCandidateReady.connect( 202 this, &LibjingleStreamTransport::OnCandidateReady); 203 channel_->SignalRouteChange.connect( 204 this, &LibjingleStreamTransport::OnRouteChange); 205 channel_->SignalWritableState.connect( 206 this, &LibjingleStreamTransport::OnWritableState); 207 if (network_settings_.nat_traversal_mode == 208 NetworkSettings::NAT_TRAVERSAL_DISABLED) { 209 channel_->set_incoming_only(true); 210 } 211 212 channel_->Connect(); 213 214 --connect_attempts_left_; 215 216 // Start reconnection timer. 217 reconnect_timer_.Start( 218 FROM_HERE, base::TimeDelta::FromSeconds(kReconnectDelaySeconds), 219 this, &LibjingleStreamTransport::TryReconnect); 220 221 // Create net::Socket adapter for the P2PTransportChannel. 222 scoped_ptr<jingle_glue::TransportChannelSocketAdapter> channel_adapter( 223 new jingle_glue::TransportChannelSocketAdapter(channel_.get())); 224 225 channel_adapter->SetOnDestroyedCallback(base::Bind( 226 &LibjingleStreamTransport::OnChannelDestroyed, base::Unretained(this))); 227 228 // Configure and connect PseudoTCP adapter. 229 socket_.reset( 230 new jingle_glue::PseudoTcpAdapter(channel_adapter.release())); 231 socket_->SetSendBufferSize(kTcpSendBufferSize); 232 socket_->SetReceiveBufferSize(kTcpReceiveBufferSize); 233 socket_->SetNoDelay(true); 234 socket_->SetAckDelay(kTcpAckDelayMilliseconds); 235 236 // TODO(sergeyu): This is a hack to improve latency of the video 237 // channel. Consider removing it once we have better flow control 238 // implemented. 239 if (name_ == kVideoChannelName) 240 socket_->SetWriteWaitsForSend(true); 241 242 int result = socket_->Connect( 243 base::Bind(&LibjingleStreamTransport::OnTcpConnected, 244 base::Unretained(this))); 245 if (result != net::ERR_IO_PENDING) 246 OnTcpConnected(result); 247 } 248 249 void LibjingleStreamTransport::AddRemoteCandidate( 250 const cricket::Candidate& candidate) { 251 DCHECK(CalledOnValidThread()); 252 if (channel_) { 253 channel_->OnCandidate(candidate); 254 } else { 255 pending_candidates_.push_back(candidate); 256 } 257 } 258 259 const std::string& LibjingleStreamTransport::name() const { 260 DCHECK(CalledOnValidThread()); 261 return name_; 262 } 263 264 bool LibjingleStreamTransport::is_connected() const { 265 DCHECK(CalledOnValidThread()); 266 return callback_.is_null(); 267 } 268 269 void LibjingleStreamTransport::OnRequestSignaling( 270 cricket::TransportChannelImpl* channel) { 271 DCHECK(CalledOnValidThread()); 272 channel_->OnSignalingReady(); 273 } 274 275 void LibjingleStreamTransport::OnCandidateReady( 276 cricket::TransportChannelImpl* channel, 277 const cricket::Candidate& candidate) { 278 DCHECK(CalledOnValidThread()); 279 event_handler_->OnTransportCandidate(this, candidate); 280 } 281 282 void LibjingleStreamTransport::OnRouteChange( 283 cricket::TransportChannel* channel, 284 const cricket::Candidate& candidate) { 285 TransportRoute route; 286 287 if (candidate.type() == "local") { 288 route.type = TransportRoute::DIRECT; 289 } else if (candidate.type() == "stun") { 290 route.type = TransportRoute::STUN; 291 } else if (candidate.type() == "relay") { 292 route.type = TransportRoute::RELAY; 293 } else { 294 LOG(FATAL) << "Unknown candidate type: " << candidate.type(); 295 } 296 297 if (!jingle_glue::SocketAddressToIPEndPoint( 298 candidate.address(), &route.remote_address)) { 299 LOG(FATAL) << "Failed to convert peer IP address."; 300 } 301 302 DCHECK(channel_->best_connection()); 303 const cricket::Candidate& local_candidate = 304 channel_->best_connection()->local_candidate(); 305 if (!jingle_glue::SocketAddressToIPEndPoint( 306 local_candidate.address(), &route.local_address)) { 307 LOG(FATAL) << "Failed to convert local IP address."; 308 } 309 310 event_handler_->OnTransportRouteChange(this, route); 311 } 312 313 void LibjingleStreamTransport::OnWritableState( 314 cricket::TransportChannel* channel) { 315 DCHECK_EQ(channel, channel_.get()); 316 317 event_handler_->OnTransportReady(this, channel->writable()); 318 319 if (channel->writable()) { 320 channel_was_writable_ = true; 321 connect_attempts_left_ = kMaxReconnectAttempts; 322 reconnect_timer_.Stop(); 323 } else if (!channel->writable() && channel_was_writable_) { 324 reconnect_timer_.Reset(); 325 TryReconnect(); 326 } 327 } 328 329 void LibjingleStreamTransport::OnTcpConnected(int result) { 330 DCHECK(CalledOnValidThread()); 331 332 if (result != net::OK) { 333 NotifyConnectFailed(); 334 return; 335 } 336 337 authenticator_->SecureAndAuthenticate( 338 socket_.PassAs<net::StreamSocket>(), 339 base::Bind(&LibjingleStreamTransport::OnAuthenticationDone, 340 base::Unretained(this))); 341 } 342 343 void LibjingleStreamTransport::OnAuthenticationDone( 344 net::Error error, 345 scoped_ptr<net::StreamSocket> socket) { 346 if (error != net::OK) { 347 NotifyConnectFailed(); 348 return; 349 } 350 351 NotifyConnected(socket.Pass()); 352 } 353 354 void LibjingleStreamTransport::OnChannelDestroyed() { 355 if (is_connected()) { 356 // The connection socket is being deleted, so delete the transport too. 357 delete this; 358 } 359 } 360 361 void LibjingleStreamTransport::TryReconnect() { 362 DCHECK(!channel_->writable()); 363 364 if (connect_attempts_left_ <= 0) { 365 reconnect_timer_.Stop(); 366 367 // Notify the caller that ICE connection has failed - normally that will 368 // terminate Jingle connection (i.e. the transport will be destroyed). 369 event_handler_->OnTransportFailed(this); 370 return; 371 } 372 --connect_attempts_left_; 373 374 // Restart ICE by resetting ICE password. 375 ice_password_ = talk_base::CreateRandomString(cricket::ICE_PWD_LENGTH); 376 channel_->SetIceCredentials(ice_username_fragment_, ice_password_); 377 } 378 379 void LibjingleStreamTransport::NotifyConnected( 380 scoped_ptr<net::StreamSocket> socket) { 381 DCHECK(!is_connected()); 382 StreamTransport::ConnectedCallback callback = callback_; 383 callback_.Reset(); 384 callback.Run(socket.Pass()); 385 } 386 387 void LibjingleStreamTransport::NotifyConnectFailed() { 388 DCHECK(!is_connected()); 389 390 socket_.reset(); 391 392 // This method may be called in response to a libjingle signal, so 393 // libjingle objects must be deleted asynchronously. 394 if (channel_.get()) { 395 base::ThreadTaskRunnerHandle::Get()->DeleteSoon( 396 FROM_HERE, channel_.release()); 397 } 398 399 authenticator_.reset(); 400 401 NotifyConnected(scoped_ptr<net::StreamSocket>()); 402 } 403 404 } // namespace 405 406 LibjingleTransportFactory::LibjingleTransportFactory( 407 SignalStrategy* signal_strategy, 408 scoped_ptr<cricket::HttpPortAllocatorBase> port_allocator, 409 const NetworkSettings& network_settings) 410 : signal_strategy_(signal_strategy), 411 port_allocator_(port_allocator.Pass()), 412 network_settings_(network_settings) { 413 jingle_glue::JingleThreadWrapper::EnsureForCurrentMessageLoop(); 414 } 415 416 LibjingleTransportFactory::~LibjingleTransportFactory() { 417 // This method may be called in response to a libjingle signal, so 418 // libjingle objects must be deleted asynchronously. 419 scoped_refptr<base::SingleThreadTaskRunner> task_runner = 420 base::ThreadTaskRunnerHandle::Get(); 421 task_runner->DeleteSoon(FROM_HERE, port_allocator_.release()); 422 } 423 424 void LibjingleTransportFactory::PrepareTokens() { 425 EnsureFreshJingleInfo(); 426 } 427 428 scoped_ptr<StreamTransport> LibjingleTransportFactory::CreateStreamTransport() { 429 scoped_ptr<LibjingleStreamTransport> result( 430 new LibjingleStreamTransport(port_allocator_.get(), network_settings_)); 431 432 EnsureFreshJingleInfo(); 433 434 // If there is a pending |jingle_info_request_| delay starting the new 435 // transport until the request is finished. 436 if (jingle_info_request_) { 437 on_jingle_info_callbacks_.push_back( 438 base::Bind(&LibjingleStreamTransport::OnCanStart, 439 result->AsWeakPtr())); 440 } else { 441 result->OnCanStart(); 442 } 443 444 return result.PassAs<StreamTransport>(); 445 } 446 447 scoped_ptr<DatagramTransport> 448 LibjingleTransportFactory::CreateDatagramTransport() { 449 NOTIMPLEMENTED(); 450 return scoped_ptr<DatagramTransport>(); 451 } 452 453 void LibjingleTransportFactory::EnsureFreshJingleInfo() { 454 if (network_settings_.nat_traversal_mode != 455 NetworkSettings::NAT_TRAVERSAL_ENABLED || 456 jingle_info_request_) { 457 return; 458 } 459 460 if (base::TimeTicks::Now() - last_jingle_info_update_time_ > 461 base::TimeDelta::FromSeconds(kJingleInfoUpdatePeriodSeconds)) { 462 jingle_info_request_.reset(new JingleInfoRequest(signal_strategy_)); 463 jingle_info_request_->Send(base::Bind( 464 &LibjingleTransportFactory::OnJingleInfo, base::Unretained(this))); 465 } 466 } 467 468 void LibjingleTransportFactory::OnJingleInfo( 469 const std::string& relay_token, 470 const std::vector<std::string>& relay_hosts, 471 const std::vector<talk_base::SocketAddress>& stun_hosts) { 472 if (!relay_token.empty() && !relay_hosts.empty()) { 473 port_allocator_->SetRelayHosts(relay_hosts); 474 port_allocator_->SetRelayToken(relay_token); 475 } 476 if (!stun_hosts.empty()) { 477 port_allocator_->SetStunHosts(stun_hosts); 478 } 479 480 jingle_info_request_.reset(); 481 if ((!relay_token.empty() && !relay_hosts.empty()) || !stun_hosts.empty()) 482 last_jingle_info_update_time_ = base::TimeTicks::Now(); 483 484 while (!on_jingle_info_callbacks_.empty()) { 485 on_jingle_info_callbacks_.begin()->Run(); 486 on_jingle_info_callbacks_.pop_front(); 487 } 488 } 489 490 } // namespace protocol 491 } // namespace remoting 492