Home | History | Annotate | Download | only in net
      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 "chrome/browser/net/dns_probe_service.h"
      6 
      7 #include "base/metrics/field_trial.h"
      8 #include "base/metrics/histogram.h"
      9 #include "base/strings/string_number_conversions.h"
     10 #include "net/base/ip_endpoint.h"
     11 #include "net/base/net_util.h"
     12 #include "net/dns/dns_client.h"
     13 #include "net/dns/dns_config_service.h"
     14 #include "net/dns/dns_protocol.h"
     15 
     16 using base::FieldTrialList;
     17 using base::StringToInt;
     18 using chrome_common_net::DnsProbeStatus;
     19 using net::DnsClient;
     20 using net::DnsConfig;
     21 using net::IPAddressNumber;
     22 using net::IPEndPoint;
     23 using net::ParseIPLiteralToNumber;
     24 using net::NetworkChangeNotifier;
     25 
     26 namespace chrome_browser_net {
     27 
     28 namespace {
     29 
     30 // How long the DnsProbeService will cache the probe result for.
     31 // If it's older than this and we get a probe request, the service expires it
     32 // and starts a new probe.
     33 const int kMaxResultAgeMs = 5000;
     34 
     35 // The public DNS servers used by the DnsProbeService to verify internet
     36 // connectivity.
     37 const char kGooglePublicDns1[] = "8.8.8.8";
     38 const char kGooglePublicDns2[] = "8.8.4.4";
     39 
     40 IPEndPoint MakeDnsEndPoint(const std::string& dns_ip_literal) {
     41   IPAddressNumber dns_ip_number;
     42   bool rv = ParseIPLiteralToNumber(dns_ip_literal, &dns_ip_number);
     43   DCHECK(rv);
     44   return IPEndPoint(dns_ip_number, net::dns_protocol::kDefaultPort);
     45 }
     46 
     47 DnsProbeStatus EvaluateResults(DnsProbeRunner::Result system_result,
     48                                DnsProbeRunner::Result public_result) {
     49   // If the system DNS is working, assume the domain doesn't exist.
     50   if (system_result == DnsProbeRunner::CORRECT)
     51     return chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN;
     52 
     53   // If the system DNS is unknown (e.g. on Android), but the public server is
     54   // reachable, assume the domain doesn't exist.
     55   if (system_result == DnsProbeRunner::UNKNOWN &&
     56       public_result == DnsProbeRunner::CORRECT) {
     57     return chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN;
     58   }
     59 
     60   // If the system DNS is not working but another public server is, assume the
     61   // DNS config is bad (or perhaps the DNS servers are down or broken).
     62   if (public_result == DnsProbeRunner::CORRECT)
     63     return chrome_common_net::DNS_PROBE_FINISHED_BAD_CONFIG;
     64 
     65   // If the system DNS is not working and another public server is unreachable,
     66   // assume the internet connection is down (note that system DNS may be a
     67   // router on the LAN, so it may be reachable but returning errors.)
     68   if (public_result == DnsProbeRunner::UNREACHABLE)
     69     return chrome_common_net::DNS_PROBE_FINISHED_NO_INTERNET;
     70 
     71   // Otherwise: the system DNS is not working and another public server is
     72   // responding but with errors or incorrect results.  This is an awkward case;
     73   // an invasive captive portal or a restrictive firewall may be intercepting
     74   // or rewriting DNS traffic, or the public server may itself be failing or
     75   // down.
     76   return chrome_common_net::DNS_PROBE_FINISHED_INCONCLUSIVE;
     77 }
     78 
     79 void HistogramProbe(DnsProbeStatus status, base::TimeDelta elapsed) {
     80   DCHECK(chrome_common_net::DnsProbeStatusIsFinished(status));
     81 
     82   UMA_HISTOGRAM_ENUMERATION("DnsProbe.ProbeResult", status,
     83                             chrome_common_net::DNS_PROBE_MAX);
     84   UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.ProbeDuration", elapsed);
     85 }
     86 
     87 }  // namespace
     88 
     89 DnsProbeService::DnsProbeService()
     90     : state_(STATE_NO_RESULT) {
     91   NetworkChangeNotifier::AddDNSObserver(this);
     92   SetSystemClientToCurrentConfig();
     93   SetPublicClientToGooglePublicDns();
     94 }
     95 
     96 DnsProbeService::~DnsProbeService() {
     97   NetworkChangeNotifier::RemoveDNSObserver(this);
     98 }
     99 
    100 void DnsProbeService::ProbeDns(const DnsProbeService::ProbeCallback& callback) {
    101   pending_callbacks_.push_back(callback);
    102 
    103   if (CachedResultIsExpired())
    104     ClearCachedResult();
    105 
    106   switch (state_) {
    107     case STATE_NO_RESULT:
    108       StartProbes();
    109       break;
    110     case STATE_RESULT_CACHED:
    111       CallCallbacks();
    112       break;
    113     case STATE_PROBE_RUNNING:
    114       // Do nothing; probe is already running, and will call the callback.
    115       break;
    116   }
    117 }
    118 
    119 void DnsProbeService::OnDNSChanged() {
    120   ClearCachedResult();
    121   SetSystemClientToCurrentConfig();
    122 }
    123 
    124 void DnsProbeService::SetSystemClientForTesting(
    125     scoped_ptr<DnsClient> system_client) {
    126   system_runner_.SetClient(system_client.Pass());
    127 }
    128 
    129 void DnsProbeService::SetPublicClientForTesting(
    130     scoped_ptr<DnsClient> public_client) {
    131   public_runner_.SetClient(public_client.Pass());
    132 }
    133 
    134 void DnsProbeService::ClearCachedResultForTesting() {
    135   ClearCachedResult();
    136 }
    137 
    138 void DnsProbeService::SetSystemClientToCurrentConfig() {
    139   DnsConfig system_config;
    140   NetworkChangeNotifier::GetDnsConfig(&system_config);
    141   system_config.search.clear();
    142   system_config.attempts = 1;
    143   system_config.randomize_ports = false;
    144 
    145   scoped_ptr<DnsClient> system_client(DnsClient::CreateClient(NULL));
    146   system_client->SetConfig(system_config);
    147 
    148   system_runner_.SetClient(system_client.Pass());
    149 }
    150 
    151 void DnsProbeService::SetPublicClientToGooglePublicDns() {
    152   DnsConfig public_config;
    153   public_config.nameservers.push_back(MakeDnsEndPoint(kGooglePublicDns1));
    154   public_config.nameservers.push_back(MakeDnsEndPoint(kGooglePublicDns2));
    155   public_config.attempts = 1;
    156   public_config.randomize_ports = false;
    157 
    158   scoped_ptr<DnsClient> public_client(DnsClient::CreateClient(NULL));
    159   public_client->SetConfig(public_config);
    160 
    161   public_runner_.SetClient(public_client.Pass());
    162 }
    163 
    164 void DnsProbeService::StartProbes() {
    165   DCHECK_EQ(STATE_NO_RESULT, state_);
    166 
    167   DCHECK(!system_runner_.IsRunning());
    168   DCHECK(!public_runner_.IsRunning());
    169 
    170   const base::Closure callback = base::Bind(&DnsProbeService::OnProbeComplete,
    171                                             base::Unretained(this));
    172   system_runner_.RunProbe(callback);
    173   public_runner_.RunProbe(callback);
    174   probe_start_time_ = base::Time::Now();
    175   state_ = STATE_PROBE_RUNNING;
    176 
    177   DCHECK(system_runner_.IsRunning());
    178   DCHECK(public_runner_.IsRunning());
    179 }
    180 
    181 void DnsProbeService::OnProbeComplete() {
    182   DCHECK_EQ(STATE_PROBE_RUNNING, state_);
    183 
    184   if (system_runner_.IsRunning() || public_runner_.IsRunning())
    185     return;
    186 
    187   cached_result_ = EvaluateResults(system_runner_.result(),
    188                                    public_runner_.result());
    189   state_ = STATE_RESULT_CACHED;
    190 
    191   HistogramProbe(cached_result_, base::Time::Now() - probe_start_time_);
    192 
    193   CallCallbacks();
    194 }
    195 
    196 void DnsProbeService::CallCallbacks() {
    197   DCHECK_EQ(STATE_RESULT_CACHED, state_);
    198   DCHECK(chrome_common_net::DnsProbeStatusIsFinished(cached_result_));
    199   DCHECK(!pending_callbacks_.empty());
    200 
    201   std::vector<ProbeCallback> callbacks;
    202   callbacks.swap(pending_callbacks_);
    203 
    204   for (std::vector<ProbeCallback>::const_iterator i = callbacks.begin();
    205        i != callbacks.end(); ++i) {
    206     i->Run(cached_result_);
    207   }
    208 }
    209 
    210 void DnsProbeService::ClearCachedResult() {
    211   if (state_ == STATE_RESULT_CACHED) {
    212     state_ = STATE_NO_RESULT;
    213     cached_result_ = chrome_common_net::DNS_PROBE_MAX;
    214   }
    215 }
    216 
    217 bool DnsProbeService::CachedResultIsExpired() const {
    218   if (state_ != STATE_RESULT_CACHED)
    219     return false;
    220 
    221   const base::TimeDelta kMaxResultAge =
    222       base::TimeDelta::FromMilliseconds(kMaxResultAgeMs);
    223   return base::Time::Now() - probe_start_time_ > kMaxResultAge;
    224 }
    225 
    226 }  // namespace chrome_browser_net
    227