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