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.
5 #include "chrome/browser/net/dns_probe_service.h"
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"
16 using base::FieldTrialList
;
17 using base::StringToInt
;
18 using chrome_common_net::DnsProbeStatus
;
21 using net::IPAddressNumber
;
22 using net::ParseIPLiteralToNumber
;
23 using net::NetworkChangeNotifier
;
25 namespace chrome_browser_net
{
29 // How long the DnsProbeService will cache the probe result for.
30 // If it's older than this and we get a probe request, the service expires it
31 // and starts a new probe.
32 const int kMaxResultAgeMs
= 5000;
34 // The public DNS servers used by the DnsProbeService to verify internet
36 const char kGooglePublicDns1
[] = "8.8.8.8";
37 const char kGooglePublicDns2
[] = "8.8.4.4";
39 net::IPEndPoint
MakeDnsEndPoint(const std::string
& dns_ip_literal
) {
40 IPAddressNumber dns_ip_number
;
41 bool rv
= ParseIPLiteralToNumber(dns_ip_literal
, &dns_ip_number
);
43 return net::IPEndPoint(dns_ip_number
, net::dns_protocol::kDefaultPort
);
46 DnsProbeStatus
EvaluateResults(DnsProbeRunner::Result system_result
,
47 DnsProbeRunner::Result public_result
) {
48 // If the system DNS is working, assume the domain doesn't exist.
49 if (system_result
== DnsProbeRunner::CORRECT
)
50 return chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN
;
52 // If the system DNS is unknown (e.g. on Android), but the public server is
53 // reachable, assume the domain doesn't exist.
54 if (system_result
== DnsProbeRunner::UNKNOWN
&&
55 public_result
== DnsProbeRunner::CORRECT
) {
56 return chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN
;
59 // If the system DNS is not working but another public server is, assume the
60 // DNS config is bad (or perhaps the DNS servers are down or broken).
61 if (public_result
== DnsProbeRunner::CORRECT
)
62 return chrome_common_net::DNS_PROBE_FINISHED_BAD_CONFIG
;
64 // If the system DNS is not working and another public server is unreachable,
65 // assume the internet connection is down (note that system DNS may be a
66 // router on the LAN, so it may be reachable but returning errors.)
67 if (public_result
== DnsProbeRunner::UNREACHABLE
)
68 return chrome_common_net::DNS_PROBE_FINISHED_NO_INTERNET
;
70 // Otherwise: the system DNS is not working and another public server is
71 // responding but with errors or incorrect results. This is an awkward case;
72 // an invasive captive portal or a restrictive firewall may be intercepting
73 // or rewriting DNS traffic, or the public server may itself be failing or
75 return chrome_common_net::DNS_PROBE_FINISHED_INCONCLUSIVE
;
78 void HistogramProbe(DnsProbeStatus status
, base::TimeDelta elapsed
) {
79 DCHECK(chrome_common_net::DnsProbeStatusIsFinished(status
));
81 UMA_HISTOGRAM_ENUMERATION("DnsProbe.ProbeResult", status
,
82 chrome_common_net::DNS_PROBE_MAX
);
83 UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.ProbeDuration", elapsed
);
88 DnsProbeService::DnsProbeService()
89 : state_(STATE_NO_RESULT
) {
90 NetworkChangeNotifier::AddDNSObserver(this);
91 SetSystemClientToCurrentConfig();
92 SetPublicClientToGooglePublicDns();
95 DnsProbeService::~DnsProbeService() {
96 NetworkChangeNotifier::RemoveDNSObserver(this);
99 void DnsProbeService::ProbeDns(const DnsProbeService::ProbeCallback
& callback
) {
100 pending_callbacks_
.push_back(callback
);
102 if (CachedResultIsExpired())
106 case STATE_NO_RESULT
:
109 case STATE_RESULT_CACHED
:
112 case STATE_PROBE_RUNNING
:
113 // Do nothing; probe is already running, and will call the callback.
118 void DnsProbeService::OnDNSChanged() {
120 SetSystemClientToCurrentConfig();
123 void DnsProbeService::SetSystemClientForTesting(
124 scoped_ptr
<DnsClient
> system_client
) {
125 system_runner_
.SetClient(system_client
.Pass());
128 void DnsProbeService::SetPublicClientForTesting(
129 scoped_ptr
<DnsClient
> public_client
) {
130 public_runner_
.SetClient(public_client
.Pass());
133 void DnsProbeService::ClearCachedResultForTesting() {
137 void DnsProbeService::SetSystemClientToCurrentConfig() {
138 DnsConfig system_config
;
139 NetworkChangeNotifier::GetDnsConfig(&system_config
);
140 system_config
.search
.clear();
141 system_config
.attempts
= 1;
142 system_config
.randomize_ports
= false;
144 scoped_ptr
<DnsClient
> system_client(DnsClient::CreateClient(NULL
));
145 system_client
->SetConfig(system_config
);
147 system_runner_
.SetClient(system_client
.Pass());
150 void DnsProbeService::SetPublicClientToGooglePublicDns() {
151 DnsConfig public_config
;
152 public_config
.nameservers
.push_back(MakeDnsEndPoint(kGooglePublicDns1
));
153 public_config
.nameservers
.push_back(MakeDnsEndPoint(kGooglePublicDns2
));
154 public_config
.attempts
= 1;
155 public_config
.randomize_ports
= false;
157 scoped_ptr
<DnsClient
> public_client(DnsClient::CreateClient(NULL
));
158 public_client
->SetConfig(public_config
);
160 public_runner_
.SetClient(public_client
.Pass());
163 void DnsProbeService::StartProbes() {
164 DCHECK_EQ(STATE_NO_RESULT
, state_
);
166 DCHECK(!system_runner_
.IsRunning());
167 DCHECK(!public_runner_
.IsRunning());
169 const base::Closure callback
= base::Bind(&DnsProbeService::OnProbeComplete
,
170 base::Unretained(this));
171 system_runner_
.RunProbe(callback
);
172 public_runner_
.RunProbe(callback
);
173 probe_start_time_
= base::Time::Now();
174 state_
= STATE_PROBE_RUNNING
;
176 DCHECK(system_runner_
.IsRunning());
177 DCHECK(public_runner_
.IsRunning());
180 void DnsProbeService::OnProbeComplete() {
181 DCHECK_EQ(STATE_PROBE_RUNNING
, state_
);
183 if (system_runner_
.IsRunning() || public_runner_
.IsRunning())
186 cached_result_
= EvaluateResults(system_runner_
.result(),
187 public_runner_
.result());
188 state_
= STATE_RESULT_CACHED
;
190 HistogramProbe(cached_result_
, base::Time::Now() - probe_start_time_
);
195 void DnsProbeService::CallCallbacks() {
196 DCHECK_EQ(STATE_RESULT_CACHED
, state_
);
197 DCHECK(chrome_common_net::DnsProbeStatusIsFinished(cached_result_
));
198 DCHECK(!pending_callbacks_
.empty());
200 std::vector
<ProbeCallback
> callbacks
;
201 callbacks
.swap(pending_callbacks_
);
203 for (std::vector
<ProbeCallback
>::const_iterator i
= callbacks
.begin();
204 i
!= callbacks
.end(); ++i
) {
205 i
->Run(cached_result_
);
209 void DnsProbeService::ClearCachedResult() {
210 if (state_
== STATE_RESULT_CACHED
) {
211 state_
= STATE_NO_RESULT
;
212 cached_result_
= chrome_common_net::DNS_PROBE_MAX
;
216 bool DnsProbeService::CachedResultIsExpired() const {
217 if (state_
!= STATE_RESULT_CACHED
)
220 const base::TimeDelta kMaxResultAge
=
221 base::TimeDelta::FromMilliseconds(kMaxResultAgeMs
);
222 return base::Time::Now() - probe_start_time_
> kMaxResultAge
;
225 } // namespace chrome_browser_net