blob: aca02f2ac7cbd4f1c2a34cc04379a3529ccf3bc8 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/net/dns_probe_service_factory.h"
#include <stdint.h>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/metrics/histogram_macros.h"
#include "base/sequence_checker.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/time/default_tick_clock.h"
#include "base/time/time.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/net/dns_probe_runner.h"
#include "chrome/browser/net/dns_probe_service.h"
#include "chrome/browser/net/secure_dns_config.h"
#include "chrome/browser/net/stub_resolver_config_reader.h"
#include "chrome/browser/net/system_network_context_manager.h"
#include "chrome/browser/profiles/incognito_helpers.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/network_service_instance.h"
#include "content/public/browser/storage_partition.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "net/base/ip_address.h"
#include "net/base/ip_endpoint.h"
#include "net/dns/public/dns_over_https_server_config.h"
#include "net/dns/public/dns_protocol.h"
#include "services/network/public/mojom/network_service.mojom.h"
namespace chrome_browser_net {
namespace {
// How long the DnsProbeService will cache the probe result for.
// If it's older than this and we get a probe request, the service expires it
// and starts a new probe.
const int kMaxResultAgeMs = 5000;
// The public DNS servers used by the DnsProbeService to verify internet
// connectivity.
const uint8_t kGooglePublicDns1[] = {8, 8, 8, 8};
const uint8_t kGooglePublicDns2[] = {8, 8, 4, 4};
void HistogramProbe(error_page::DnsProbeStatus status,
base::TimeDelta elapsed) {
DCHECK(error_page::DnsProbeStatusIsFinished(status));
UMA_HISTOGRAM_ENUMERATION("DnsProbe.ProbeResult", status,
error_page::DNS_PROBE_MAX);
UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.ProbeDuration2", elapsed);
}
network::mojom::NetworkContext* GetNetworkContextForProfile(
content::BrowserContext* context) {
return content::BrowserContext::GetDefaultStoragePartition(context)
->GetNetworkContext();
}
mojo::Remote<network::mojom::DnsConfigChangeManager>
GetDnsConfigChangeManager() {
mojo::Remote<network::mojom::DnsConfigChangeManager>
dns_config_change_manager_remote;
content::GetNetworkService()->GetDnsConfigChangeManager(
dns_config_change_manager_remote.BindNewPipeAndPassReceiver());
return dns_config_change_manager_remote;
}
net::DnsConfigOverrides GoogleConfigOverrides() {
net::DnsConfigOverrides overrides =
net::DnsConfigOverrides::CreateOverridingEverythingWithDefaults();
overrides.nameservers = std::vector<net::IPEndPoint>{
net::IPEndPoint(net::IPAddress(kGooglePublicDns1),
net::dns_protocol::kDefaultPort),
net::IPEndPoint(net::IPAddress(kGooglePublicDns2),
net::dns_protocol::kDefaultPort)};
overrides.attempts = 1;
overrides.secure_dns_mode = net::DnsConfig::SecureDnsMode::OFF;
return overrides;
}
class DnsProbeServiceImpl
: public DnsProbeService,
public network::mojom::DnsConfigChangeManagerClient {
public:
using NetworkContextGetter = DnsProbeServiceFactory::NetworkContextGetter;
using DnsConfigChangeManagerGetter =
DnsProbeServiceFactory::DnsConfigChangeManagerGetter;
explicit DnsProbeServiceImpl(content::BrowserContext* context);
DnsProbeServiceImpl(
const NetworkContextGetter& network_context_getter,
const DnsConfigChangeManagerGetter& dns_config_change_manager_getter,
const base::TickClock* tick_clock);
~DnsProbeServiceImpl() override;
// DnsProbeService implementation:
void ProbeDns(DnsProbeService::ProbeCallback callback) override;
net::DnsConfigOverrides GetCurrentConfigOverridesForTesting() override;
// mojom::network::DnsConfigChangeManagerClient implementation:
void OnDnsConfigChanged() override;
private:
enum State {
STATE_NO_RESULT,
STATE_PROBE_RUNNING,
STATE_RESULT_CACHED,
};
// Create the current config runner using overrides that will mimic the
// current secure DNS mode and pre-specified DoH servers.
void SetUpCurrentConfigRunner();
// Starts a probe (runs probes for both the current config and google config)
void StartProbes();
void OnProbeComplete();
// Determine what error page should be shown given the results of the two
// probe runners.
error_page::DnsProbeStatus EvaluateResults(
DnsProbeRunner::Result current_config_result,
DnsProbeRunner::Result google_config_result);
// Calls all |pending_callbacks_| with the |cached_result_|.
void CallCallbacks();
// Calls callback by posting a task to the same sequence. |pending_callbacks_|
// must have exactly one element.
void CallCallbackAsynchronously();
// Clears a cached probe result.
void ClearCachedResult();
bool CachedResultIsExpired() const;
void SetupDnsConfigChangeNotifications();
void OnDnsConfigChangeManagerConnectionError();
State state_;
std::vector<DnsProbeService::ProbeCallback> pending_callbacks_;
base::TimeTicks probe_start_time_;
error_page::DnsProbeStatus cached_result_;
NetworkContextGetter network_context_getter_;
DnsConfigChangeManagerGetter dns_config_change_manager_getter_;
mojo::Receiver<network::mojom::DnsConfigChangeManagerClient> receiver_{this};
net::DnsConfig::SecureDnsMode current_config_secure_dns_mode_ =
net::DnsConfig::SecureDnsMode::OFF;
// DnsProbeRunners for the current DNS configuration and a Google DNS
// configuration. Both runners will have the insecure async resolver enabled
// regardless of the platform. This is out of necessity for the Google config
// runner, where the nameservers are being changed. For the current config
// runner, the insecure async resolver will only be used when the runner is
// not operating in SECURE mode.
std::unique_ptr<DnsProbeRunner> current_config_runner_;
std::unique_ptr<DnsProbeRunner> google_config_runner_;
// Time source for cache expiry.
const base::TickClock* tick_clock_; // Not owned.
SEQUENCE_CHECKER(sequence_checker_);
DISALLOW_COPY_AND_ASSIGN(DnsProbeServiceImpl);
};
DnsProbeServiceImpl::DnsProbeServiceImpl(content::BrowserContext* context)
: DnsProbeServiceImpl(
base::BindRepeating(&GetNetworkContextForProfile, context),
base::BindRepeating(&GetDnsConfigChangeManager),
base::DefaultTickClock::GetInstance()) {}
DnsProbeServiceImpl::DnsProbeServiceImpl(
const NetworkContextGetter& network_context_getter,
const DnsConfigChangeManagerGetter& dns_config_change_manager_getter,
const base::TickClock* tick_clock)
: state_(STATE_NO_RESULT),
network_context_getter_(network_context_getter),
dns_config_change_manager_getter_(dns_config_change_manager_getter),
tick_clock_(tick_clock) {
SetupDnsConfigChangeNotifications();
google_config_runner_ = std::make_unique<DnsProbeRunner>(
GoogleConfigOverrides(), network_context_getter_);
SetUpCurrentConfigRunner();
}
DnsProbeServiceImpl::~DnsProbeServiceImpl() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void DnsProbeServiceImpl::ProbeDns(ProbeCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
pending_callbacks_.push_back(std::move(callback));
if (CachedResultIsExpired())
ClearCachedResult();
switch (state_) {
case STATE_NO_RESULT:
StartProbes();
break;
case STATE_RESULT_CACHED:
CallCallbackAsynchronously();
break;
case STATE_PROBE_RUNNING:
// Do nothing; probe is already running, and will call the callback.
break;
}
}
net::DnsConfigOverrides
DnsProbeServiceImpl::GetCurrentConfigOverridesForTesting() {
DCHECK(current_config_runner_);
return current_config_runner_->GetConfigOverridesForTesting();
}
void DnsProbeServiceImpl::OnDnsConfigChanged() {
SetUpCurrentConfigRunner();
ClearCachedResult();
}
void DnsProbeServiceImpl::SetUpCurrentConfigRunner() {
SecureDnsConfig secure_dns_config =
SystemNetworkContextManager::GetStubResolverConfigReader()
->GetSecureDnsConfiguration(
false /* force_check_parental_controls_for_automatic_mode */);
current_config_secure_dns_mode_ = secure_dns_config.mode();
net::DnsConfigOverrides current_config_overrides;
current_config_overrides.search = std::vector<std::string>();
current_config_overrides.attempts = 1;
if (current_config_secure_dns_mode_ ==
net::DnsConfig::SecureDnsMode::SECURE) {
if (!secure_dns_config.servers().empty()) {
current_config_overrides.dns_over_https_servers.emplace(
secure_dns_config.servers());
}
current_config_overrides.secure_dns_mode =
net::DnsConfig::SecureDnsMode::SECURE;
} else {
// A DNS error that occurred in automatic mode must have had an insecure
// DNS failure. For efficiency, probe queries in this case can just be
// issued in OFF mode.
current_config_overrides.secure_dns_mode =
net::DnsConfig::SecureDnsMode::OFF;
}
current_config_runner_ = std::make_unique<DnsProbeRunner>(
current_config_overrides, network_context_getter_);
}
void DnsProbeServiceImpl::StartProbes() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(STATE_NO_RESULT, state_);
DCHECK(!current_config_runner_->IsRunning());
DCHECK(!google_config_runner_->IsRunning());
// Unretained safe because the callback will not be run if the DnsProbeRunner
// is destroyed.
current_config_runner_->RunProbe(base::BindOnce(
&DnsProbeServiceImpl::OnProbeComplete, base::Unretained(this)));
google_config_runner_->RunProbe(base::BindOnce(
&DnsProbeServiceImpl::OnProbeComplete, base::Unretained(this)));
probe_start_time_ = tick_clock_->NowTicks();
state_ = STATE_PROBE_RUNNING;
DCHECK(current_config_runner_->IsRunning());
DCHECK(google_config_runner_->IsRunning());
}
void DnsProbeServiceImpl::OnProbeComplete() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(STATE_PROBE_RUNNING, state_);
DCHECK(!current_config_runner_->IsRunning() ||
!google_config_runner_->IsRunning());
if (current_config_runner_->IsRunning() || google_config_runner_->IsRunning())
return;
cached_result_ = EvaluateResults(current_config_runner_->result(),
google_config_runner_->result());
state_ = STATE_RESULT_CACHED;
HistogramProbe(cached_result_, tick_clock_->NowTicks() - probe_start_time_);
CallCallbacks();
}
error_page::DnsProbeStatus DnsProbeServiceImpl::EvaluateResults(
DnsProbeRunner::Result current_config_result,
DnsProbeRunner::Result google_config_result) {
// If the current DNS config is working, assume the domain doesn't exist.
if (current_config_result == DnsProbeRunner::CORRECT)
return error_page::DNS_PROBE_FINISHED_NXDOMAIN;
// If the current DNS config is unknown (e.g. on Android), but Google DNS is
// reachable, assume the domain doesn't exist.
if (current_config_result == DnsProbeRunner::UNKNOWN &&
google_config_result == DnsProbeRunner::CORRECT) {
return error_page::DNS_PROBE_FINISHED_NXDOMAIN;
}
// If the current DNS config is not working but Google DNS is, assume the DNS
// config is bad (or perhaps the DNS servers are down or broken). If the
// current DNS config is in secure mode, return an error indicating that this
// is a secure DNS config issue.
if (google_config_result == DnsProbeRunner::CORRECT) {
return (current_config_secure_dns_mode_ ==
net::DnsConfig::SecureDnsMode::SECURE)
? error_page::DNS_PROBE_FINISHED_BAD_SECURE_CONFIG
: error_page::DNS_PROBE_FINISHED_BAD_CONFIG;
}
// If the current DNS config is not working and Google DNS is unreachable,
// assume the internet connection is down (note that current DNS may be a
// router on the LAN, so it may be reachable but returning errors.)
if (google_config_result == DnsProbeRunner::UNREACHABLE)
return error_page::DNS_PROBE_FINISHED_NO_INTERNET;
// Otherwise: the current DNS config is not working and Google DNS is
// responding but with errors or incorrect results. This is an awkward case;
// an invasive captive portal or a restrictive firewall may be intercepting
// or rewriting DNS traffic, or the public server may itself be failing or
// down.
return error_page::DNS_PROBE_FINISHED_INCONCLUSIVE;
}
void DnsProbeServiceImpl::CallCallbacks() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(STATE_RESULT_CACHED, state_);
DCHECK(error_page::DnsProbeStatusIsFinished(cached_result_));
DCHECK(!pending_callbacks_.empty());
std::vector<ProbeCallback> callbacks;
callbacks.swap(pending_callbacks_);
for (std::vector<ProbeCallback>::iterator i = callbacks.begin();
i != callbacks.end(); ++i) {
std::move(*i).Run(cached_result_);
}
}
void DnsProbeServiceImpl::CallCallbackAsynchronously() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(STATE_RESULT_CACHED, state_);
DCHECK(error_page::DnsProbeStatusIsFinished(cached_result_));
DCHECK_EQ(1U, pending_callbacks_.size());
std::vector<ProbeCallback> callbacks;
callbacks.swap(pending_callbacks_);
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callbacks.front()), cached_result_));
}
void DnsProbeServiceImpl::ClearCachedResult() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (state_ == STATE_RESULT_CACHED) {
state_ = STATE_NO_RESULT;
cached_result_ = error_page::DNS_PROBE_MAX;
}
}
bool DnsProbeServiceImpl::CachedResultIsExpired() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (state_ != STATE_RESULT_CACHED)
return false;
const base::TimeDelta kMaxResultAge =
base::TimeDelta::FromMilliseconds(kMaxResultAgeMs);
return tick_clock_->NowTicks() - probe_start_time_ > kMaxResultAge;
}
void DnsProbeServiceImpl::SetupDnsConfigChangeNotifications() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
dns_config_change_manager_getter_.Run()->RequestNotifications(
receiver_.BindNewPipeAndPassRemote());
receiver_.set_disconnect_handler(base::BindOnce(
&DnsProbeServiceImpl::OnDnsConfigChangeManagerConnectionError,
base::Unretained(this)));
}
void DnsProbeServiceImpl::OnDnsConfigChangeManagerConnectionError() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Clear the cache, since the configuration may have changed while not
// getting notifications.
ClearCachedResult();
receiver_.reset();
SetupDnsConfigChangeNotifications();
}
} // namespace
DnsProbeService* DnsProbeServiceFactory::GetForContext(
content::BrowserContext* browser_context) {
return static_cast<DnsProbeService*>(
GetInstance()->GetServiceForBrowserContext(browser_context,
true /* create */));
}
DnsProbeServiceFactory* DnsProbeServiceFactory::GetInstance() {
return base::Singleton<DnsProbeServiceFactory>::get();
}
DnsProbeServiceFactory::DnsProbeServiceFactory()
: BrowserContextKeyedServiceFactory(
"DnsProbeService",
BrowserContextDependencyManager::GetInstance()) {}
DnsProbeServiceFactory::~DnsProbeServiceFactory() {}
KeyedService* DnsProbeServiceFactory::BuildServiceInstanceFor(
content::BrowserContext* context) const {
return new DnsProbeServiceImpl(context);
}
content::BrowserContext* DnsProbeServiceFactory::GetBrowserContextToUse(
content::BrowserContext* context) const {
// Create separate service for incognito profiles.
return chrome::GetBrowserContextOwnInstanceInIncognito(context);
}
// static
std::unique_ptr<DnsProbeService> DnsProbeServiceFactory::CreateForTesting(
const NetworkContextGetter& network_context_getter,
const DnsConfigChangeManagerGetter& dns_config_change_manager_getter,
const base::TickClock* tick_clock) {
return std::make_unique<DnsProbeServiceImpl>(
network_context_getter, dns_config_change_manager_getter, tick_clock);
}
} // namespace chrome_browser_net