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