blob: 299fdea9e04822d8a2f6fcd595d73c8031d48832 [file] [log] [blame]
// Copyright 2020 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/ui/webui/settings/settings_secure_dns_handler.h"
#include <memory>
#include <utility>
#include "base/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/chrome_features.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_server_config.h"
#include "net/dns/public/doh_provider_entry.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 {
std::unique_ptr<base::DictionaryValue> 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 */);
auto secure_dns_templates = std::make_unique<base::ListValue>();
for (const auto& doh_server : config.servers()) {
secure_dns_templates->Append(doh_server.server_template);
}
auto dict = std::make_unique<base::DictionaryValue>();
dict->SetString("mode", SecureDnsConfig::ModeToString(config.mode()));
dict->SetList("templates", std::move(secure_dns_templates));
dict->SetInteger("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(
"parseCustomDnsEntry",
base::BindRepeating(&SecureDnsHandler::HandleParseCustomDnsEntry,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"probeCustomDnsTemplate",
base::BindRepeating(&SecureDnsHandler::HandleProbeCustomDnsTemplate,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"recordUserDropdownInteraction",
base::BindRepeating(
&SecureDnsHandler::HandleRecordUserDropdownInteraction,
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::Bind(&SecureDnsHandler::SendSecureDnsSettingUpdatesToJavascript,
base::Unretained(this)));
pref_registrar_.Add(
prefs::kDnsOverHttpsTemplates,
base::Bind(&SecureDnsHandler::SendSecureDnsSettingUpdatesToJavascript,
base::Unretained(this)));
}
void SecureDnsHandler::OnJavascriptDisallowed() {
pref_registrar_.RemoveAll();
}
base::Value SecureDnsHandler::GetSecureDnsResolverList() {
base::Value resolvers(base::Value::Type::LIST);
for (const auto* entry : providers_) {
base::Value dict(base::Value::Type::DICTIONARY);
dict.SetStringKey("name", entry->ui_name);
dict.SetStringKey("value", entry->dns_over_https_template);
dict.SetStringKey("policy", entry->privacy_policy);
resolvers.Append(std::move(dict));
}
// Randomize the order of the resolvers.
base::RandomShuffle(resolvers.GetList().begin(), resolvers.GetList().end());
// Add a custom option to the front of the list
base::Value custom(base::Value::Type::DICTIONARY);
custom.SetStringKey("name", l10n_util::GetStringUTF8(IDS_SETTINGS_CUSTOM));
custom.SetStringKey("value", std::string()); // Empty value means custom.
custom.SetStringKey("policy", std::string());
resolvers.Insert(resolvers.GetList().begin(), std::move(custom));
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 content::BrowserContext::GetDefaultStoragePartition(
web_ui()->GetWebContents()->GetBrowserContext())
->GetNetworkContext();
}
void SecureDnsHandler::SetProvidersForTesting(
net::DohProviderEntry::List providers) {
providers_ = std::move(providers);
}
void SecureDnsHandler::HandleGetSecureDnsResolverList(
const base::ListValue* args) {
AllowJavascript();
std::string callback_id = args->GetList()[0].GetString();
ResolveJavascriptCallback(base::Value(callback_id),
GetSecureDnsResolverList());
}
void SecureDnsHandler::HandleGetSecureDnsSetting(const base::ListValue* args) {
AllowJavascript();
CHECK_EQ(1u, args->GetList().size());
const base::Value& callback_id = args->GetList()[0];
ResolveJavascriptCallback(callback_id, *CreateSecureDnsSettingDict());
}
void SecureDnsHandler::HandleParseCustomDnsEntry(const base::ListValue* args) {
AllowJavascript();
const base::Value* callback_id;
std::string custom_entry;
CHECK(args->Get(0, &callback_id));
CHECK(args->GetString(1, &custom_entry));
// Return all templates in the entry, or none if they are not all valid.
base::Value templates(base::Value::Type::LIST);
if (secure_dns::IsValidGroup(custom_entry)) {
for (base::StringPiece t : secure_dns::SplitGroup(custom_entry)) {
templates.Append(t);
}
}
secure_dns::UpdateValidationHistogram(!templates.GetList().empty());
ResolveJavascriptCallback(*callback_id, templates);
}
void SecureDnsHandler::HandleProbeCustomDnsTemplate(
const base::ListValue* 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));
}
std::string server_template;
CHECK(args->GetString(0, &probe_callback_id_));
CHECK(args->GetString(1, &server_template));
net::DnsConfigOverrides overrides;
overrides.search = std::vector<std::string>();
overrides.attempts = 1;
overrides.secure_dns_mode = net::DnsConfig::SecureDnsMode::SECURE;
secure_dns::ApplyTemplate(&overrides, server_template);
DCHECK(!runner_);
runner_ = std::make_unique<chrome_browser_net::DnsProbeRunner>(
overrides, network_context_getter_);
runner_->RunProbe(base::BindOnce(&SecureDnsHandler::OnProbeComplete,
base::Unretained(this)));
}
void SecureDnsHandler::HandleRecordUserDropdownInteraction(
const base::ListValue* args) {
CHECK_EQ(2U, args->GetSize());
std::string old_provider;
std::string new_provider;
CHECK(args->GetString(0, &old_provider));
CHECK(args->GetString(1, &new_provider));
secure_dns::UpdateDropdownHistograms(providers_, old_provider, new_provider);
}
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() {
const auto local_providers = secure_dns::ProvidersForCountry(
net::DohProviderEntry::GetList(), country_codes::GetCurrentCountryID());
return secure_dns::RemoveDisabledProviders(
local_providers, secure_dns::GetDisabledProviders());
}
} // namespace settings