blob: b423a46b14e7f4ddbf148cbd5ff37418b9900f54 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/webui/settings/settings_secure_dns_handler.h"
#include <memory>
#include <optional>
#include <utility>
#include "base/check.h"
#include "base/functional/bind.h"
#include "base/rand_util.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/net/secure_dns_config.h"
#include "chrome/browser/net/secure_dns_util.h"
#include "chrome/browser/net/stub_resolver_config_reader.h"
#include "chrome/browser/net/system_network_context_manager.h"
#include "chrome/common/pref_names.h"
#include "chrome/grit/generated_resources.h"
#include "components/country_codes/country_codes.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "net/dns/public/dns_over_https_config.h"
#include "net/dns/public/doh_provider_entry.h"
#include "net/dns/public/secure_dns_mode.h"
#include "net/dns/public/util.h"
#include "ui/base/l10n/l10n_util.h"
namespace secure_dns = chrome_browser_net::secure_dns;
namespace settings {
namespace {
base::Value::Dict CreateSecureDnsSettingDict() {
// Fetch the current host resolver configuration. It is not sufficient to read
// the secure DNS prefs directly since the host resolver configuration takes
// other factors into account such as whether a managed environment or
// parental controls have been detected.
SecureDnsConfig config =
SystemNetworkContextManager::GetStubResolverConfigReader()
->GetSecureDnsConfiguration(
true /* force_check_parental_controls_for_automatic_mode */);
base::Value::Dict dict;
dict.Set("mode", SecureDnsConfig::ModeToString(config.mode()));
dict.Set("config", config.doh_servers().ToString());
#if BUILDFLAG(IS_CHROMEOS_ASH)
std::optional<std::string> doh_with_identifiers_servers_for_display =
SystemNetworkContextManager::GetStubResolverConfigReader()
->GetDohWithIdentifiersDisplayServers();
dict.Set("dohWithIdentifiersActive",
doh_with_identifiers_servers_for_display.has_value());
dict.Set("configForDisplay",
doh_with_identifiers_servers_for_display.value_or(std::string()));
#endif
dict.Set("managementMode", static_cast<int>(config.management_mode()));
return dict;
}
} // namespace
SecureDnsHandler::SecureDnsHandler() = default;
SecureDnsHandler::~SecureDnsHandler() = default;
void SecureDnsHandler::RegisterMessages() {
web_ui()->RegisterMessageCallback(
"getSecureDnsResolverList",
base::BindRepeating(&SecureDnsHandler::HandleGetSecureDnsResolverList,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"getSecureDnsSetting",
base::BindRepeating(&SecureDnsHandler::HandleGetSecureDnsSetting,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"isValidConfig",
base::BindRepeating(&SecureDnsHandler::HandleIsValidConfig,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"probeConfig", base::BindRepeating(&SecureDnsHandler::HandleProbeConfig,
base::Unretained(this)));
}
void SecureDnsHandler::OnJavascriptAllowed() {
// Register for updates to the underlying secure DNS prefs so that the
// secure DNS setting can be updated to reflect the current host resolver
// configuration.
pref_registrar_.Init(g_browser_process->local_state());
pref_registrar_.Add(
prefs::kDnsOverHttpsMode,
base::BindRepeating(
&SecureDnsHandler::SendSecureDnsSettingUpdatesToJavascript,
base::Unretained(this)));
pref_registrar_.Add(
prefs::kDnsOverHttpsTemplates,
base::BindRepeating(
&SecureDnsHandler::SendSecureDnsSettingUpdatesToJavascript,
base::Unretained(this)));
#if BUILDFLAG(IS_CHROMEOS)
pref_registrar_.Add(
prefs::kDnsOverHttpsEffectiveTemplatesChromeOS,
base::BindRepeating(
&SecureDnsHandler::SendSecureDnsSettingUpdatesToJavascript,
base::Unretained(this)));
#endif
}
void SecureDnsHandler::OnJavascriptDisallowed() {
pref_registrar_.RemoveAll();
}
base::Value::List SecureDnsHandler::GetSecureDnsResolverList() {
base::Value::List resolvers;
for (const net::DohProviderEntry* entry : providers_) {
net::DnsOverHttpsConfig doh_config({entry->doh_server_config});
base::Value::Dict dict;
dict.Set("name", entry->ui_name);
dict.Set("value", doh_config.ToString());
dict.Set("policy", entry->privacy_policy);
resolvers.Append(std::move(dict));
}
base::RandomShuffle(resolvers.begin(), resolvers.end());
return resolvers;
}
void SecureDnsHandler::SetNetworkContextForTesting(
network::mojom::NetworkContext* network_context) {
network_context_getter_ = base::BindRepeating(
[](network::mojom::NetworkContext* network_context) {
return network_context;
},
network_context);
}
network::mojom::NetworkContext* SecureDnsHandler::GetNetworkContext() {
return web_ui()
->GetWebContents()
->GetBrowserContext()
->GetDefaultStoragePartition()
->GetNetworkContext();
}
void SecureDnsHandler::SetProvidersForTesting(
net::DohProviderEntry::List providers) {
providers_ = std::move(providers);
}
void SecureDnsHandler::HandleGetSecureDnsResolverList(
const base::Value::List& args) {
AllowJavascript();
std::string callback_id = args[0].GetString();
ResolveJavascriptCallback(base::Value(callback_id),
GetSecureDnsResolverList());
}
void SecureDnsHandler::HandleGetSecureDnsSetting(
const base::Value::List& args) {
AllowJavascript();
CHECK_EQ(1u, args.size());
const base::Value& callback_id = args[0];
ResolveJavascriptCallback(callback_id, CreateSecureDnsSettingDict());
}
void SecureDnsHandler::HandleIsValidConfig(const base::Value::List& args) {
AllowJavascript();
const base::Value& callback_id = args[0];
const std::string& custom_entry = args[1].GetString();
bool valid = net::DnsOverHttpsConfig::FromString(custom_entry).has_value();
secure_dns::UpdateValidationHistogram(valid);
ResolveJavascriptCallback(callback_id, base::Value(valid));
}
void SecureDnsHandler::HandleProbeConfig(const base::Value::List& args) {
AllowJavascript();
if (!probe_callback_id_.empty()) {
// Cancel the pending probe by destroying the probe runner (which does not
// call the callback), and report a non-error response to avoid leaking the
// callback.
runner_.reset();
ResolveJavascriptCallback(base::Value(probe_callback_id_),
base::Value(true));
}
probe_callback_id_ = args[0].GetString();
const std::string& doh_config = args[1].GetString();
DCHECK(!runner_);
std::optional<net::DnsOverHttpsConfig> parsed =
net::DnsOverHttpsConfig::FromString(doh_config);
DCHECK(parsed.has_value()); // `doh_config` must be valid.
runner_ =
secure_dns::MakeProbeRunner(std::move(*parsed), network_context_getter_);
runner_->RunProbe(base::BindOnce(&SecureDnsHandler::OnProbeComplete,
base::Unretained(this)));
}
void SecureDnsHandler::OnProbeComplete() {
bool success =
runner_->result() == chrome_browser_net::DnsProbeRunner::CORRECT;
runner_.reset();
secure_dns::UpdateProbeHistogram(success);
ResolveJavascriptCallback(base::Value(probe_callback_id_),
base::Value(success));
probe_callback_id_.clear();
}
void SecureDnsHandler::SendSecureDnsSettingUpdatesToJavascript() {
FireWebUIListener("secure-dns-setting-changed", CreateSecureDnsSettingDict());
}
// static
net::DohProviderEntry::List SecureDnsHandler::GetFilteredProviders() {
// Note: Check whether each provider is enabled *after* filtering based on
// country code so that if we are doing experimentation via Finch for a
// regional provider, the experiment groups will be less likely to include
// users from other regions unnecessarily (since a client will be included in
// the experiment if the provider feature flag is checked).
return secure_dns::SelectEnabledProviders(secure_dns::ProvidersForCountry(
net::DohProviderEntry::GetList(), country_codes::GetCurrentCountryID()));
}
} // namespace settings