| OLD | NEW | 
 | (Empty) | 
|    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 <algorithm> |  | 
|    8  |  | 
|    9 #include "base/callback.h" |  | 
|   10 #include "base/callback_helpers.h" |  | 
|   11 #include "base/single_thread_task_runner.h" |  | 
|   12 #include "base/thread_task_runner_handle.h" |  | 
|   13 #include "base/timer/timer.h" |  | 
|   14 #include "jingle/glue/utils.h" |  | 
|   15 #include "net/base/net_errors.h" |  | 
|   16 #include "remoting/protocol/channel_socket_adapter.h" |  | 
|   17 #include "remoting/protocol/network_settings.h" |  | 
|   18 #include "remoting/signaling/jingle_info_request.h" |  | 
|   19 #include "third_party/webrtc/base/network.h" |  | 
|   20 #include "third_party/webrtc/p2p/base/constants.h" |  | 
|   21 #include "third_party/webrtc/p2p/base/p2ptransportchannel.h" |  | 
|   22 #include "third_party/webrtc/p2p/base/port.h" |  | 
|   23 #include "third_party/webrtc/p2p/client/httpportallocator.h" |  | 
|   24  |  | 
|   25 namespace remoting { |  | 
|   26 namespace protocol { |  | 
|   27  |  | 
|   28 namespace { |  | 
|   29  |  | 
|   30 // Try connecting ICE twice with timeout of 15 seconds for each attempt. |  | 
|   31 const int kMaxReconnectAttempts = 2; |  | 
|   32 const int kReconnectDelaySeconds = 15; |  | 
|   33  |  | 
|   34 // Get fresh STUN/Relay configuration every hour. |  | 
|   35 const int kJingleInfoUpdatePeriodSeconds = 3600; |  | 
|   36  |  | 
|   37 // Utility function to map a cricket::Candidate string type to a |  | 
|   38 // TransportRoute::RouteType enum value. |  | 
|   39 TransportRoute::RouteType CandidateTypeToTransportRouteType( |  | 
|   40     const std::string& candidate_type) { |  | 
|   41   if (candidate_type == "local") { |  | 
|   42     return TransportRoute::DIRECT; |  | 
|   43   } else if (candidate_type == "stun" || candidate_type == "prflx") { |  | 
|   44     return TransportRoute::STUN; |  | 
|   45   } else if (candidate_type == "relay") { |  | 
|   46     return TransportRoute::RELAY; |  | 
|   47   } else { |  | 
|   48     LOG(FATAL) << "Unknown candidate type: " << candidate_type; |  | 
|   49     return TransportRoute::DIRECT; |  | 
|   50   } |  | 
|   51 } |  | 
|   52  |  | 
|   53 class LibjingleTransport |  | 
|   54     : public Transport, |  | 
|   55       public base::SupportsWeakPtr<LibjingleTransport>, |  | 
|   56       public sigslot::has_slots<> { |  | 
|   57  public: |  | 
|   58   LibjingleTransport(cricket::PortAllocator* port_allocator, |  | 
|   59                      const NetworkSettings& network_settings, |  | 
|   60                      TransportRole role); |  | 
|   61   ~LibjingleTransport() override; |  | 
|   62  |  | 
|   63   // Called by JingleTransportFactory when it has fresh Jingle info. |  | 
|   64   void OnCanStart(); |  | 
|   65  |  | 
|   66   // Transport interface. |  | 
|   67   void Connect(const std::string& name, |  | 
|   68                Transport::EventHandler* event_handler, |  | 
|   69                const Transport::ConnectedCallback& callback) override; |  | 
|   70   void SetRemoteCredentials(const std::string& ufrag, |  | 
|   71                             const std::string& password) override; |  | 
|   72   void AddRemoteCandidate(const cricket::Candidate& candidate) override; |  | 
|   73   const std::string& name() const override; |  | 
|   74   bool is_connected() const override; |  | 
|   75  |  | 
|   76  private: |  | 
|   77   void DoStart(); |  | 
|   78   void NotifyConnected(); |  | 
|   79  |  | 
|   80   // Signal handlers for cricket::TransportChannel. |  | 
|   81   void OnCandidateGathered(cricket::TransportChannelImpl* channel, |  | 
|   82                            const cricket::Candidate& candidate); |  | 
|   83   void OnRouteChange(cricket::TransportChannel* channel, |  | 
|   84                      const cricket::Candidate& candidate); |  | 
|   85   void OnReceivingState(cricket::TransportChannel* channel); |  | 
|   86   void OnWritableState(cricket::TransportChannel* channel); |  | 
|   87  |  | 
|   88   // Callback for TransportChannelSocketAdapter to notify when the socket is |  | 
|   89   // destroyed. |  | 
|   90   void OnChannelDestroyed(); |  | 
|   91  |  | 
|   92   void NotifyRouteChanged(); |  | 
|   93  |  | 
|   94   // Tries to connect by restarting ICE. Called by |reconnect_timer_|. |  | 
|   95   void TryReconnect(); |  | 
|   96  |  | 
|   97   cricket::PortAllocator* port_allocator_; |  | 
|   98   NetworkSettings network_settings_; |  | 
|   99   TransportRole role_; |  | 
|  100  |  | 
|  101   std::string name_; |  | 
|  102   EventHandler* event_handler_; |  | 
|  103   Transport::ConnectedCallback callback_; |  | 
|  104   std::string ice_username_fragment_; |  | 
|  105  |  | 
|  106   bool can_start_; |  | 
|  107  |  | 
|  108   std::string remote_ice_username_fragment_; |  | 
|  109   std::string remote_ice_password_; |  | 
|  110   std::list<cricket::Candidate> pending_candidates_; |  | 
|  111   scoped_ptr<cricket::P2PTransportChannel> channel_; |  | 
|  112   int connect_attempts_left_; |  | 
|  113   base::RepeatingTimer reconnect_timer_; |  | 
|  114  |  | 
|  115   base::WeakPtrFactory<LibjingleTransport> weak_factory_; |  | 
|  116  |  | 
|  117   DISALLOW_COPY_AND_ASSIGN(LibjingleTransport); |  | 
|  118 }; |  | 
|  119  |  | 
|  120 LibjingleTransport::LibjingleTransport(cricket::PortAllocator* port_allocator, |  | 
|  121                                        const NetworkSettings& network_settings, |  | 
|  122                                        TransportRole role) |  | 
|  123     : port_allocator_(port_allocator), |  | 
|  124       network_settings_(network_settings), |  | 
|  125       role_(role), |  | 
|  126       event_handler_(nullptr), |  | 
|  127       ice_username_fragment_( |  | 
|  128           rtc::CreateRandomString(cricket::ICE_UFRAG_LENGTH)), |  | 
|  129       can_start_(false), |  | 
|  130       connect_attempts_left_(kMaxReconnectAttempts), |  | 
|  131       weak_factory_(this) { |  | 
|  132   DCHECK(!ice_username_fragment_.empty()); |  | 
|  133 } |  | 
|  134  |  | 
|  135 LibjingleTransport::~LibjingleTransport() { |  | 
|  136   DCHECK(event_handler_); |  | 
|  137  |  | 
|  138   event_handler_->OnTransportDeleted(this); |  | 
|  139  |  | 
|  140   if (channel_.get()) { |  | 
|  141     base::ThreadTaskRunnerHandle::Get()->DeleteSoon( |  | 
|  142         FROM_HERE, channel_.release()); |  | 
|  143   } |  | 
|  144 } |  | 
|  145  |  | 
|  146 void LibjingleTransport::OnCanStart() { |  | 
|  147   DCHECK(CalledOnValidThread()); |  | 
|  148  |  | 
|  149   DCHECK(!can_start_); |  | 
|  150   can_start_ = true; |  | 
|  151  |  | 
|  152   // If Connect() has been called then start connection. |  | 
|  153   if (!callback_.is_null()) |  | 
|  154     DoStart(); |  | 
|  155  |  | 
|  156   // Pass pending ICE credentials and candidates to the channel. |  | 
|  157   if (!remote_ice_username_fragment_.empty()) { |  | 
|  158     channel_->SetRemoteIceCredentials(remote_ice_username_fragment_, |  | 
|  159                                       remote_ice_password_); |  | 
|  160   } |  | 
|  161  |  | 
|  162   while (!pending_candidates_.empty()) { |  | 
|  163     channel_->AddRemoteCandidate(pending_candidates_.front()); |  | 
|  164     pending_candidates_.pop_front(); |  | 
|  165   } |  | 
|  166 } |  | 
|  167  |  | 
|  168 void LibjingleTransport::Connect( |  | 
|  169     const std::string& name, |  | 
|  170     Transport::EventHandler* event_handler, |  | 
|  171     const Transport::ConnectedCallback& callback) { |  | 
|  172   DCHECK(CalledOnValidThread()); |  | 
|  173   DCHECK(!name.empty()); |  | 
|  174   DCHECK(event_handler); |  | 
|  175   DCHECK(!callback.is_null()); |  | 
|  176  |  | 
|  177   DCHECK(name_.empty()); |  | 
|  178   name_ = name; |  | 
|  179   event_handler_ = event_handler; |  | 
|  180   callback_ = callback; |  | 
|  181  |  | 
|  182   if (can_start_) |  | 
|  183     DoStart(); |  | 
|  184 } |  | 
|  185  |  | 
|  186 void LibjingleTransport::DoStart() { |  | 
|  187   DCHECK(!channel_.get()); |  | 
|  188  |  | 
|  189   // Create P2PTransportChannel, attach signal handlers and connect it. |  | 
|  190   // TODO(sergeyu): Specify correct component ID for the channel. |  | 
|  191   channel_.reset(new cricket::P2PTransportChannel( |  | 
|  192       std::string(), 0, nullptr, port_allocator_)); |  | 
|  193   std::string ice_password = rtc::CreateRandomString(cricket::ICE_PWD_LENGTH); |  | 
|  194   channel_->SetIceProtocolType(cricket::ICEPROTO_RFC5245); |  | 
|  195   channel_->SetIceRole((role_ == TransportRole::CLIENT) |  | 
|  196                            ? cricket::ICEROLE_CONTROLLING |  | 
|  197                            : cricket::ICEROLE_CONTROLLED); |  | 
|  198   event_handler_->OnTransportIceCredentials(this, ice_username_fragment_, |  | 
|  199                                             ice_password); |  | 
|  200   channel_->SetIceCredentials(ice_username_fragment_, ice_password); |  | 
|  201   channel_->SignalCandidateGathered.connect( |  | 
|  202       this, &LibjingleTransport::OnCandidateGathered); |  | 
|  203   channel_->SignalRouteChange.connect( |  | 
|  204       this, &LibjingleTransport::OnRouteChange); |  | 
|  205   channel_->SignalReceivingState.connect( |  | 
|  206       this, &LibjingleTransport::OnReceivingState); |  | 
|  207   channel_->SignalWritableState.connect( |  | 
|  208       this, &LibjingleTransport::OnWritableState); |  | 
|  209   channel_->set_incoming_only( |  | 
|  210       !(network_settings_.flags & NetworkSettings::NAT_TRAVERSAL_OUTGOING)); |  | 
|  211  |  | 
|  212   channel_->Connect(); |  | 
|  213   channel_->MaybeStartGathering(); |  | 
|  214  |  | 
|  215   --connect_attempts_left_; |  | 
|  216  |  | 
|  217   // Start reconnection timer. |  | 
|  218   reconnect_timer_.Start( |  | 
|  219       FROM_HERE, base::TimeDelta::FromSeconds(kReconnectDelaySeconds), |  | 
|  220       this, &LibjingleTransport::TryReconnect); |  | 
|  221 } |  | 
|  222  |  | 
|  223 void LibjingleTransport::NotifyConnected() { |  | 
|  224   // Create P2PDatagramSocket adapter for the P2PTransportChannel. |  | 
|  225   scoped_ptr<TransportChannelSocketAdapter> socket( |  | 
|  226       new TransportChannelSocketAdapter(channel_.get())); |  | 
|  227   socket->SetOnDestroyedCallback(base::Bind( |  | 
|  228       &LibjingleTransport::OnChannelDestroyed, base::Unretained(this))); |  | 
|  229   base::ResetAndReturn(&callback_).Run(socket.Pass()); |  | 
|  230 } |  | 
|  231  |  | 
|  232 void LibjingleTransport::SetRemoteCredentials(const std::string& ufrag, |  | 
|  233                                               const std::string& password) { |  | 
|  234   DCHECK(CalledOnValidThread()); |  | 
|  235  |  | 
|  236   remote_ice_username_fragment_ = ufrag; |  | 
|  237   remote_ice_password_ = password; |  | 
|  238  |  | 
|  239   if (channel_) |  | 
|  240     channel_->SetRemoteIceCredentials(ufrag, password); |  | 
|  241 } |  | 
|  242  |  | 
|  243 void LibjingleTransport::AddRemoteCandidate( |  | 
|  244     const cricket::Candidate& candidate) { |  | 
|  245   DCHECK(CalledOnValidThread()); |  | 
|  246  |  | 
|  247   // To enforce the no-relay setting, it's not enough to not produce relay |  | 
|  248   // candidates. It's also necessary to discard remote relay candidates. |  | 
|  249   bool relay_allowed = (network_settings_.flags & |  | 
|  250                         NetworkSettings::NAT_TRAVERSAL_RELAY) != 0; |  | 
|  251   if (!relay_allowed && candidate.type() == cricket::RELAY_PORT_TYPE) |  | 
|  252     return; |  | 
|  253  |  | 
|  254   if (channel_) { |  | 
|  255     channel_->AddRemoteCandidate(candidate); |  | 
|  256   } else { |  | 
|  257     pending_candidates_.push_back(candidate); |  | 
|  258   } |  | 
|  259 } |  | 
|  260  |  | 
|  261 const std::string& LibjingleTransport::name() const { |  | 
|  262   DCHECK(CalledOnValidThread()); |  | 
|  263   return name_; |  | 
|  264 } |  | 
|  265  |  | 
|  266 bool LibjingleTransport::is_connected() const { |  | 
|  267   DCHECK(CalledOnValidThread()); |  | 
|  268   return callback_.is_null(); |  | 
|  269 } |  | 
|  270  |  | 
|  271 void LibjingleTransport::OnCandidateGathered( |  | 
|  272     cricket::TransportChannelImpl* channel, |  | 
|  273     const cricket::Candidate& candidate) { |  | 
|  274   DCHECK(CalledOnValidThread()); |  | 
|  275   event_handler_->OnTransportCandidate(this, candidate); |  | 
|  276 } |  | 
|  277  |  | 
|  278 void LibjingleTransport::OnRouteChange( |  | 
|  279     cricket::TransportChannel* channel, |  | 
|  280     const cricket::Candidate& candidate) { |  | 
|  281   // Ignore notifications if the channel is not writable. |  | 
|  282   if (channel_->writable()) |  | 
|  283     NotifyRouteChanged(); |  | 
|  284 } |  | 
|  285  |  | 
|  286 void LibjingleTransport::OnReceivingState(cricket::TransportChannel* channel) { |  | 
|  287   DCHECK_EQ(channel, static_cast<cricket::TransportChannel*>(channel_.get())); |  | 
|  288  |  | 
|  289   if (channel->receiving() && !callback_.is_null()) |  | 
|  290     NotifyConnected(); |  | 
|  291 } |  | 
|  292  |  | 
|  293 void LibjingleTransport::OnWritableState(cricket::TransportChannel* channel) { |  | 
|  294   DCHECK_EQ(channel, static_cast<cricket::TransportChannel*>(channel_.get())); |  | 
|  295  |  | 
|  296   if (channel->writable()) { |  | 
|  297     connect_attempts_left_ = kMaxReconnectAttempts; |  | 
|  298     reconnect_timer_.Stop(); |  | 
|  299  |  | 
|  300     // Route change notifications are ignored when the |channel_| is not |  | 
|  301     // writable. Notify the event handler about the current route once the |  | 
|  302     // channel is writable. |  | 
|  303     NotifyRouteChanged(); |  | 
|  304   } else { |  | 
|  305     reconnect_timer_.Reset(); |  | 
|  306     TryReconnect(); |  | 
|  307   } |  | 
|  308 } |  | 
|  309  |  | 
|  310 void LibjingleTransport::OnChannelDestroyed() { |  | 
|  311   // The connection socket is being deleted, so delete the transport too. |  | 
|  312   delete this; |  | 
|  313 } |  | 
|  314  |  | 
|  315 void LibjingleTransport::NotifyRouteChanged() { |  | 
|  316   TransportRoute route; |  | 
|  317  |  | 
|  318   DCHECK(channel_->best_connection()); |  | 
|  319   const cricket::Connection* connection = channel_->best_connection(); |  | 
|  320  |  | 
|  321   // A connection has both a local and a remote candidate. For our purposes, the |  | 
|  322   // route type is determined by the most indirect candidate type. For example: |  | 
|  323   // it's possible for the local candidate be a "relay" type, while the remote |  | 
|  324   // candidate is "local". In this case, we still want to report a RELAY route |  | 
|  325   // type. |  | 
|  326   static_assert(TransportRoute::DIRECT < TransportRoute::STUN && |  | 
|  327                 TransportRoute::STUN < TransportRoute::RELAY, |  | 
|  328                 "Route type enum values are ordered by 'indirectness'"); |  | 
|  329   route.type = std::max( |  | 
|  330       CandidateTypeToTransportRouteType(connection->local_candidate().type()), |  | 
|  331       CandidateTypeToTransportRouteType(connection->remote_candidate().type())); |  | 
|  332  |  | 
|  333   if (!jingle_glue::SocketAddressToIPEndPoint( |  | 
|  334           connection->remote_candidate().address(), &route.remote_address)) { |  | 
|  335     LOG(FATAL) << "Failed to convert peer IP address."; |  | 
|  336   } |  | 
|  337  |  | 
|  338   const cricket::Candidate& local_candidate = |  | 
|  339       channel_->best_connection()->local_candidate(); |  | 
|  340   if (!jingle_glue::SocketAddressToIPEndPoint( |  | 
|  341           local_candidate.address(), &route.local_address)) { |  | 
|  342     LOG(FATAL) << "Failed to convert local IP address."; |  | 
|  343   } |  | 
|  344  |  | 
|  345   event_handler_->OnTransportRouteChange(this, route); |  | 
|  346 } |  | 
|  347  |  | 
|  348 void LibjingleTransport::TryReconnect() { |  | 
|  349   DCHECK(!channel_->writable()); |  | 
|  350  |  | 
|  351   if (connect_attempts_left_ <= 0) { |  | 
|  352     reconnect_timer_.Stop(); |  | 
|  353  |  | 
|  354     // Notify the caller that ICE connection has failed - normally that will |  | 
|  355     // terminate Jingle connection (i.e. the transport will be destroyed). |  | 
|  356     event_handler_->OnTransportFailed(this); |  | 
|  357     return; |  | 
|  358   } |  | 
|  359   --connect_attempts_left_; |  | 
|  360  |  | 
|  361   // Restart ICE by resetting ICE password. |  | 
|  362   std::string ice_password = rtc::CreateRandomString(cricket::ICE_PWD_LENGTH); |  | 
|  363   event_handler_->OnTransportIceCredentials(this, ice_username_fragment_, |  | 
|  364                                             ice_password); |  | 
|  365   channel_->SetIceCredentials(ice_username_fragment_, ice_password); |  | 
|  366 } |  | 
|  367  |  | 
|  368 }  // namespace |  | 
|  369  |  | 
|  370 LibjingleTransportFactory::LibjingleTransportFactory( |  | 
|  371     SignalStrategy* signal_strategy, |  | 
|  372     scoped_ptr<cricket::HttpPortAllocatorBase> port_allocator, |  | 
|  373     const NetworkSettings& network_settings, |  | 
|  374     TransportRole role) |  | 
|  375     : signal_strategy_(signal_strategy), |  | 
|  376       port_allocator_(port_allocator.Pass()), |  | 
|  377       network_settings_(network_settings), |  | 
|  378       role_(role) { |  | 
|  379 } |  | 
|  380  |  | 
|  381 LibjingleTransportFactory::~LibjingleTransportFactory() { |  | 
|  382   // This method may be called in response to a libjingle signal, so |  | 
|  383   // libjingle objects must be deleted asynchronously. |  | 
|  384   scoped_refptr<base::SingleThreadTaskRunner> task_runner = |  | 
|  385       base::ThreadTaskRunnerHandle::Get(); |  | 
|  386   task_runner->DeleteSoon(FROM_HERE, port_allocator_.release()); |  | 
|  387 } |  | 
|  388  |  | 
|  389 void LibjingleTransportFactory::PrepareTokens() { |  | 
|  390   EnsureFreshJingleInfo(); |  | 
|  391 } |  | 
|  392  |  | 
|  393 scoped_ptr<Transport> LibjingleTransportFactory::CreateTransport() { |  | 
|  394   scoped_ptr<LibjingleTransport> result( |  | 
|  395       new LibjingleTransport(port_allocator_.get(), network_settings_, role_)); |  | 
|  396  |  | 
|  397   EnsureFreshJingleInfo(); |  | 
|  398  |  | 
|  399   // If there is a pending |jingle_info_request_| delay starting the new |  | 
|  400   // transport until the request is finished. |  | 
|  401   if (jingle_info_request_) { |  | 
|  402     on_jingle_info_callbacks_.push_back( |  | 
|  403         base::Bind(&LibjingleTransport::OnCanStart, |  | 
|  404                    result->AsWeakPtr())); |  | 
|  405   } else { |  | 
|  406     result->OnCanStart(); |  | 
|  407   } |  | 
|  408  |  | 
|  409   return result.Pass(); |  | 
|  410 } |  | 
|  411  |  | 
|  412 void LibjingleTransportFactory::EnsureFreshJingleInfo() { |  | 
|  413   uint32 stun_or_relay_flags = NetworkSettings::NAT_TRAVERSAL_STUN | |  | 
|  414       NetworkSettings::NAT_TRAVERSAL_RELAY; |  | 
|  415   if (!(network_settings_.flags & stun_or_relay_flags) || |  | 
|  416       jingle_info_request_) { |  | 
|  417     return; |  | 
|  418   } |  | 
|  419  |  | 
|  420   if (last_jingle_info_update_time_.is_null() || |  | 
|  421       base::TimeTicks::Now() - last_jingle_info_update_time_ > |  | 
|  422           base::TimeDelta::FromSeconds(kJingleInfoUpdatePeriodSeconds)) { |  | 
|  423     jingle_info_request_.reset(new JingleInfoRequest(signal_strategy_)); |  | 
|  424     jingle_info_request_->Send(base::Bind( |  | 
|  425         &LibjingleTransportFactory::OnJingleInfo, base::Unretained(this))); |  | 
|  426   } |  | 
|  427 } |  | 
|  428  |  | 
|  429 void LibjingleTransportFactory::OnJingleInfo( |  | 
|  430     const std::string& relay_token, |  | 
|  431     const std::vector<std::string>& relay_hosts, |  | 
|  432     const std::vector<rtc::SocketAddress>& stun_hosts) { |  | 
|  433   if (!relay_token.empty() && !relay_hosts.empty()) { |  | 
|  434     port_allocator_->SetRelayHosts(relay_hosts); |  | 
|  435     port_allocator_->SetRelayToken(relay_token); |  | 
|  436   } |  | 
|  437   if (!stun_hosts.empty()) { |  | 
|  438     port_allocator_->SetStunHosts(stun_hosts); |  | 
|  439   } |  | 
|  440  |  | 
|  441   jingle_info_request_.reset(); |  | 
|  442   if ((!relay_token.empty() && !relay_hosts.empty()) || !stun_hosts.empty()) |  | 
|  443     last_jingle_info_update_time_ = base::TimeTicks::Now(); |  | 
|  444  |  | 
|  445   while (!on_jingle_info_callbacks_.empty()) { |  | 
|  446     on_jingle_info_callbacks_.begin()->Run(); |  | 
|  447     on_jingle_info_callbacks_.pop_front(); |  | 
|  448   } |  | 
|  449 } |  | 
|  450  |  | 
|  451 }  // namespace protocol |  | 
|  452 }  // namespace remoting |  | 
| OLD | NEW |