Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / libjingle / source / talk / p2p / client / connectivitychecker.cc
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 "webrtc/p2p/client/connectivitychecker.h"
31
32 #include "webrtc/p2p/base/candidate.h"
33 #include "webrtc/p2p/base/common.h"
34 #include "webrtc/p2p/base/constants.h"
35 #include "webrtc/p2p/base/port.h"
36 #include "webrtc/p2p/base/relayport.h"
37 #include "webrtc/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