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/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/libjingle/source/talk/p2p/base/constants.h" 17#include "third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.h" 18#include "third_party/libjingle/source/talk/p2p/base/port.h" 19#include "third_party/libjingle/source/talk/p2p/client/basicportallocator.h" 20#include "third_party/libjingle/source/talk/p2p/client/httpportallocator.h" 21#include "third_party/webrtc/base/network.h" 22 23namespace remoting { 24namespace protocol { 25 26namespace { 27 28// Try connecting ICE twice with timeout of 15 seconds for each attempt. 29const int kMaxReconnectAttempts = 2; 30const int kReconnectDelaySeconds = 15; 31 32// Get fresh STUN/Relay configuration every hour. 33const int kJingleInfoUpdatePeriodSeconds = 3600; 34 35class LibjingleTransport 36 : public Transport, 37 public base::SupportsWeakPtr<LibjingleTransport>, 38 public sigslot::has_slots<> { 39 public: 40 LibjingleTransport(cricket::PortAllocator* port_allocator, 41 const NetworkSettings& network_settings); 42 virtual ~LibjingleTransport(); 43 44 // Called by JingleTransportFactory when it has fresh Jingle info. 45 void OnCanStart(); 46 47 // Transport interface. 48 virtual void Connect( 49 const std::string& name, 50 Transport::EventHandler* event_handler, 51 const Transport::ConnectedCallback& callback) OVERRIDE; 52 virtual void AddRemoteCandidate(const cricket::Candidate& candidate) OVERRIDE; 53 virtual const std::string& name() const OVERRIDE; 54 virtual bool is_connected() const OVERRIDE; 55 56 private: 57 void DoStart(); 58 void NotifyConnected(); 59 60 // Signal handlers for cricket::TransportChannel. 61 void OnRequestSignaling(cricket::TransportChannelImpl* channel); 62 void OnCandidateReady(cricket::TransportChannelImpl* channel, 63 const cricket::Candidate& candidate); 64 void OnRouteChange(cricket::TransportChannel* channel, 65 const cricket::Candidate& candidate); 66 void OnWritableState(cricket::TransportChannel* channel); 67 68 // Callback for jingle_glue::TransportChannelSocketAdapter to notify when the 69 // socket is destroyed. 70 void OnChannelDestroyed(); 71 72 // Tries to connect by restarting ICE. Called by |reconnect_timer_|. 73 void TryReconnect(); 74 75 cricket::PortAllocator* port_allocator_; 76 NetworkSettings network_settings_; 77 78 std::string name_; 79 EventHandler* event_handler_; 80 Transport::ConnectedCallback callback_; 81 std::string ice_username_fragment_; 82 std::string ice_password_; 83 84 bool can_start_; 85 86 std::list<cricket::Candidate> pending_candidates_; 87 scoped_ptr<cricket::P2PTransportChannel> channel_; 88 bool channel_was_writable_; 89 int connect_attempts_left_; 90 base::RepeatingTimer<LibjingleTransport> reconnect_timer_; 91 92 base::WeakPtrFactory<LibjingleTransport> weak_factory_; 93 94 DISALLOW_COPY_AND_ASSIGN(LibjingleTransport); 95}; 96 97LibjingleTransport::LibjingleTransport(cricket::PortAllocator* port_allocator, 98 const NetworkSettings& network_settings) 99 : port_allocator_(port_allocator), 100 network_settings_(network_settings), 101 event_handler_(NULL), 102 ice_username_fragment_( 103 rtc::CreateRandomString(cricket::ICE_UFRAG_LENGTH)), 104 ice_password_(rtc::CreateRandomString(cricket::ICE_PWD_LENGTH)), 105 can_start_(false), 106 channel_was_writable_(false), 107 connect_attempts_left_(kMaxReconnectAttempts), 108 weak_factory_(this) { 109 DCHECK(!ice_username_fragment_.empty()); 110 DCHECK(!ice_password_.empty()); 111} 112 113LibjingleTransport::~LibjingleTransport() { 114 DCHECK(event_handler_); 115 116 event_handler_->OnTransportDeleted(this); 117 118 if (channel_.get()) { 119 base::ThreadTaskRunnerHandle::Get()->DeleteSoon( 120 FROM_HERE, channel_.release()); 121 } 122} 123 124void LibjingleTransport::OnCanStart() { 125 DCHECK(CalledOnValidThread()); 126 127 DCHECK(!can_start_); 128 can_start_ = true; 129 130 // If Connect() has been called then start connection. 131 if (!callback_.is_null()) 132 DoStart(); 133 134 while (!pending_candidates_.empty()) { 135 channel_->OnCandidate(pending_candidates_.front()); 136 pending_candidates_.pop_front(); 137 } 138} 139 140void LibjingleTransport::Connect( 141 const std::string& name, 142 Transport::EventHandler* event_handler, 143 const Transport::ConnectedCallback& callback) { 144 DCHECK(CalledOnValidThread()); 145 DCHECK(!name.empty()); 146 DCHECK(event_handler); 147 DCHECK(!callback.is_null()); 148 149 DCHECK(name_.empty()); 150 name_ = name; 151 event_handler_ = event_handler; 152 callback_ = callback; 153 154 if (can_start_) 155 DoStart(); 156} 157 158void LibjingleTransport::DoStart() { 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_->SetIceProtocolType(cricket::ICEPROTO_GOOGLE); 166 channel_->SetIceCredentials(ice_username_fragment_, ice_password_); 167 channel_->SignalRequestSignaling.connect( 168 this, &LibjingleTransport::OnRequestSignaling); 169 channel_->SignalCandidateReady.connect( 170 this, &LibjingleTransport::OnCandidateReady); 171 channel_->SignalRouteChange.connect( 172 this, &LibjingleTransport::OnRouteChange); 173 channel_->SignalWritableState.connect( 174 this, &LibjingleTransport::OnWritableState); 175 channel_->set_incoming_only( 176 !(network_settings_.flags & NetworkSettings::NAT_TRAVERSAL_OUTGOING)); 177 178 channel_->Connect(); 179 180 --connect_attempts_left_; 181 182 // Start reconnection timer. 183 reconnect_timer_.Start( 184 FROM_HERE, base::TimeDelta::FromSeconds(kReconnectDelaySeconds), 185 this, &LibjingleTransport::TryReconnect); 186} 187 188void LibjingleTransport::NotifyConnected() { 189 // Create net::Socket adapter for the P2PTransportChannel. 190 scoped_ptr<jingle_glue::TransportChannelSocketAdapter> socket( 191 new jingle_glue::TransportChannelSocketAdapter(channel_.get())); 192 socket->SetOnDestroyedCallback(base::Bind( 193 &LibjingleTransport::OnChannelDestroyed, base::Unretained(this))); 194 195 Transport::ConnectedCallback callback = callback_; 196 callback_.Reset(); 197 callback.Run(socket.PassAs<net::Socket>()); 198} 199 200void LibjingleTransport::AddRemoteCandidate( 201 const cricket::Candidate& candidate) { 202 DCHECK(CalledOnValidThread()); 203 204 // To enforce the no-relay setting, it's not enough to not produce relay 205 // candidates. It's also necessary to discard remote relay candidates. 206 bool relay_allowed = (network_settings_.flags & 207 NetworkSettings::NAT_TRAVERSAL_RELAY) != 0; 208 if (!relay_allowed && candidate.type() == cricket::RELAY_PORT_TYPE) 209 return; 210 211 if (channel_) { 212 channel_->OnCandidate(candidate); 213 } else { 214 pending_candidates_.push_back(candidate); 215 } 216} 217 218const std::string& LibjingleTransport::name() const { 219 DCHECK(CalledOnValidThread()); 220 return name_; 221} 222 223bool LibjingleTransport::is_connected() const { 224 DCHECK(CalledOnValidThread()); 225 return callback_.is_null(); 226} 227 228void LibjingleTransport::OnRequestSignaling( 229 cricket::TransportChannelImpl* channel) { 230 DCHECK(CalledOnValidThread()); 231 channel_->OnSignalingReady(); 232} 233 234void LibjingleTransport::OnCandidateReady( 235 cricket::TransportChannelImpl* channel, 236 const cricket::Candidate& candidate) { 237 DCHECK(CalledOnValidThread()); 238 event_handler_->OnTransportCandidate(this, candidate); 239} 240 241void LibjingleTransport::OnRouteChange( 242 cricket::TransportChannel* channel, 243 const cricket::Candidate& candidate) { 244 TransportRoute route; 245 246 if (candidate.type() == "local") { 247 route.type = TransportRoute::DIRECT; 248 } else if (candidate.type() == "stun") { 249 route.type = TransportRoute::STUN; 250 } else if (candidate.type() == "relay") { 251 route.type = TransportRoute::RELAY; 252 } else { 253 LOG(FATAL) << "Unknown candidate type: " << candidate.type(); 254 } 255 256 if (!jingle_glue::SocketAddressToIPEndPoint( 257 candidate.address(), &route.remote_address)) { 258 LOG(FATAL) << "Failed to convert peer IP address."; 259 } 260 261 DCHECK(channel_->best_connection()); 262 const cricket::Candidate& local_candidate = 263 channel_->best_connection()->local_candidate(); 264 if (!jingle_glue::SocketAddressToIPEndPoint( 265 local_candidate.address(), &route.local_address)) { 266 LOG(FATAL) << "Failed to convert local IP address."; 267 } 268 269 event_handler_->OnTransportRouteChange(this, route); 270} 271 272void LibjingleTransport::OnWritableState( 273 cricket::TransportChannel* channel) { 274 DCHECK_EQ(channel, channel_.get()); 275 276 if (channel->writable()) { 277 if (!channel_was_writable_) { 278 channel_was_writable_ = true; 279 base::ThreadTaskRunnerHandle::Get()->PostTask( 280 FROM_HERE, 281 base::Bind(&LibjingleTransport::NotifyConnected, 282 weak_factory_.GetWeakPtr())); 283 } 284 connect_attempts_left_ = kMaxReconnectAttempts; 285 reconnect_timer_.Stop(); 286 } else if (!channel->writable() && channel_was_writable_) { 287 reconnect_timer_.Reset(); 288 TryReconnect(); 289 } 290} 291 292void LibjingleTransport::OnChannelDestroyed() { 293 if (is_connected()) { 294 // The connection socket is being deleted, so delete the transport too. 295 delete this; 296 } 297} 298 299void LibjingleTransport::TryReconnect() { 300 DCHECK(!channel_->writable()); 301 302 if (connect_attempts_left_ <= 0) { 303 reconnect_timer_.Stop(); 304 305 // Notify the caller that ICE connection has failed - normally that will 306 // terminate Jingle connection (i.e. the transport will be destroyed). 307 event_handler_->OnTransportFailed(this); 308 return; 309 } 310 --connect_attempts_left_; 311 312 // Restart ICE by resetting ICE password. 313 ice_password_ = rtc::CreateRandomString(cricket::ICE_PWD_LENGTH); 314 channel_->SetIceCredentials(ice_username_fragment_, ice_password_); 315} 316 317} // namespace 318 319LibjingleTransportFactory::LibjingleTransportFactory( 320 SignalStrategy* signal_strategy, 321 scoped_ptr<cricket::HttpPortAllocatorBase> port_allocator, 322 const NetworkSettings& network_settings) 323 : signal_strategy_(signal_strategy), 324 port_allocator_(port_allocator.Pass()), 325 network_settings_(network_settings) { 326} 327 328LibjingleTransportFactory::~LibjingleTransportFactory() { 329 // This method may be called in response to a libjingle signal, so 330 // libjingle objects must be deleted asynchronously. 331 scoped_refptr<base::SingleThreadTaskRunner> task_runner = 332 base::ThreadTaskRunnerHandle::Get(); 333 task_runner->DeleteSoon(FROM_HERE, port_allocator_.release()); 334} 335 336void LibjingleTransportFactory::PrepareTokens() { 337 EnsureFreshJingleInfo(); 338} 339 340scoped_ptr<Transport> LibjingleTransportFactory::CreateTransport() { 341 scoped_ptr<LibjingleTransport> result( 342 new LibjingleTransport(port_allocator_.get(), network_settings_)); 343 344 EnsureFreshJingleInfo(); 345 346 // If there is a pending |jingle_info_request_| delay starting the new 347 // transport until the request is finished. 348 if (jingle_info_request_) { 349 on_jingle_info_callbacks_.push_back( 350 base::Bind(&LibjingleTransport::OnCanStart, 351 result->AsWeakPtr())); 352 } else { 353 result->OnCanStart(); 354 } 355 356 return result.PassAs<Transport>(); 357} 358 359void LibjingleTransportFactory::EnsureFreshJingleInfo() { 360 uint32 stun_or_relay_flags = NetworkSettings::NAT_TRAVERSAL_STUN | 361 NetworkSettings::NAT_TRAVERSAL_RELAY; 362 if (!(network_settings_.flags & stun_or_relay_flags) || 363 jingle_info_request_) { 364 return; 365 } 366 367 if (base::TimeTicks::Now() - last_jingle_info_update_time_ > 368 base::TimeDelta::FromSeconds(kJingleInfoUpdatePeriodSeconds)) { 369 jingle_info_request_.reset(new JingleInfoRequest(signal_strategy_)); 370 jingle_info_request_->Send(base::Bind( 371 &LibjingleTransportFactory::OnJingleInfo, base::Unretained(this))); 372 } 373} 374 375void LibjingleTransportFactory::OnJingleInfo( 376 const std::string& relay_token, 377 const std::vector<std::string>& relay_hosts, 378 const std::vector<rtc::SocketAddress>& stun_hosts) { 379 if (!relay_token.empty() && !relay_hosts.empty()) { 380 port_allocator_->SetRelayHosts(relay_hosts); 381 port_allocator_->SetRelayToken(relay_token); 382 } 383 if (!stun_hosts.empty()) { 384 port_allocator_->SetStunHosts(stun_hosts); 385 } 386 387 jingle_info_request_.reset(); 388 if ((!relay_token.empty() && !relay_hosts.empty()) || !stun_hosts.empty()) 389 last_jingle_info_update_time_ = base::TimeTicks::Now(); 390 391 while (!on_jingle_info_callbacks_.empty()) { 392 on_jingle_info_callbacks_.begin()->Run(); 393 on_jingle_info_callbacks_.pop_front(); 394 } 395} 396 397} // namespace protocol 398} // namespace remoting 399