1 // Copyright 2012 The Chromium Authors
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 "net/dns/dns_client.h"
6
7 #include <memory>
8 #include <string>
9 #include <utility>
10
11 #include "base/functional/bind.h"
12 #include "base/memory/raw_ptr.h"
13 #include "base/metrics/histogram_macros.h"
14 #include "base/rand_util.h"
15 #include "base/ranges/algorithm.h"
16 #include "base/values.h"
17 #include "net/base/ip_address.h"
18 #include "net/base/ip_endpoint.h"
19 #include "net/dns/address_sorter.h"
20 #include "net/dns/dns_session.h"
21 #include "net/dns/dns_transaction.h"
22 #include "net/dns/dns_util.h"
23 #include "net/dns/public/dns_over_https_config.h"
24 #include "net/dns/public/secure_dns_mode.h"
25 #include "net/dns/resolve_context.h"
26 #include "net/log/net_log.h"
27 #include "net/log/net_log_event_type.h"
28 #include "net/socket/client_socket_factory.h"
29 #include "net/third_party/uri_template/uri_template.h"
30 #include "url/gurl.h"
31 #include "url/scheme_host_port.h"
32
33 namespace net {
34
35 namespace {
36
IsEqual(const absl::optional<DnsConfig> & c1,const DnsConfig * c2)37 bool IsEqual(const absl::optional<DnsConfig>& c1, const DnsConfig* c2) {
38 if (!c1.has_value() && c2 == nullptr)
39 return true;
40
41 if (!c1.has_value() || c2 == nullptr)
42 return false;
43
44 return c1.value() == *c2;
45 }
46
UpdateConfigForDohUpgrade(DnsConfig * config)47 void UpdateConfigForDohUpgrade(DnsConfig* config) {
48 bool has_doh_servers = !config->doh_config.servers().empty();
49 // Do not attempt upgrade when there are already DoH servers specified or
50 // when there are aspects of the system DNS config that are unhandled.
51 if (!config->unhandled_options && config->allow_dns_over_https_upgrade &&
52 !has_doh_servers &&
53 config->secure_dns_mode == SecureDnsMode::kAutomatic) {
54 // If we're in strict mode on Android, only attempt to upgrade the
55 // specified DoT hostname.
56 if (!config->dns_over_tls_hostname.empty()) {
57 config->doh_config = DnsOverHttpsConfig(
58 GetDohUpgradeServersFromDotHostname(config->dns_over_tls_hostname));
59 has_doh_servers = !config->doh_config.servers().empty();
60 UMA_HISTOGRAM_BOOLEAN("Net.DNS.UpgradeConfig.DotUpgradeSucceeded",
61 has_doh_servers);
62 } else {
63 bool all_local = true;
64 for (const auto& server : config->nameservers) {
65 if (server.address().IsPubliclyRoutable()) {
66 all_local = false;
67 break;
68 }
69 }
70 UMA_HISTOGRAM_BOOLEAN("Net.DNS.UpgradeConfig.HasPublicInsecureNameserver",
71 !all_local);
72
73 config->doh_config = DnsOverHttpsConfig(
74 GetDohUpgradeServersFromNameservers(config->nameservers));
75 has_doh_servers = !config->doh_config.servers().empty();
76 UMA_HISTOGRAM_BOOLEAN("Net.DNS.UpgradeConfig.InsecureUpgradeSucceeded",
77 has_doh_servers);
78 }
79 } else {
80 UMA_HISTOGRAM_BOOLEAN("Net.DNS.UpgradeConfig.Ineligible.DohSpecified",
81 has_doh_servers);
82 UMA_HISTOGRAM_BOOLEAN("Net.DNS.UpgradeConfig.Ineligible.UnhandledOptions",
83 config->unhandled_options);
84 }
85 }
86
87 class DnsClientImpl : public DnsClient {
88 public:
DnsClientImpl(NetLog * net_log,const RandIntCallback & rand_int_callback)89 DnsClientImpl(NetLog* net_log, const RandIntCallback& rand_int_callback)
90 : net_log_(net_log), rand_int_callback_(rand_int_callback) {}
91
92 DnsClientImpl(const DnsClientImpl&) = delete;
93 DnsClientImpl& operator=(const DnsClientImpl&) = delete;
94
95 ~DnsClientImpl() override = default;
96
CanUseSecureDnsTransactions() const97 bool CanUseSecureDnsTransactions() const override {
98 const DnsConfig* config = GetEffectiveConfig();
99 return config && !config->doh_config.servers().empty();
100 }
101
CanUseInsecureDnsTransactions() const102 bool CanUseInsecureDnsTransactions() const override {
103 const DnsConfig* config = GetEffectiveConfig();
104 return config && config->nameservers.size() > 0 && insecure_enabled_ &&
105 !config->unhandled_options && !config->dns_over_tls_active;
106 }
107
CanQueryAdditionalTypesViaInsecureDns() const108 bool CanQueryAdditionalTypesViaInsecureDns() const override {
109 // Only useful information if insecure DNS is usable, so expect this to
110 // never be called if that is not the case.
111 DCHECK(CanUseInsecureDnsTransactions());
112
113 return can_query_additional_types_via_insecure_;
114 }
115
SetInsecureEnabled(bool enabled,bool additional_types_enabled)116 void SetInsecureEnabled(bool enabled,
117 bool additional_types_enabled) override {
118 insecure_enabled_ = enabled;
119 can_query_additional_types_via_insecure_ = additional_types_enabled;
120 }
121
FallbackFromSecureTransactionPreferred(ResolveContext * context) const122 bool FallbackFromSecureTransactionPreferred(
123 ResolveContext* context) const override {
124 if (!CanUseSecureDnsTransactions())
125 return true;
126
127 DCHECK(session_); // Should be true if CanUseSecureDnsTransactions() true.
128 return context->NumAvailableDohServers(session_.get()) == 0;
129 }
130
FallbackFromInsecureTransactionPreferred() const131 bool FallbackFromInsecureTransactionPreferred() const override {
132 return !CanUseInsecureDnsTransactions() ||
133 insecure_fallback_failures_ >= kMaxInsecureFallbackFailures;
134 }
135
SetSystemConfig(absl::optional<DnsConfig> system_config)136 bool SetSystemConfig(absl::optional<DnsConfig> system_config) override {
137 if (system_config == system_config_)
138 return false;
139
140 system_config_ = std::move(system_config);
141
142 return UpdateDnsConfig();
143 }
144
SetConfigOverrides(DnsConfigOverrides config_overrides)145 bool SetConfigOverrides(DnsConfigOverrides config_overrides) override {
146 if (config_overrides == config_overrides_)
147 return false;
148
149 config_overrides_ = std::move(config_overrides);
150
151 return UpdateDnsConfig();
152 }
153
ReplaceCurrentSession()154 void ReplaceCurrentSession() override {
155 if (!session_)
156 return;
157
158 UpdateSession(session_->config());
159 }
160
GetCurrentSession()161 DnsSession* GetCurrentSession() override { return session_.get(); }
162
GetEffectiveConfig() const163 const DnsConfig* GetEffectiveConfig() const override {
164 if (!session_)
165 return nullptr;
166
167 DCHECK(session_->config().IsValid());
168 return &session_->config();
169 }
170
GetHosts() const171 const DnsHosts* GetHosts() const override {
172 const DnsConfig* config = GetEffectiveConfig();
173 if (!config)
174 return nullptr;
175
176 return &config->hosts;
177 }
178
GetPresetAddrs(const url::SchemeHostPort & endpoint) const179 absl::optional<std::vector<IPEndPoint>> GetPresetAddrs(
180 const url::SchemeHostPort& endpoint) const override {
181 DCHECK(endpoint.IsValid());
182 if (!session_)
183 return absl::nullopt;
184 const auto& servers = session_->config().doh_config.servers();
185 auto it = base::ranges::find_if(servers, [&](const auto& server) {
186 std::string uri;
187 bool valid = uri_template::Expand(server.server_template(), {}, &uri);
188 // Server templates are validated before being allowed into the config.
189 DCHECK(valid);
190 GURL gurl(uri);
191 return url::SchemeHostPort(gurl) == endpoint;
192 });
193 if (it == servers.end())
194 return absl::nullopt;
195 std::vector<IPEndPoint> combined;
196 for (const IPAddressList& ips : it->endpoints()) {
197 for (const IPAddress& ip : ips) {
198 combined.emplace_back(ip, endpoint.port());
199 }
200 }
201 return combined;
202 }
203
GetTransactionFactory()204 DnsTransactionFactory* GetTransactionFactory() override {
205 return session_.get() ? factory_.get() : nullptr;
206 }
207
GetAddressSorter()208 AddressSorter* GetAddressSorter() override { return address_sorter_.get(); }
209
IncrementInsecureFallbackFailures()210 void IncrementInsecureFallbackFailures() override {
211 ++insecure_fallback_failures_;
212 }
213
ClearInsecureFallbackFailures()214 void ClearInsecureFallbackFailures() override {
215 insecure_fallback_failures_ = 0;
216 }
217
GetDnsConfigAsValueForNetLog() const218 base::Value::Dict GetDnsConfigAsValueForNetLog() const override {
219 const DnsConfig* config = GetEffectiveConfig();
220 if (config == nullptr)
221 return base::Value::Dict();
222 base::Value::Dict dict = config->ToDict();
223 dict.Set("can_use_secure_dns_transactions", CanUseSecureDnsTransactions());
224 dict.Set("can_use_insecure_dns_transactions",
225 CanUseInsecureDnsTransactions());
226 return dict;
227 }
228
GetSystemConfigForTesting() const229 absl::optional<DnsConfig> GetSystemConfigForTesting() const override {
230 return system_config_;
231 }
232
GetConfigOverridesForTesting() const233 DnsConfigOverrides GetConfigOverridesForTesting() const override {
234 return config_overrides_;
235 }
236
SetTransactionFactoryForTesting(std::unique_ptr<DnsTransactionFactory> factory)237 void SetTransactionFactoryForTesting(
238 std::unique_ptr<DnsTransactionFactory> factory) override {
239 factory_ = std::move(factory);
240 }
241
242 private:
BuildEffectiveConfig() const243 absl::optional<DnsConfig> BuildEffectiveConfig() const {
244 DnsConfig config;
245 if (config_overrides_.OverridesEverything()) {
246 config = config_overrides_.ApplyOverrides(DnsConfig());
247 } else {
248 if (!system_config_)
249 return absl::nullopt;
250
251 config = config_overrides_.ApplyOverrides(system_config_.value());
252 }
253
254 UpdateConfigForDohUpgrade(&config);
255
256 // TODO(ericorth): Consider keeping a separate DnsConfig for pure Chrome-
257 // produced configs to allow respecting all fields like |unhandled_options|
258 // while still being able to fallback to system config for DoH.
259 // For now, clear the nameservers for extra security if parts of the system
260 // config are unhandled.
261 if (config.unhandled_options)
262 config.nameservers.clear();
263
264 if (!config.IsValid())
265 return absl::nullopt;
266
267 return config;
268 }
269
UpdateDnsConfig()270 bool UpdateDnsConfig() {
271 absl::optional<DnsConfig> new_effective_config = BuildEffectiveConfig();
272
273 if (IsEqual(new_effective_config, GetEffectiveConfig()))
274 return false;
275
276 insecure_fallback_failures_ = 0;
277 UpdateSession(std::move(new_effective_config));
278
279 if (net_log_) {
280 net_log_->AddGlobalEntry(NetLogEventType::DNS_CONFIG_CHANGED, [this] {
281 return GetDnsConfigAsValueForNetLog();
282 });
283 }
284
285 return true;
286 }
287
UpdateSession(absl::optional<DnsConfig> new_effective_config)288 void UpdateSession(absl::optional<DnsConfig> new_effective_config) {
289 factory_.reset();
290 session_ = nullptr;
291
292 if (new_effective_config) {
293 DCHECK(new_effective_config.value().IsValid());
294
295 session_ = base::MakeRefCounted<DnsSession>(
296 std::move(new_effective_config).value(), rand_int_callback_,
297 net_log_);
298 factory_ = DnsTransactionFactory::CreateFactory(session_.get());
299 }
300 }
301
302 bool insecure_enabled_ = false;
303 bool can_query_additional_types_via_insecure_ = false;
304 int insecure_fallback_failures_ = 0;
305
306 absl::optional<DnsConfig> system_config_;
307 DnsConfigOverrides config_overrides_;
308
309 scoped_refptr<DnsSession> session_;
310 std::unique_ptr<DnsTransactionFactory> factory_;
311 std::unique_ptr<AddressSorter> address_sorter_ =
312 AddressSorter::CreateAddressSorter();
313
314 raw_ptr<NetLog> net_log_;
315
316 const RandIntCallback rand_int_callback_;
317 };
318
319 } // namespace
320
321 // static
CreateClient(NetLog * net_log)322 std::unique_ptr<DnsClient> DnsClient::CreateClient(NetLog* net_log) {
323 return std::make_unique<DnsClientImpl>(net_log,
324 base::BindRepeating(&base::RandInt));
325 }
326
327 // static
CreateClientForTesting(NetLog * net_log,const RandIntCallback & rand_int_callback)328 std::unique_ptr<DnsClient> DnsClient::CreateClientForTesting(
329 NetLog* net_log,
330 const RandIntCallback& rand_int_callback) {
331 return std::make_unique<DnsClientImpl>(net_log, rand_int_callback);
332 }
333
334 } // namespace net
335