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