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