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