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