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