Home | History | Annotate | Download | only in client
      1 /*
      2  * libjingle
      3  * Copyright 2011, Google Inc.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are met:
      7  *
      8  *  1. Redistributions of source code must retain the above copyright notice,
      9  *     this list of conditions and the following disclaimer.
     10  *  2. Redistributions in binary form must reproduce the above copyright notice,
     11  *     this list of conditions and the following disclaimer in the documentation
     12  *     and/or other materials provided with the distribution.
     13  *  3. The name of the author may not be used to endorse or promote products
     14  *     derived from this software without specific prior written permission.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
     17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
     19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
     25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 #include <string>
     29 
     30 #include "talk/p2p/client/connectivitychecker.h"
     31 
     32 #include "talk/p2p/base/candidate.h"
     33 #include "talk/p2p/base/common.h"
     34 #include "talk/p2p/base/constants.h"
     35 #include "talk/p2p/base/port.h"
     36 #include "talk/p2p/base/relayport.h"
     37 #include "talk/p2p/base/stunport.h"
     38 #include "webrtc/base/asynchttprequest.h"
     39 #include "webrtc/base/autodetectproxy.h"
     40 #include "webrtc/base/helpers.h"
     41 #include "webrtc/base/httpcommon-inl.h"
     42 #include "webrtc/base/httpcommon.h"
     43 #include "webrtc/base/logging.h"
     44 #include "webrtc/base/proxydetect.h"
     45 #include "webrtc/base/thread.h"
     46 
     47 namespace cricket {
     48 
     49 static const char kDefaultStunHostname[] = "stun.l.google.com";
     50 static const int kDefaultStunPort = 19302;
     51 
     52 // Default maximum time in milliseconds we will wait for connections.
     53 static const uint32 kDefaultTimeoutMs = 3000;
     54 
     55 enum {
     56   MSG_START = 1,
     57   MSG_STOP = 2,
     58   MSG_TIMEOUT = 3,
     59   MSG_SIGNAL_RESULTS = 4
     60 };
     61 
     62 class TestHttpPortAllocator : public HttpPortAllocator {
     63  public:
     64   TestHttpPortAllocator(rtc::NetworkManager* network_manager,
     65                         const std::string& user_agent,
     66                         const std::string& relay_token) :
     67       HttpPortAllocator(network_manager, user_agent) {
     68     SetRelayToken(relay_token);
     69   }
     70   PortAllocatorSession* CreateSessionInternal(
     71       const std::string& content_name,
     72       int component,
     73       const std::string& ice_ufrag,
     74       const std::string& ice_pwd) {
     75     return new TestHttpPortAllocatorSession(this, content_name, component,
     76                                             ice_ufrag, ice_pwd,
     77                                             stun_hosts(), relay_hosts(),
     78                                             relay_token(), user_agent());
     79   }
     80 };
     81 
     82 void TestHttpPortAllocatorSession::ConfigReady(PortConfiguration* config) {
     83   SignalConfigReady(username(), password(), config, proxy_);
     84   delete config;
     85 }
     86 
     87 void TestHttpPortAllocatorSession::OnRequestDone(
     88     rtc::SignalThread* data) {
     89   rtc::AsyncHttpRequest* request =
     90       static_cast<rtc::AsyncHttpRequest*>(data);
     91 
     92   // Tell the checker that the request is complete.
     93   SignalRequestDone(request);
     94 
     95   // Pass on the response to super class.
     96   HttpPortAllocatorSession::OnRequestDone(data);
     97 }
     98 
     99 ConnectivityChecker::ConnectivityChecker(
    100     rtc::Thread* worker,
    101     const std::string& jid,
    102     const std::string& session_id,
    103     const std::string& user_agent,
    104     const std::string& relay_token,
    105     const std::string& connection)
    106     : worker_(worker),
    107       jid_(jid),
    108       session_id_(session_id),
    109       user_agent_(user_agent),
    110       relay_token_(relay_token),
    111       connection_(connection),
    112       proxy_detect_(NULL),
    113       timeout_ms_(kDefaultTimeoutMs),
    114       stun_address_(kDefaultStunHostname, kDefaultStunPort),
    115       started_(false) {
    116 }
    117 
    118 ConnectivityChecker::~ConnectivityChecker() {
    119   if (started_) {
    120     // We try to clear the TIMEOUT below. But worker may still handle it and
    121     // cause SignalCheckDone to happen on main-thread. So we finally clear any
    122     // pending SIGNAL_RESULTS.
    123     worker_->Clear(this, MSG_TIMEOUT);
    124     worker_->Send(this, MSG_STOP);
    125     nics_.clear();
    126     main_->Clear(this, MSG_SIGNAL_RESULTS);
    127   }
    128 }
    129 
    130 bool ConnectivityChecker::Initialize() {
    131   network_manager_.reset(CreateNetworkManager());
    132   socket_factory_.reset(CreateSocketFactory(worker_));
    133   port_allocator_.reset(CreatePortAllocator(network_manager_.get(),
    134                                             user_agent_, relay_token_));
    135   uint32 new_allocator_flags = port_allocator_->flags();
    136   new_allocator_flags |= cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG;
    137   port_allocator_->set_flags(new_allocator_flags);
    138   return true;
    139 }
    140 
    141 void ConnectivityChecker::Start() {
    142   main_ = rtc::Thread::Current();
    143   worker_->Post(this, MSG_START);
    144   started_ = true;
    145 }
    146 
    147 void ConnectivityChecker::CleanUp() {
    148   ASSERT(worker_ == rtc::Thread::Current());
    149   if (proxy_detect_) {
    150     proxy_detect_->Release();
    151     proxy_detect_ = NULL;
    152   }
    153 
    154   for (uint32 i = 0; i < sessions_.size(); ++i) {
    155     delete sessions_[i];
    156   }
    157   sessions_.clear();
    158   for (uint32 i = 0; i < ports_.size(); ++i) {
    159     delete ports_[i];
    160   }
    161   ports_.clear();
    162 }
    163 
    164 bool ConnectivityChecker::AddNic(const rtc::IPAddress& ip,
    165                                  const rtc::SocketAddress& proxy_addr) {
    166   NicMap::iterator i = nics_.find(NicId(ip, proxy_addr));
    167   if (i != nics_.end()) {
    168     // Already have it.
    169     return false;
    170   }
    171   uint32 now = rtc::Time();
    172   NicInfo info;
    173   info.ip = ip;
    174   info.proxy_info = GetProxyInfo();
    175   info.stun.start_time_ms = now;
    176   nics_.insert(std::pair<NicId, NicInfo>(NicId(ip, proxy_addr), info));
    177   return true;
    178 }
    179 
    180 void ConnectivityChecker::SetProxyInfo(const rtc::ProxyInfo& proxy_info) {
    181   port_allocator_->set_proxy(user_agent_, proxy_info);
    182   AllocatePorts();
    183 }
    184 
    185 rtc::ProxyInfo ConnectivityChecker::GetProxyInfo() const {
    186   rtc::ProxyInfo proxy_info;
    187   if (proxy_detect_) {
    188     proxy_info = proxy_detect_->proxy();
    189   }
    190   return proxy_info;
    191 }
    192 
    193 void ConnectivityChecker::CheckNetworks() {
    194   network_manager_->SignalNetworksChanged.connect(
    195       this, &ConnectivityChecker::OnNetworksChanged);
    196   network_manager_->StartUpdating();
    197 }
    198 
    199 void ConnectivityChecker::OnMessage(rtc::Message *msg) {
    200   switch (msg->message_id) {
    201     case MSG_START:
    202       ASSERT(worker_ == rtc::Thread::Current());
    203       worker_->PostDelayed(timeout_ms_, this, MSG_TIMEOUT);
    204       CheckNetworks();
    205       break;
    206     case MSG_STOP:
    207       // We're being stopped, free resources.
    208       CleanUp();
    209       break;
    210     case MSG_TIMEOUT:
    211       // We need to signal results on the main thread.
    212       main_->Post(this, MSG_SIGNAL_RESULTS);
    213       break;
    214     case MSG_SIGNAL_RESULTS:
    215       ASSERT(main_ == rtc::Thread::Current());
    216       SignalCheckDone(this);
    217       break;
    218     default:
    219       LOG(LS_ERROR) << "Unknown message: " << msg->message_id;
    220   }
    221 }
    222 
    223 void ConnectivityChecker::OnProxyDetect(rtc::SignalThread* thread) {
    224   ASSERT(worker_ == rtc::Thread::Current());
    225   if (proxy_detect_->proxy().type != rtc::PROXY_NONE) {
    226     SetProxyInfo(proxy_detect_->proxy());
    227   }
    228 }
    229 
    230 void ConnectivityChecker::OnRequestDone(rtc::AsyncHttpRequest* request) {
    231   ASSERT(worker_ == rtc::Thread::Current());
    232   // Since we don't know what nic were actually used for the http request,
    233   // for now, just use the first one.
    234   std::vector<rtc::Network*> networks;
    235   network_manager_->GetNetworks(&networks);
    236   if (networks.empty()) {
    237     LOG(LS_ERROR) << "No networks while registering http start.";
    238     return;
    239   }
    240   rtc::ProxyInfo proxy_info = request->proxy();
    241   NicMap::iterator i =
    242 #ifdef USE_WEBRTC_DEV_BRANCH
    243       nics_.find(NicId(networks[0]->GetBestIP(), proxy_info.address));
    244 #else  // USE_WEBRTC_DEV_BRANCH
    245       nics_.find(NicId(networks[0]->ip(), proxy_info.address));
    246 #endif  // USE_WEBRTC_DEV_BRANCH
    247   if (i != nics_.end()) {
    248     int port = request->port();
    249     uint32 now = rtc::Time();
    250     NicInfo* nic_info = &i->second;
    251     if (port == rtc::HTTP_DEFAULT_PORT) {
    252       nic_info->http.rtt = now - nic_info->http.start_time_ms;
    253     } else if (port == rtc::HTTP_SECURE_PORT) {
    254       nic_info->https.rtt = now - nic_info->https.start_time_ms;
    255     } else {
    256       LOG(LS_ERROR) << "Got response with unknown port: " << port;
    257     }
    258   } else {
    259     LOG(LS_ERROR) << "No nic info found while receiving response.";
    260   }
    261 }
    262 
    263 void ConnectivityChecker::OnConfigReady(
    264     const std::string& username, const std::string& password,
    265     const PortConfiguration* config, const rtc::ProxyInfo& proxy_info) {
    266   ASSERT(worker_ == rtc::Thread::Current());
    267 
    268   // Since we send requests on both HTTP and HTTPS we will get two
    269   // configs per nic. Results from the second will overwrite the
    270   // result from the first.
    271   // TODO: Handle multiple pings on one nic.
    272   CreateRelayPorts(username, password, config, proxy_info);
    273 }
    274 
    275 void ConnectivityChecker::OnRelayPortComplete(Port* port) {
    276   ASSERT(worker_ == rtc::Thread::Current());
    277   RelayPort* relay_port = reinterpret_cast<RelayPort*>(port);
    278   const ProtocolAddress* address = relay_port->ServerAddress(0);
    279 #ifdef USE_WEBRTC_DEV_BRANCH
    280   rtc::IPAddress ip = port->Network()->GetBestIP();
    281 #else  // USE_WEBRTC_DEV_BRANCH
    282   rtc::IPAddress ip = port->Network()->ip();
    283 #endif  // USE_WEBRTC_DEV_BRANCH
    284   NicMap::iterator i = nics_.find(NicId(ip, port->proxy().address));
    285   if (i != nics_.end()) {
    286     // We have it already, add the new information.
    287     NicInfo* nic_info = &i->second;
    288     ConnectInfo* connect_info = NULL;
    289     if (address) {
    290       switch (address->proto) {
    291         case PROTO_UDP:
    292           connect_info = &nic_info->udp;
    293           break;
    294         case PROTO_TCP:
    295           connect_info = &nic_info->tcp;
    296           break;
    297         case PROTO_SSLTCP:
    298           connect_info = &nic_info->ssltcp;
    299           break;
    300         default:
    301           LOG(LS_ERROR) << " relay address with bad protocol added";
    302       }
    303       if (connect_info) {
    304         connect_info->rtt =
    305             rtc::TimeSince(connect_info->start_time_ms);
    306       }
    307     }
    308   } else {
    309     LOG(LS_ERROR) << " got relay address for non-existing nic";
    310   }
    311 }
    312 
    313 void ConnectivityChecker::OnStunPortComplete(Port* port) {
    314   ASSERT(worker_ == rtc::Thread::Current());
    315   const std::vector<Candidate> candidates = port->Candidates();
    316   Candidate c = candidates[0];
    317 #ifdef USE_WEBRTC_DEV_BRANCH
    318   rtc::IPAddress ip = port->Network()->GetBestIP();
    319 #else  // USE_WEBRTC_DEV_BRANCH
    320   rtc::IPAddress ip = port->Network()->ip();
    321 #endif  // USE_WEBRTC_DEV_BRANCH
    322   NicMap::iterator i = nics_.find(NicId(ip, port->proxy().address));
    323   if (i != nics_.end()) {
    324     // We have it already, add the new information.
    325     uint32 now = rtc::Time();
    326     NicInfo* nic_info = &i->second;
    327     nic_info->external_address = c.address();
    328 
    329     nic_info->stun_server_addresses =
    330         static_cast<StunPort*>(port)->server_addresses();
    331     nic_info->stun.rtt = now - nic_info->stun.start_time_ms;
    332   } else {
    333     LOG(LS_ERROR) << "Got stun address for non-existing nic";
    334   }
    335 }
    336 
    337 void ConnectivityChecker::OnStunPortError(Port* port) {
    338   ASSERT(worker_ == rtc::Thread::Current());
    339   LOG(LS_ERROR) << "Stun address error.";
    340 #ifdef USE_WEBRTC_DEV_BRANCH
    341   rtc::IPAddress ip = port->Network()->GetBestIP();
    342 #else  // USE_WEBRTC_DEV_BRANCH
    343   rtc::IPAddress ip = port->Network()->ip();
    344 #endif  // USE_WEBRTC_DEV_BRANCH
    345   NicMap::iterator i = nics_.find(NicId(ip, port->proxy().address));
    346   if (i != nics_.end()) {
    347     // We have it already, add the new information.
    348     NicInfo* nic_info = &i->second;
    349 
    350     nic_info->stun_server_addresses =
    351         static_cast<StunPort*>(port)->server_addresses();
    352   }
    353 }
    354 
    355 void ConnectivityChecker::OnRelayPortError(Port* port) {
    356   ASSERT(worker_ == rtc::Thread::Current());
    357   LOG(LS_ERROR) << "Relay address error.";
    358 }
    359 
    360 void ConnectivityChecker::OnNetworksChanged() {
    361   ASSERT(worker_ == rtc::Thread::Current());
    362   std::vector<rtc::Network*> networks;
    363   network_manager_->GetNetworks(&networks);
    364   if (networks.empty()) {
    365     LOG(LS_ERROR) << "Machine has no networks; nothing to do";
    366     return;
    367   }
    368   AllocatePorts();
    369 }
    370 
    371 HttpPortAllocator* ConnectivityChecker::CreatePortAllocator(
    372     rtc::NetworkManager* network_manager,
    373     const std::string& user_agent,
    374     const std::string& relay_token) {
    375   return new TestHttpPortAllocator(network_manager, user_agent, relay_token);
    376 }
    377 
    378 StunPort* ConnectivityChecker::CreateStunPort(
    379     const std::string& username, const std::string& password,
    380     const PortConfiguration* config, rtc::Network* network) {
    381   return StunPort::Create(worker_,
    382                           socket_factory_.get(),
    383                           network,
    384 #ifdef USE_WEBRTC_DEV_BRANCH
    385                           network->GetBestIP(),
    386 #else  // USE_WEBRTC_DEV_BRANCH
    387                           network->ip(),
    388 #endif  // USE_WEBRTC_DEV_BRANCH
    389                           0,
    390                           0,
    391                           username,
    392                           password,
    393                           config->stun_servers);
    394 }
    395 
    396 RelayPort* ConnectivityChecker::CreateRelayPort(
    397     const std::string& username, const std::string& password,
    398     const PortConfiguration* config, rtc::Network* network) {
    399   return RelayPort::Create(worker_,
    400                            socket_factory_.get(),
    401                            network,
    402 #ifdef USE_WEBRTC_DEV_BRANCH
    403                            network->GetBestIP(),
    404 #else  // USE_WEBRTC_DEV_BRANCH
    405                            network->ip(),
    406 #endif  // USE_WEBRTC_DEV_BRANCH
    407                            port_allocator_->min_port(),
    408                            port_allocator_->max_port(),
    409                            username,
    410                            password);
    411 }
    412 
    413 void ConnectivityChecker::CreateRelayPorts(
    414     const std::string& username, const std::string& password,
    415     const PortConfiguration* config, const rtc::ProxyInfo& proxy_info) {
    416   PortConfiguration::RelayList::const_iterator relay;
    417   std::vector<rtc::Network*> networks;
    418   network_manager_->GetNetworks(&networks);
    419   if (networks.empty()) {
    420     LOG(LS_ERROR) << "Machine has no networks; no relay ports created.";
    421     return;
    422   }
    423   for (relay = config->relays.begin();
    424        relay != config->relays.end(); ++relay) {
    425     for (uint32 i = 0; i < networks.size(); ++i) {
    426       NicMap::iterator iter =
    427 #ifdef USE_WEBRTC_DEV_BRANCH
    428           nics_.find(NicId(networks[i]->GetBestIP(), proxy_info.address));
    429 #else  // USE_WEBRTC_DEV_BRANCH
    430           nics_.find(NicId(networks[i]->ip(), proxy_info.address));
    431 #endif  // USE_WEBRTC_DEV_BRANCH
    432       if (iter != nics_.end()) {
    433         // TODO: Now setting the same start time for all protocols.
    434         // This might affect accuracy, but since we are mainly looking for
    435         // connect failures or number that stick out, this is good enough.
    436         uint32 now = rtc::Time();
    437         NicInfo* nic_info = &iter->second;
    438         nic_info->udp.start_time_ms = now;
    439         nic_info->tcp.start_time_ms = now;
    440         nic_info->ssltcp.start_time_ms = now;
    441 
    442         // Add the addresses of this protocol.
    443         PortList::const_iterator relay_port;
    444         for (relay_port = relay->ports.begin();
    445              relay_port != relay->ports.end();
    446              ++relay_port) {
    447           RelayPort* port = CreateRelayPort(username, password,
    448                                             config, networks[i]);
    449           port->AddServerAddress(*relay_port);
    450           port->AddExternalAddress(*relay_port);
    451 
    452           nic_info->media_server_address = port->ServerAddress(0)->address;
    453 
    454           // Listen to network events.
    455           port->SignalPortComplete.connect(
    456               this, &ConnectivityChecker::OnRelayPortComplete);
    457           port->SignalPortError.connect(
    458               this, &ConnectivityChecker::OnRelayPortError);
    459 
    460           port->set_proxy(user_agent_, proxy_info);
    461 
    462           // Start fetching an address for this port.
    463           port->PrepareAddress();
    464           ports_.push_back(port);
    465         }
    466       } else {
    467         LOG(LS_ERROR) << "Failed to find nic info when creating relay ports.";
    468       }
    469     }
    470   }
    471 }
    472 
    473 void ConnectivityChecker::AllocatePorts() {
    474   const std::string username = rtc::CreateRandomString(ICE_UFRAG_LENGTH);
    475   const std::string password = rtc::CreateRandomString(ICE_PWD_LENGTH);
    476   ServerAddresses stun_servers;
    477   stun_servers.insert(stun_address_);
    478   PortConfiguration config(stun_servers, username, password);
    479   std::vector<rtc::Network*> networks;
    480   network_manager_->GetNetworks(&networks);
    481   if (networks.empty()) {
    482     LOG(LS_ERROR) << "Machine has no networks; no ports will be allocated";
    483     return;
    484   }
    485   rtc::ProxyInfo proxy_info = GetProxyInfo();
    486   bool allocate_relay_ports = false;
    487   for (uint32 i = 0; i < networks.size(); ++i) {
    488 #ifdef USE_WEBRTC_DEV_BRANCH
    489     if (AddNic(networks[i]->GetBestIP(), proxy_info.address)) {
    490 #else  // USE_WEBRTC_DEV_BRANCH
    491     if (AddNic(networks[i]->ip(), proxy_info.address)) {
    492 #endif  // USE_WEBRTC_DEV_BRANCH
    493       Port* port = CreateStunPort(username, password, &config, networks[i]);
    494       if (port) {
    495 
    496         // Listen to network events.
    497         port->SignalPortComplete.connect(
    498             this, &ConnectivityChecker::OnStunPortComplete);
    499         port->SignalPortError.connect(
    500             this, &ConnectivityChecker::OnStunPortError);
    501 
    502         port->set_proxy(user_agent_, proxy_info);
    503         port->PrepareAddress();
    504         ports_.push_back(port);
    505         allocate_relay_ports = true;
    506       }
    507     }
    508   }
    509 
    510   // If any new ip/proxy combinations were added, send a relay allocate.
    511   if (allocate_relay_ports) {
    512     AllocateRelayPorts();
    513   }
    514 
    515   // Initiate proxy detection.
    516   InitiateProxyDetection();
    517 }
    518 
    519 void ConnectivityChecker::InitiateProxyDetection() {
    520   // Only start if we haven't been started before.
    521   if (!proxy_detect_) {
    522     proxy_detect_ = new rtc::AutoDetectProxy(user_agent_);
    523     rtc::Url<char> host_url("/", "relay.google.com",
    524                                   rtc::HTTP_DEFAULT_PORT);
    525     host_url.set_secure(true);
    526     proxy_detect_->set_server_url(host_url.url());
    527     proxy_detect_->SignalWorkDone.connect(
    528         this, &ConnectivityChecker::OnProxyDetect);
    529     proxy_detect_->Start();
    530   }
    531 }
    532 
    533 void ConnectivityChecker::AllocateRelayPorts() {
    534   // Currently we are using the 'default' nic for http(s) requests.
    535   TestHttpPortAllocatorSession* allocator_session =
    536       reinterpret_cast<TestHttpPortAllocatorSession*>(
    537           port_allocator_->CreateSessionInternal(
    538               "connectivity checker test content",
    539               ICE_CANDIDATE_COMPONENT_RTP,
    540               rtc::CreateRandomString(ICE_UFRAG_LENGTH),
    541               rtc::CreateRandomString(ICE_PWD_LENGTH)));
    542   allocator_session->set_proxy(port_allocator_->proxy());
    543   allocator_session->SignalConfigReady.connect(
    544       this, &ConnectivityChecker::OnConfigReady);
    545   allocator_session->SignalRequestDone.connect(
    546       this, &ConnectivityChecker::OnRequestDone);
    547 
    548   // Try both http and https.
    549   RegisterHttpStart(rtc::HTTP_SECURE_PORT);
    550   allocator_session->SendSessionRequest("relay.l.google.com",
    551                                         rtc::HTTP_SECURE_PORT);
    552   RegisterHttpStart(rtc::HTTP_DEFAULT_PORT);
    553   allocator_session->SendSessionRequest("relay.l.google.com",
    554                                         rtc::HTTP_DEFAULT_PORT);
    555 
    556   sessions_.push_back(allocator_session);
    557 }
    558 
    559 void ConnectivityChecker::RegisterHttpStart(int port) {
    560   // Since we don't know what nic were actually used for the http request,
    561   // for now, just use the first one.
    562   std::vector<rtc::Network*> networks;
    563   network_manager_->GetNetworks(&networks);
    564   if (networks.empty()) {
    565     LOG(LS_ERROR) << "No networks while registering http start.";
    566     return;
    567   }
    568   rtc::ProxyInfo proxy_info = GetProxyInfo();
    569   NicMap::iterator i =
    570 #ifdef USE_WEBRTC_DEV_BRANCH
    571       nics_.find(NicId(networks[0]->GetBestIP(), proxy_info.address));
    572 #else  // USE_WEBRTC_DEV_BRANCH
    573       nics_.find(NicId(networks[0]->ip(), proxy_info.address));
    574 #endif  // USE_WEBRTC_DEV_BRANCH
    575   if (i != nics_.end()) {
    576     uint32 now = rtc::Time();
    577     NicInfo* nic_info = &i->second;
    578     if (port == rtc::HTTP_DEFAULT_PORT) {
    579       nic_info->http.start_time_ms = now;
    580     } else if (port == rtc::HTTP_SECURE_PORT) {
    581       nic_info->https.start_time_ms = now;
    582     } else {
    583       LOG(LS_ERROR) << "Registering start time for unknown port: " << port;
    584     }
    585   } else {
    586     LOG(LS_ERROR) << "Error, no nic info found while registering http start.";
    587   }
    588 }
    589 
    590 }  // namespace rtc
    591