xref: /aosp_15_r20/external/cronet/net/dns/host_resolver_system_task.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2022 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/host_resolver_system_task.h"
6 
7 #include <memory>
8 #include <optional>
9 
10 #include "base/dcheck_is_on.h"
11 #include "base/functional/bind.h"
12 #include "base/functional/callback.h"
13 #include "base/metrics/field_trial_params.h"
14 #include "base/no_destructor.h"
15 #include "base/sequence_checker.h"
16 #include "base/sequence_checker_impl.h"
17 #include "base/task/sequenced_task_runner.h"
18 #include "base/task/task_traits.h"
19 #include "base/task/thread_pool.h"
20 #include "base/threading/scoped_blocking_call.h"
21 #include "base/types/pass_key.h"
22 #include "dns_reloader.h"
23 #include "net/base/net_errors.h"
24 #include "net/base/network_interfaces.h"
25 #include "net/base/sys_addrinfo.h"
26 #include "net/base/trace_constants.h"
27 #include "net/base/tracing.h"
28 #include "net/dns/address_info.h"
29 #include "net/dns/dns_names_util.h"
30 
31 #if BUILDFLAG(IS_WIN)
32 #include "net/base/winsock_init.h"
33 #endif
34 
35 namespace net {
36 
37 namespace {
38 
39 // Returns nullptr in the common case, or a task runner if the default has
40 // been overridden.
GetSystemDnsResolutionTaskRunnerOverride()41 scoped_refptr<base::TaskRunner>& GetSystemDnsResolutionTaskRunnerOverride() {
42   static base::NoDestructor<scoped_refptr<base::TaskRunner>>
43       system_dns_resolution_task_runner(nullptr);
44   return *system_dns_resolution_task_runner;
45 }
46 
47 // Posts a synchronous callback to a thread pool task runner created with
48 // MayBlock, USER_BLOCKING, and CONTINUE_ON_SHUTDOWN. This task runner can be
49 // overridden by assigning to GetSystemDnsResolutionTaskRunnerOverride().
50 // `results_cb` will be called later on the current sequence with the results of
51 // the DNS resolution.
PostSystemDnsResolutionTaskAndReply(base::OnceCallback<int (AddressList * addrlist,int * os_error)> system_dns_resolution_callback,SystemDnsResultsCallback results_cb)52 void PostSystemDnsResolutionTaskAndReply(
53     base::OnceCallback<int(AddressList* addrlist, int* os_error)>
54         system_dns_resolution_callback,
55     SystemDnsResultsCallback results_cb) {
56   auto addr_list = std::make_unique<net::AddressList>();
57   net::AddressList* addr_list_ptr = addr_list.get();
58   auto os_error = std::make_unique<int>();
59   int* os_error_ptr = os_error.get();
60 
61   // This callback owns |addr_list| and |os_error| and just calls |results_cb|
62   // with the results.
63   auto call_with_results_cb = base::BindOnce(
64       [](SystemDnsResultsCallback results_cb,
65          std::unique_ptr<net::AddressList> addr_list,
66          std::unique_ptr<int> os_error, int net_error) {
67         std::move(results_cb).Run(std::move(*addr_list), *os_error, net_error);
68       },
69       std::move(results_cb), std::move(addr_list), std::move(os_error));
70 
71   scoped_refptr<base::TaskRunner> system_dns_resolution_task_runner =
72       GetSystemDnsResolutionTaskRunnerOverride();
73   if (!system_dns_resolution_task_runner) {
74     // In production this will run on every call, otherwise some tests will
75     // leave a stale task runner around after tearing down their task
76     // environment. This should not be less performant than the regular
77     // base::ThreadPool::PostTask().
78     system_dns_resolution_task_runner = base::ThreadPool::CreateTaskRunner(
79         {base::MayBlock(), base::TaskPriority::USER_BLOCKING,
80          base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN});
81   }
82   system_dns_resolution_task_runner->PostTaskAndReplyWithResult(
83       FROM_HERE,
84       base::BindOnce(std::move(system_dns_resolution_callback), addr_list_ptr,
85                      os_error_ptr),
86       std::move(call_with_results_cb));
87 }
88 
ResolveOnWorkerThread(scoped_refptr<HostResolverProc> resolver_proc,std::optional<std::string> hostname,AddressFamily address_family,HostResolverFlags flags,handles::NetworkHandle network,AddressList * addrlist,int * os_error)89 int ResolveOnWorkerThread(scoped_refptr<HostResolverProc> resolver_proc,
90                           std::optional<std::string> hostname,
91                           AddressFamily address_family,
92                           HostResolverFlags flags,
93                           handles::NetworkHandle network,
94                           AddressList* addrlist,
95                           int* os_error) {
96   std::string hostname_str = hostname ? *hostname : GetHostName();
97   if (resolver_proc) {
98     return resolver_proc->Resolve(hostname_str, address_family, flags, addrlist,
99                                   os_error, network);
100   } else {
101     return SystemHostResolverCall(hostname_str, address_family, flags, addrlist,
102                                   os_error, network);
103   }
104 }
105 
106 // Creates NetLog parameters when the resolve failed.
NetLogHostResolverSystemTaskFailedParams(uint32_t attempt_number,int net_error,int os_error)107 base::Value::Dict NetLogHostResolverSystemTaskFailedParams(
108     uint32_t attempt_number,
109     int net_error,
110     int os_error) {
111   base::Value::Dict dict;
112   if (attempt_number)
113     dict.Set("attempt_number", base::saturated_cast<int>(attempt_number));
114 
115   dict.Set("net_error", net_error);
116 
117   if (os_error) {
118     dict.Set("os_error", os_error);
119 #if BUILDFLAG(IS_WIN)
120     // Map the error code to a human-readable string.
121     LPWSTR error_string = nullptr;
122     FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
123                   nullptr,  // Use the internal message table.
124                   os_error,
125                   0,  // Use default language.
126                   (LPWSTR)&error_string,
127                   0,         // Buffer size.
128                   nullptr);  // Arguments (unused).
129     dict.Set("os_error_string", base::WideToUTF8(error_string));
130     LocalFree(error_string);
131 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
132     dict.Set("os_error_string", gai_strerror(os_error));
133 #endif
134   }
135 
136   return dict;
137 }
138 
139 using SystemDnsResolverOverrideCallback =
140     base::RepeatingCallback<void(const std::optional<std::string>& host,
141                                  AddressFamily address_family,
142                                  HostResolverFlags host_resolver_flags,
143                                  SystemDnsResultsCallback results_cb,
144                                  handles::NetworkHandle network)>;
145 
GetSystemDnsResolverOverride()146 SystemDnsResolverOverrideCallback& GetSystemDnsResolverOverride() {
147   static base::NoDestructor<SystemDnsResolverOverrideCallback> dns_override;
148 
149 #if DCHECK_IS_ON()
150   if (*dns_override) {
151     // This should only be called on the main thread, so DCHECK that it is.
152     // However, in unittests this may be called on different task environments
153     // in the same process so only bother sequence checking if an override
154     // exists.
155     static base::NoDestructor<base::SequenceCheckerImpl> sequence_checker;
156     base::ScopedValidateSequenceChecker scoped_validated_sequence_checker(
157         *sequence_checker);
158   }
159 #endif
160 
161   return *dns_override;
162 }
163 
164 }  // namespace
165 
SetSystemDnsResolverOverride(SystemDnsResolverOverrideCallback dns_override)166 void SetSystemDnsResolverOverride(
167     SystemDnsResolverOverrideCallback dns_override) {
168   GetSystemDnsResolverOverride() = std::move(dns_override);
169 }
170 
Params(scoped_refptr<HostResolverProc> resolver_proc,size_t in_max_retry_attempts)171 HostResolverSystemTask::Params::Params(
172     scoped_refptr<HostResolverProc> resolver_proc,
173     size_t in_max_retry_attempts)
174     : resolver_proc(std::move(resolver_proc)),
175       max_retry_attempts(in_max_retry_attempts),
176       unresponsive_delay(kDnsDefaultUnresponsiveDelay) {
177   // Maximum of 4 retry attempts for host resolution.
178   static const size_t kDefaultMaxRetryAttempts = 4u;
179   if (max_retry_attempts == kDefaultRetryAttempts)
180     max_retry_attempts = kDefaultMaxRetryAttempts;
181 }
182 
183 HostResolverSystemTask::Params::Params(const Params& other) = default;
184 
185 HostResolverSystemTask::Params::~Params() = default;
186 
187 // static
Create(std::string hostname,AddressFamily address_family,HostResolverFlags flags,const Params & params,const NetLogWithSource & job_net_log,handles::NetworkHandle network)188 std::unique_ptr<HostResolverSystemTask> HostResolverSystemTask::Create(
189     std::string hostname,
190     AddressFamily address_family,
191     HostResolverFlags flags,
192     const Params& params,
193     const NetLogWithSource& job_net_log,
194     handles::NetworkHandle network) {
195   return std::make_unique<HostResolverSystemTask>(
196       hostname, address_family, flags, params, job_net_log, network);
197 }
198 
199 // static
200 std::unique_ptr<HostResolverSystemTask>
CreateForOwnHostname(AddressFamily address_family,HostResolverFlags flags,const Params & params,const NetLogWithSource & job_net_log,handles::NetworkHandle network)201 HostResolverSystemTask::CreateForOwnHostname(
202     AddressFamily address_family,
203     HostResolverFlags flags,
204     const Params& params,
205     const NetLogWithSource& job_net_log,
206     handles::NetworkHandle network) {
207   return std::make_unique<HostResolverSystemTask>(
208       std::nullopt, address_family, flags, params, job_net_log, network);
209 }
210 
HostResolverSystemTask(std::optional<std::string> hostname,AddressFamily address_family,HostResolverFlags flags,const Params & params,const NetLogWithSource & job_net_log,handles::NetworkHandle network)211 HostResolverSystemTask::HostResolverSystemTask(
212     std::optional<std::string> hostname,
213     AddressFamily address_family,
214     HostResolverFlags flags,
215     const Params& params,
216     const NetLogWithSource& job_net_log,
217     handles::NetworkHandle network)
218     : hostname_(std::move(hostname)),
219       address_family_(address_family),
220       flags_(flags),
221       params_(params),
222       net_log_(job_net_log),
223       network_(network) {
224   if (hostname_) {
225     // |host| should be a valid domain name. HostResolverManager has checks to
226     // fail early if this is not the case.
227     DCHECK(dns_names_util::IsValidDnsName(*hostname_))
228         << "Invalid hostname: " << *hostname_;
229   }
230   // If a resolver_proc has not been specified, try to use a default if one is
231   // set, as it may be in tests.
232   if (!params_.resolver_proc.get())
233     params_.resolver_proc = HostResolverProc::GetDefault();
234 }
235 
236 // Cancels this HostResolverSystemTask. Any outstanding resolve attempts cannot
237 // be cancelled, but they will post back to the current thread before checking
238 // their WeakPtrs to find that this task is cancelled.
~HostResolverSystemTask()239 HostResolverSystemTask::~HostResolverSystemTask() {
240   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
241 
242   // If this is cancellation, log the EndEvent (otherwise this was logged in
243   // OnLookupComplete()).
244   if (!was_completed())
245     net_log_.EndEvent(NetLogEventType::HOST_RESOLVER_SYSTEM_TASK);
246 }
247 
Start(SystemDnsResultsCallback results_cb)248 void HostResolverSystemTask::Start(SystemDnsResultsCallback results_cb) {
249   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
250   DCHECK(results_cb);
251   DCHECK(!results_cb_);
252   results_cb_ = std::move(results_cb);
253   net_log_.BeginEvent(NetLogEventType::HOST_RESOLVER_SYSTEM_TASK);
254   StartLookupAttempt();
255 }
256 
StartLookupAttempt()257 void HostResolverSystemTask::StartLookupAttempt() {
258   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
259   DCHECK(!was_completed());
260   ++attempt_number_;
261 
262   net_log_.AddEventWithIntParams(
263       NetLogEventType::HOST_RESOLVER_MANAGER_ATTEMPT_STARTED, "attempt_number",
264       attempt_number_);
265 
266   // If the results aren't received within a given time, RetryIfNotComplete
267   // will start a new attempt if none of the outstanding attempts have
268   // completed yet.
269   // Use a WeakPtr to avoid keeping the HostResolverSystemTask alive after
270   // completion or cancellation.
271   if (attempt_number_ <= params_.max_retry_attempts) {
272     base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
273         FROM_HERE,
274         base::BindOnce(&HostResolverSystemTask::StartLookupAttempt,
275                        weak_ptr_factory_.GetWeakPtr()),
276         params_.unresponsive_delay *
277             std::pow(params_.retry_factor, attempt_number_ - 1));
278   }
279 
280   auto lookup_complete_cb =
281       base::BindOnce(&HostResolverSystemTask::OnLookupComplete,
282                      weak_ptr_factory_.GetWeakPtr(), attempt_number_);
283 
284   // If a hook has been installed, call it instead of posting a resolution task
285   // to a worker thread.
286   if (GetSystemDnsResolverOverride()) {
287     GetSystemDnsResolverOverride().Run(hostname_, address_family_, flags_,
288                                        std::move(lookup_complete_cb), network_);
289     // Do not add code below. `lookup_complete_cb` may have already deleted
290     // `this`.
291   } else {
292     base::OnceCallback<int(AddressList * addrlist, int* os_error)> resolve_cb =
293         base::BindOnce(&ResolveOnWorkerThread, params_.resolver_proc, hostname_,
294                        address_family_, flags_, network_);
295     PostSystemDnsResolutionTaskAndReply(std::move(resolve_cb),
296                                         std::move(lookup_complete_cb));
297   }
298 }
299 
300 // Callback for when DoLookup() completes.
OnLookupComplete(const uint32_t attempt_number,const AddressList & results,const int os_error,int error)301 void HostResolverSystemTask::OnLookupComplete(const uint32_t attempt_number,
302                                               const AddressList& results,
303                                               const int os_error,
304                                               int error) {
305   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
306   DCHECK(!was_completed());
307 
308   TRACE_EVENT0(NetTracingCategory(),
309                "HostResolverSystemTask::OnLookupComplete");
310 
311   // Invalidate WeakPtrs to cancel handling of all outstanding lookup attempts
312   // and retries.
313   weak_ptr_factory_.InvalidateWeakPtrs();
314 
315   // If results are empty, we should return an error.
316   bool empty_list_on_ok = (error == OK && results.empty());
317   if (empty_list_on_ok)
318     error = ERR_NAME_NOT_RESOLVED;
319 
320   if (error != OK && NetworkChangeNotifier::IsOffline())
321     error = ERR_INTERNET_DISCONNECTED;
322 
323   if (error != OK) {
324     net_log_.EndEvent(NetLogEventType::HOST_RESOLVER_SYSTEM_TASK, [&] {
325       return NetLogHostResolverSystemTaskFailedParams(0, error, os_error);
326     });
327     net_log_.AddEvent(NetLogEventType::HOST_RESOLVER_MANAGER_ATTEMPT_FINISHED,
328                       [&] {
329                         return NetLogHostResolverSystemTaskFailedParams(
330                             attempt_number, error, os_error);
331                       });
332   } else {
333     net_log_.EndEvent(NetLogEventType::HOST_RESOLVER_SYSTEM_TASK,
334                       [&] { return results.NetLogParams(); });
335     net_log_.AddEventWithIntParams(
336         NetLogEventType::HOST_RESOLVER_MANAGER_ATTEMPT_FINISHED,
337         "attempt_number", attempt_number);
338   }
339 
340   std::move(results_cb_).Run(results, os_error, error);
341   // Running |results_cb_| can delete |this|.
342 }
343 
EnsureSystemHostResolverCallReady()344 void EnsureSystemHostResolverCallReady() {
345   EnsureDnsReloaderInit();
346 #if BUILDFLAG(IS_WIN)
347   EnsureWinsockInit();
348 #endif
349 }
350 
351 namespace {
352 
AddressFamilyToAF(AddressFamily address_family)353 int AddressFamilyToAF(AddressFamily address_family) {
354   switch (address_family) {
355     case ADDRESS_FAMILY_IPV4:
356       return AF_INET;
357     case ADDRESS_FAMILY_IPV6:
358       return AF_INET6;
359     case ADDRESS_FAMILY_UNSPECIFIED:
360       return AF_UNSPEC;
361   }
362 }
363 
364 }  // namespace
365 
SystemHostResolverCall(const std::string & host,AddressFamily address_family,HostResolverFlags host_resolver_flags,AddressList * addrlist,int * os_error_opt,handles::NetworkHandle network)366 int SystemHostResolverCall(const std::string& host,
367                            AddressFamily address_family,
368                            HostResolverFlags host_resolver_flags,
369                            AddressList* addrlist,
370                            int* os_error_opt,
371                            handles::NetworkHandle network) {
372   struct addrinfo hints = {0};
373   hints.ai_family = AddressFamilyToAF(address_family);
374 
375 #if BUILDFLAG(IS_WIN)
376   // DO NOT USE AI_ADDRCONFIG ON WINDOWS.
377   //
378   // The following comment in <winsock2.h> is the best documentation I found
379   // on AI_ADDRCONFIG for Windows:
380   //   Flags used in "hints" argument to getaddrinfo()
381   //       - AI_ADDRCONFIG is supported starting with Vista
382   //       - default is AI_ADDRCONFIG ON whether the flag is set or not
383   //         because the performance penalty in not having ADDRCONFIG in
384   //         the multi-protocol stack environment is severe;
385   //         this defaulting may be disabled by specifying the AI_ALL flag,
386   //         in that case AI_ADDRCONFIG must be EXPLICITLY specified to
387   //         enable ADDRCONFIG behavior
388   //
389   // Not only is AI_ADDRCONFIG unnecessary, but it can be harmful.  If the
390   // computer is not connected to a network, AI_ADDRCONFIG causes getaddrinfo
391   // to fail with WSANO_DATA (11004) for "localhost", probably because of the
392   // following note on AI_ADDRCONFIG in the MSDN getaddrinfo page:
393   //   The IPv4 or IPv6 loopback address is not considered a valid global
394   //   address.
395   // See http://crbug.com/5234.
396   //
397   // OpenBSD does not support it, either.
398   hints.ai_flags = 0;
399 #else
400   // On other operating systems, AI_ADDRCONFIG may reduce the amount of
401   // unnecessary DNS lookups, e.g. getaddrinfo() will not send a request for
402   // AAAA records if the current machine has no IPv6 addresses configured and
403   // therefore could not use the resulting AAAA record anyway. On some ancient
404   // routers, AAAA DNS queries won't be handled correctly and will cause
405   // multiple retransmitions and large latency spikes.
406   hints.ai_flags = AI_ADDRCONFIG;
407 #endif
408 
409   // On Linux AI_ADDRCONFIG doesn't consider loopback addresses, even if only
410   // loopback addresses are configured. So don't use it when there are only
411   // loopback addresses. See loopback_only.h and
412   // https://fedoraproject.org/wiki/QA/Networking/NameResolution/ADDRCONFIG for
413   // a description of some of the issues AI_ADDRCONFIG can cause.
414   if (host_resolver_flags & HOST_RESOLVER_LOOPBACK_ONLY) {
415     hints.ai_flags &= ~AI_ADDRCONFIG;
416   }
417 
418   if (host_resolver_flags & HOST_RESOLVER_CANONNAME)
419     hints.ai_flags |= AI_CANONNAME;
420 
421 #if BUILDFLAG(IS_WIN)
422   // See crbug.com/1176970. Flag not documented (other than the declaration
423   // comment in ws2def.h) but confirmed by Microsoft to work for this purpose
424   // and be safe.
425   if (host_resolver_flags & HOST_RESOLVER_AVOID_MULTICAST)
426     hints.ai_flags |= AI_DNS_ONLY;
427 #endif  // BUILDFLAG(IS_WIN)
428 
429   // Restrict result set to only this socket type to avoid duplicates.
430   hints.ai_socktype = SOCK_STREAM;
431 
432   // This function can block for a long time. Use ScopedBlockingCall to increase
433   // the current thread pool's capacity and thus avoid reducing CPU usage by the
434   // current process during that time.
435   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
436                                                 base::BlockingType::WILL_BLOCK);
437   DnsReloaderMaybeReload();
438 
439   auto [ai, err, os_error] = AddressInfo::Get(host, hints, nullptr, network);
440   bool should_retry = false;
441   // If the lookup was restricted (either by address family, or address
442   // detection), and the results where all localhost of a single family,
443   // maybe we should retry.  There were several bugs related to these
444   // issues, for example http://crbug.com/42058 and http://crbug.com/49024
445   if ((hints.ai_family != AF_UNSPEC || hints.ai_flags & AI_ADDRCONFIG) && ai &&
446       ai->IsAllLocalhostOfOneFamily()) {
447     if (host_resolver_flags & HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6) {
448       hints.ai_family = AF_UNSPEC;
449       should_retry = true;
450     }
451     if (hints.ai_flags & AI_ADDRCONFIG) {
452       hints.ai_flags &= ~AI_ADDRCONFIG;
453       should_retry = true;
454     }
455   }
456   if (should_retry) {
457     std::tie(ai, err, os_error) =
458         AddressInfo::Get(host, hints, nullptr, network);
459   }
460 
461   if (os_error_opt)
462     *os_error_opt = os_error;
463 
464   if (!ai)
465     return err;
466 
467   *addrlist = ai->CreateAddressList();
468   return OK;
469 }
470 
SetSystemDnsResolutionTaskRunnerForTesting(scoped_refptr<base::TaskRunner> task_runner)471 void SetSystemDnsResolutionTaskRunnerForTesting(  // IN-TEST
472     scoped_refptr<base::TaskRunner> task_runner) {
473   GetSystemDnsResolutionTaskRunnerOverride() = task_runner;
474 }
475 
476 }  // namespace net
477