xref: /aosp_15_r20/external/cronet/net/dns/dns_client.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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