1 // Copyright 2024 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_manager_job.h"
6
7 #include <deque>
8 #include <memory>
9 #include <optional>
10 #include <vector>
11
12 #include "base/containers/linked_list.h"
13 #include "base/memory/raw_ptr.h"
14 #include "base/memory/safe_ref.h"
15 #include "base/memory/weak_ptr.h"
16 #include "base/metrics/histogram_functions.h"
17 #include "base/metrics/histogram_macros.h"
18 #include "base/task/sequenced_task_runner.h"
19 #include "base/time/time.h"
20 #include "net/base/address_family.h"
21 #include "net/base/features.h"
22 #include "net/base/network_anonymization_key.h"
23 #include "net/base/network_handle.h"
24 #include "net/base/prioritized_dispatcher.h"
25 #include "net/base/url_util.h"
26 #include "net/dns/dns_client.h"
27 #include "net/dns/dns_task_results_manager.h"
28 #include "net/dns/host_cache.h"
29 #include "net/dns/host_resolver.h"
30 #include "net/dns/host_resolver_dns_task.h"
31 #include "net/dns/host_resolver_manager.h"
32 #include "net/dns/host_resolver_manager_request_impl.h"
33 #include "net/dns/host_resolver_manager_service_endpoint_request_impl.h"
34 #include "net/dns/host_resolver_mdns_task.h"
35 #include "net/dns/host_resolver_nat64_task.h"
36 #include "net/dns/public/dns_query_type.h"
37 #include "net/dns/public/secure_dns_mode.h"
38 #include "net/log/net_log_with_source.h"
39 #include "third_party/abseil-cpp/absl/types/variant.h"
40 #include "url/url_constants.h"
41
42 namespace net {
43
44 namespace {
45
46 // Default TTL for successful resolutions with HostResolverSystemTask.
47 const unsigned kCacheEntryTTLSeconds = 60;
48
49 // Default TTL for unsuccessful resolutions with HostResolverSystemTask.
50 const unsigned kNegativeCacheEntryTTLSeconds = 0;
51
52 // Minimum TTL for successful resolutions with HostResolverDnsTask.
53 const unsigned kMinimumTTLSeconds = kCacheEntryTTLSeconds;
54
55 // ICANN uses this localhost address to indicate a name collision.
56 //
57 // The policy in Chromium is to fail host resolving if it resolves to
58 // this special address.
59 //
60 // Not however that IP literals are exempt from this policy, so it is still
61 // possible to navigate to http://127.0.53.53/ directly.
62 //
63 // For more details: https://www.icann.org/news/announcement-2-2014-08-01-en
64 const uint8_t kIcanNameCollisionIp[] = {127, 0, 53, 53};
65
ContainsIcannNameCollisionIp(const std::vector<IPEndPoint> & endpoints)66 bool ContainsIcannNameCollisionIp(const std::vector<IPEndPoint>& endpoints) {
67 for (const auto& endpoint : endpoints) {
68 const IPAddress& addr = endpoint.address();
69 if (addr.IsIPv4() && IPAddressStartsWith(addr, kIcanNameCollisionIp)) {
70 return true;
71 }
72 }
73 return false;
74 }
75
76 // Creates NetLog parameters for HOST_RESOLVER_MANAGER_JOB_ATTACH/DETACH events.
NetLogJobAttachParams(const NetLogSource & source,RequestPriority priority)77 base::Value::Dict NetLogJobAttachParams(const NetLogSource& source,
78 RequestPriority priority) {
79 base::Value::Dict dict;
80 source.AddToEventParameters(dict);
81 dict.Set("priority", RequestPriorityToString(priority));
82 return dict;
83 }
84
IsSchemeHttpsOrWss(const HostResolver::Host & host)85 bool IsSchemeHttpsOrWss(const HostResolver::Host& host) {
86 if (!host.HasScheme()) {
87 return false;
88 }
89 const std::string& scheme = host.GetScheme();
90 return scheme == url::kHttpsScheme || scheme == url::kWssScheme;
91 }
92
93 } // namespace
94
JobKey(HostResolver::Host host,ResolveContext * resolve_context)95 HostResolverManager::JobKey::JobKey(HostResolver::Host host,
96 ResolveContext* resolve_context)
97 : host(std::move(host)), resolve_context(resolve_context->GetWeakPtr()) {}
98
99 HostResolverManager::JobKey::~JobKey() = default;
100
101 HostResolverManager::JobKey::JobKey(const JobKey& other) = default;
102 HostResolverManager::JobKey& HostResolverManager::JobKey::operator=(
103 const JobKey& other) = default;
104
operator <(const JobKey & other) const105 bool HostResolverManager::JobKey::operator<(const JobKey& other) const {
106 return std::forward_as_tuple(query_types.ToEnumBitmask(), flags, source,
107 secure_dns_mode, &*resolve_context, host,
108 network_anonymization_key) <
109 std::forward_as_tuple(other.query_types.ToEnumBitmask(), other.flags,
110 other.source, other.secure_dns_mode,
111 &*other.resolve_context, other.host,
112 other.network_anonymization_key);
113 }
114
operator ==(const JobKey & other) const115 bool HostResolverManager::JobKey::operator==(const JobKey& other) const {
116 return !(*this < other || other < *this);
117 }
118
ToCacheKey(bool secure) const119 HostCache::Key HostResolverManager::JobKey::ToCacheKey(bool secure) const {
120 if (query_types.size() != 1) {
121 // This function will produce identical cache keys for `JobKey` structs
122 // that differ only in their (non-singleton) `query_types` fields. When we
123 // enable new query types, this behavior could lead to subtle bugs. That
124 // is why the following DCHECK restricts the allowable query types.
125 DCHECK(Difference(query_types, {DnsQueryType::A, DnsQueryType::AAAA,
126 DnsQueryType::HTTPS})
127 .empty());
128 }
129 const DnsQueryType query_type_for_key = query_types.size() == 1
130 ? *query_types.begin()
131 : DnsQueryType::UNSPECIFIED;
132 absl::variant<url::SchemeHostPort, std::string> host_for_cache;
133 if (host.HasScheme()) {
134 host_for_cache = host.AsSchemeHostPort();
135 } else {
136 host_for_cache = std::string(host.GetHostnameWithoutBrackets());
137 }
138 HostCache::Key key(std::move(host_for_cache), query_type_for_key, flags,
139 source, network_anonymization_key);
140 key.secure = secure;
141 return key;
142 }
143
GetTargetNetwork() const144 handles::NetworkHandle HostResolverManager::JobKey::GetTargetNetwork() const {
145 return resolve_context ? resolve_context->GetTargetNetwork()
146 : handles::kInvalidNetworkHandle;
147 }
148
Job(const base::WeakPtr<HostResolverManager> & resolver,JobKey key,ResolveHostParameters::CacheUsage cache_usage,HostCache * host_cache,std::deque<TaskType> tasks,RequestPriority priority,const NetLogWithSource & source_net_log,const base::TickClock * tick_clock,const HostResolver::HttpsSvcbOptions & https_svcb_options)149 HostResolverManager::Job::Job(
150 const base::WeakPtr<HostResolverManager>& resolver,
151 JobKey key,
152 ResolveHostParameters::CacheUsage cache_usage,
153 HostCache* host_cache,
154 std::deque<TaskType> tasks,
155 RequestPriority priority,
156 const NetLogWithSource& source_net_log,
157 const base::TickClock* tick_clock,
158 const HostResolver::HttpsSvcbOptions& https_svcb_options)
159 : resolver_(resolver),
160 key_(std::move(key)),
161 cache_usage_(cache_usage),
162 host_cache_(host_cache),
163 tasks_(tasks),
164 priority_tracker_(priority),
165 tick_clock_(tick_clock),
166 https_svcb_options_(https_svcb_options),
167 net_log_(
168 NetLogWithSource::Make(source_net_log.net_log(),
169 NetLogSourceType::HOST_RESOLVER_IMPL_JOB)) {
170 source_net_log.AddEvent(NetLogEventType::HOST_RESOLVER_MANAGER_CREATE_JOB);
171
172 net_log_.BeginEvent(NetLogEventType::HOST_RESOLVER_MANAGER_JOB, [&] {
173 return NetLogJobCreationParams(source_net_log.source());
174 });
175 }
176
~Job()177 HostResolverManager::Job::~Job() {
178 bool was_queued = is_queued();
179 bool was_running = is_running();
180 // Clean up now for nice NetLog.
181 Finish();
182 if (was_running) {
183 // This Job was destroyed while still in flight.
184 net_log_.EndEventWithNetErrorCode(
185 NetLogEventType::HOST_RESOLVER_MANAGER_JOB, ERR_ABORTED);
186 } else if (was_queued) {
187 // Job was cancelled before it could run.
188 // TODO(szym): is there any benefit in having this distinction?
189 net_log_.AddEvent(NetLogEventType::CANCELLED);
190 net_log_.EndEvent(NetLogEventType::HOST_RESOLVER_MANAGER_JOB);
191 }
192 // else CompleteRequests logged EndEvent.
193 while (!requests_.empty()) {
194 // Log any remaining Requests as cancelled.
195 RequestImpl* req = requests_.head()->value();
196 req->RemoveFromList();
197 CHECK(key_ == req->GetJobKey());
198 req->OnJobCancelled(key_);
199 }
200
201 while (!service_endpoint_requests_.empty()) {
202 ServiceEndpointRequestImpl* request =
203 service_endpoint_requests_.head()->value();
204 request->RemoveFromList();
205 request->OnJobCancelled();
206 }
207 }
208
Schedule(bool at_head)209 void HostResolverManager::Job::Schedule(bool at_head) {
210 DCHECK(!is_queued());
211 PrioritizedDispatcher::Handle handle;
212 DCHECK(dispatched_);
213 if (!at_head) {
214 handle = resolver_->dispatcher_->Add(this, priority());
215 } else {
216 handle = resolver_->dispatcher_->AddAtHead(this, priority());
217 }
218 // The dispatcher could have started |this| in the above call to Add, which
219 // could have called Schedule again. In that case |handle| will be null,
220 // but |handle_| may have been set by the other nested call to Schedule.
221 if (!handle.is_null()) {
222 DCHECK(handle_.is_null());
223 handle_ = handle;
224 }
225 }
226
AddRequest(RequestImpl * request)227 void HostResolverManager::Job::AddRequest(RequestImpl* request) {
228 // Job currently assumes a 1:1 correspondence between ResolveContext and
229 // HostCache. Since the ResolveContext is part of the JobKey, any request
230 // added to any existing Job should share the same HostCache.
231 DCHECK_EQ(host_cache_, request->host_cache());
232 // TODO(crbug.com/1206799): Check equality of whole host once Jobs are
233 // separated by scheme/port.
234 DCHECK_EQ(key_.host.GetHostnameWithoutBrackets(),
235 request->request_host().GetHostnameWithoutBrackets());
236
237 request->AssignJob(weak_ptr_factory_.GetSafeRef());
238
239 AddRequestCommon(request->priority(), request->source_net_log(),
240 request->parameters().is_speculative);
241
242 requests_.Append(request);
243
244 UpdatePriority();
245 }
246
ChangeRequestPriority(RequestImpl * req,RequestPriority priority)247 void HostResolverManager::Job::ChangeRequestPriority(RequestImpl* req,
248 RequestPriority priority) {
249 DCHECK_EQ(key_.host, req->request_host());
250
251 priority_tracker_.Remove(req->priority());
252 req->set_priority(priority);
253 priority_tracker_.Add(req->priority());
254 UpdatePriority();
255 }
256
CancelRequest(RequestImpl * request)257 void HostResolverManager::Job::CancelRequest(RequestImpl* request) {
258 DCHECK_EQ(key_.host, request->request_host());
259 DCHECK(!requests_.empty());
260
261 CancelRequestCommon(request->priority(), request->source_net_log());
262
263 if (num_active_requests() > 0) {
264 UpdatePriority();
265 request->RemoveFromList();
266 } else {
267 // If we were called from a Request's callback within CompleteRequests,
268 // that Request could not have been cancelled, so num_active_requests()
269 // could not be 0. Therefore, we are not in CompleteRequests().
270 CompleteRequestsWithError(ERR_DNS_REQUEST_CANCELLED,
271 /*task_type=*/std::nullopt);
272 }
273 }
274
AddServiceEndpointRequest(ServiceEndpointRequestImpl * request)275 void HostResolverManager::Job::AddServiceEndpointRequest(
276 ServiceEndpointRequestImpl* request) {
277 CHECK_EQ(host_cache_, request->host_cache());
278
279 request->AssignJob(weak_ptr_factory_.GetSafeRef());
280
281 AddRequestCommon(request->priority(), request->net_log(),
282 request->parameters().is_speculative);
283
284 service_endpoint_requests_.Append(request);
285
286 UpdatePriority();
287 }
288
CancelServiceEndpointRequest(ServiceEndpointRequestImpl * request)289 void HostResolverManager::Job::CancelServiceEndpointRequest(
290 ServiceEndpointRequestImpl* request) {
291 CancelRequestCommon(request->priority(), request->net_log());
292
293 if (num_active_requests() > 0) {
294 UpdatePriority();
295 request->RemoveFromList();
296 } else {
297 // See comments in CancelRequest().
298 CompleteRequestsWithError(ERR_DNS_REQUEST_CANCELLED,
299 /*task_type=*/std::nullopt);
300 }
301 }
302
Abort()303 void HostResolverManager::Job::Abort() {
304 CompleteRequestsWithError(ERR_NETWORK_CHANGED, /*task_type=*/std::nullopt);
305 }
306
GetAbortInsecureDnsTaskClosure(int error,bool fallback_only)307 base::OnceClosure HostResolverManager::Job::GetAbortInsecureDnsTaskClosure(
308 int error,
309 bool fallback_only) {
310 return base::BindOnce(&Job::AbortInsecureDnsTask,
311 weak_ptr_factory_.GetWeakPtr(), error, fallback_only);
312 }
313
AbortInsecureDnsTask(int error,bool fallback_only)314 void HostResolverManager::Job::AbortInsecureDnsTask(int error,
315 bool fallback_only) {
316 bool has_system_fallback = base::Contains(tasks_, TaskType::SYSTEM);
317 if (has_system_fallback) {
318 for (auto it = tasks_.begin(); it != tasks_.end();) {
319 if (*it == TaskType::DNS) {
320 it = tasks_.erase(it);
321 } else {
322 ++it;
323 }
324 }
325 }
326
327 if (dns_task_ && !dns_task_->secure()) {
328 if (has_system_fallback) {
329 KillDnsTask();
330 dns_task_error_ = OK;
331 RunNextTask();
332 } else if (!fallback_only) {
333 CompleteRequestsWithError(error, /*task_type=*/std::nullopt);
334 }
335 }
336 }
337
OnEvicted()338 void HostResolverManager::Job::OnEvicted() {
339 DCHECK(!is_running());
340 DCHECK(is_queued());
341 handle_.Reset();
342
343 net_log_.AddEvent(NetLogEventType::HOST_RESOLVER_MANAGER_JOB_EVICTED);
344
345 // This signals to CompleteRequests that parts of this job never ran.
346 // Job must be saved in |resolver_| to be completed asynchronously.
347 // Otherwise the job will be destroyed with requests silently cancelled
348 // before completion runs.
349 DCHECK(self_iterator_);
350 base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
351 FROM_HERE, base::BindOnce(&Job::CompleteRequestsWithError,
352 weak_ptr_factory_.GetWeakPtr(),
353 ERR_HOST_RESOLVER_QUEUE_TOO_LARGE,
354 /*task_type=*/std::nullopt));
355 }
356
ServeFromHosts()357 bool HostResolverManager::Job::ServeFromHosts() {
358 DCHECK_GT(num_active_requests(), 0u);
359 std::optional<HostCache::Entry> results = resolver_->ServeFromHosts(
360 key_.host.GetHostnameWithoutBrackets(), key_.query_types,
361 key_.flags & HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6, tasks_);
362 if (results) {
363 // This will destroy the Job.
364 CompleteRequests(results.value(), base::TimeDelta(), true /* allow_cache */,
365 true /* secure */, TaskType::HOSTS);
366 return true;
367 }
368 return false;
369 }
370
OnAddedToJobMap(JobMap::iterator iterator)371 void HostResolverManager::Job::OnAddedToJobMap(JobMap::iterator iterator) {
372 DCHECK(!self_iterator_);
373 DCHECK(iterator != resolver_->jobs_.end());
374 self_iterator_ = iterator;
375 }
376
OnRemovedFromJobMap()377 void HostResolverManager::Job::OnRemovedFromJobMap() {
378 DCHECK(self_iterator_);
379 self_iterator_ = std::nullopt;
380 }
381
RunNextTask()382 void HostResolverManager::Job::RunNextTask() {
383 // If there are no tasks left to try, cache any stored results and complete
384 // the request with the last stored result. All stored results should be
385 // errors.
386 if (tasks_.empty()) {
387 // If there are no stored results, complete with an error.
388 if (completion_results_.size() == 0) {
389 CompleteRequestsWithError(ERR_NAME_NOT_RESOLVED,
390 /*task_type=*/std::nullopt);
391 return;
392 }
393
394 // Cache all but the last result here. The last result will be cached
395 // as part of CompleteRequests.
396 for (size_t i = 0; i < completion_results_.size() - 1; ++i) {
397 const auto& result = completion_results_[i];
398 DCHECK_NE(OK, result.entry.error());
399 MaybeCacheResult(result.entry, result.ttl, result.secure);
400 }
401 const auto& last_result = completion_results_.back();
402 DCHECK_NE(OK, last_result.entry.error());
403 CompleteRequests(last_result.entry, last_result.ttl, true /* allow_cache */,
404 last_result.secure,
405 last_result.secure ? TaskType::SECURE_DNS : TaskType::DNS);
406 return;
407 }
408
409 TaskType next_task = tasks_.front();
410
411 // Schedule insecure DnsTasks and HostResolverSystemTasks with the
412 // dispatcher.
413 if (!dispatched_ &&
414 (next_task == TaskType::DNS || next_task == TaskType::SYSTEM ||
415 next_task == TaskType::MDNS)) {
416 dispatched_ = true;
417 job_running_ = false;
418 Schedule(false);
419 DCHECK(is_running() || is_queued());
420
421 // Check for queue overflow.
422 PrioritizedDispatcher& dispatcher = *resolver_->dispatcher_;
423 if (dispatcher.num_queued_jobs() > resolver_->max_queued_jobs_) {
424 Job* evicted = static_cast<Job*>(dispatcher.EvictOldestLowest());
425 DCHECK(evicted);
426 evicted->OnEvicted();
427 }
428 return;
429 }
430
431 if (start_time_ == base::TimeTicks()) {
432 net_log_.AddEvent(NetLogEventType::HOST_RESOLVER_MANAGER_JOB_STARTED);
433 start_time_ = tick_clock_->NowTicks();
434 }
435 tasks_.pop_front();
436 job_running_ = true;
437
438 switch (next_task) {
439 case TaskType::SYSTEM:
440 StartSystemTask();
441 break;
442 case TaskType::DNS:
443 StartDnsTask(false /* secure */);
444 break;
445 case TaskType::SECURE_DNS:
446 StartDnsTask(true /* secure */);
447 break;
448 case TaskType::MDNS:
449 StartMdnsTask();
450 break;
451 case TaskType::INSECURE_CACHE_LOOKUP:
452 InsecureCacheLookup();
453 break;
454 case TaskType::NAT64:
455 StartNat64Task();
456 break;
457 case TaskType::SECURE_CACHE_LOOKUP:
458 case TaskType::CACHE_LOOKUP:
459 case TaskType::CONFIG_PRESET:
460 case TaskType::HOSTS:
461 // These task types should have been handled synchronously in
462 // ResolveLocally() prior to Job creation.
463 NOTREACHED();
464 break;
465 }
466 }
467
NetLogJobCreationParams(const NetLogSource & source)468 base::Value::Dict HostResolverManager::Job::NetLogJobCreationParams(
469 const NetLogSource& source) {
470 base::Value::Dict dict;
471 source.AddToEventParameters(dict);
472 dict.Set("host", key_.host.ToString());
473 base::Value::List query_types_list;
474 for (DnsQueryType query_type : key_.query_types) {
475 query_types_list.Append(kDnsQueryTypes.at(query_type));
476 }
477 dict.Set("dns_query_types", std::move(query_types_list));
478 dict.Set("secure_dns_mode", base::strict_cast<int>(key_.secure_dns_mode));
479 dict.Set("network_anonymization_key",
480 key_.network_anonymization_key.ToDebugString());
481 return dict;
482 }
483
Finish()484 void HostResolverManager::Job::Finish() {
485 if (is_running()) {
486 // Clean up but don't run any callbacks.
487 system_task_ = nullptr;
488 KillDnsTask();
489 mdns_task_ = nullptr;
490 job_running_ = false;
491
492 if (dispatched_) {
493 // Job should only ever occupy one slot after any tasks that may have
494 // required additional slots, e.g. DnsTask, have been killed, and
495 // additional slots are expected to be vacated as part of killing the
496 // task.
497 DCHECK_EQ(1, num_occupied_job_slots_);
498 if (resolver_) {
499 resolver_->dispatcher_->OnJobFinished();
500 }
501 num_occupied_job_slots_ = 0;
502 }
503 } else if (is_queued()) {
504 DCHECK(dispatched_);
505 if (resolver_) {
506 resolver_->dispatcher_->Cancel(handle_);
507 }
508 handle_.Reset();
509 }
510 }
511
KillDnsTask()512 void HostResolverManager::Job::KillDnsTask() {
513 if (dns_task_) {
514 if (dispatched_) {
515 while (num_occupied_job_slots_ > 1 || is_queued()) {
516 ReduceByOneJobSlot();
517 }
518 }
519 dns_task_.reset();
520 }
521 dns_task_results_manager_.reset();
522 }
523
ReduceByOneJobSlot()524 void HostResolverManager::Job::ReduceByOneJobSlot() {
525 DCHECK_GE(num_occupied_job_slots_, 1);
526 DCHECK(dispatched_);
527 if (is_queued()) {
528 if (resolver_) {
529 resolver_->dispatcher_->Cancel(handle_);
530 }
531 handle_.Reset();
532 } else if (num_occupied_job_slots_ > 1) {
533 if (resolver_) {
534 resolver_->dispatcher_->OnJobFinished();
535 }
536 --num_occupied_job_slots_;
537 } else {
538 NOTREACHED();
539 }
540 }
541
AddRequestCommon(RequestPriority request_priority,const NetLogWithSource & request_net_log,bool is_speculative)542 void HostResolverManager::Job::AddRequestCommon(
543 RequestPriority request_priority,
544 const NetLogWithSource& request_net_log,
545 bool is_speculative) {
546 priority_tracker_.Add(request_priority);
547 request_net_log.AddEventReferencingSource(
548 NetLogEventType::HOST_RESOLVER_MANAGER_JOB_ATTACH, net_log_.source());
549 net_log_.AddEvent(
550 NetLogEventType::HOST_RESOLVER_MANAGER_JOB_REQUEST_ATTACH, [&] {
551 return NetLogJobAttachParams(request_net_log.source(), priority());
552 });
553 if (!is_speculative) {
554 had_non_speculative_request_ = true;
555 }
556 }
557
CancelRequestCommon(RequestPriority request_priority,const NetLogWithSource & request_net_log)558 void HostResolverManager::Job::CancelRequestCommon(
559 RequestPriority request_priority,
560 const NetLogWithSource& request_net_log) {
561 priority_tracker_.Remove(request_priority);
562 net_log_.AddEvent(
563 NetLogEventType::HOST_RESOLVER_MANAGER_JOB_REQUEST_DETACH, [&] {
564 return NetLogJobAttachParams(request_net_log.source(), priority());
565 });
566 }
567
UpdatePriority()568 void HostResolverManager::Job::UpdatePriority() {
569 if (is_queued()) {
570 handle_ = resolver_->dispatcher_->ChangePriority(handle_, priority());
571 }
572 }
573
Start()574 void HostResolverManager::Job::Start() {
575 handle_.Reset();
576 ++num_occupied_job_slots_;
577
578 if (num_occupied_job_slots_ >= 2) {
579 if (!dns_task_) {
580 resolver_->dispatcher_->OnJobFinished();
581 return;
582 }
583 StartNextDnsTransaction();
584 DCHECK_EQ(num_occupied_job_slots_,
585 dns_task_->num_transactions_in_progress());
586 if (dns_task_->num_additional_transactions_needed() >= 1) {
587 Schedule(true);
588 }
589 return;
590 }
591
592 DCHECK(!is_running());
593 DCHECK(!tasks_.empty());
594 RunNextTask();
595 // Caution: Job::Start must not complete synchronously.
596 }
597
StartSystemTask()598 void HostResolverManager::Job::StartSystemTask() {
599 DCHECK(dispatched_);
600 DCHECK_EQ(1, num_occupied_job_slots_);
601 DCHECK(HasAddressType(key_.query_types));
602
603 system_task_ = HostResolverSystemTask::Create(
604 std::string(key_.host.GetHostnameWithoutBrackets()),
605 HostResolver::DnsQueryTypeSetToAddressFamily(key_.query_types),
606 key_.flags, resolver_->host_resolver_system_params_, net_log_,
607 key_.GetTargetNetwork());
608
609 // Start() could be called from within Resolve(), hence it must NOT directly
610 // call OnSystemTaskComplete, for example, on synchronous failure.
611 system_task_->Start(base::BindOnce(&Job::OnSystemTaskComplete,
612 base::Unretained(this),
613 tick_clock_->NowTicks()));
614 }
615
OnSystemTaskComplete(base::TimeTicks start_time,const AddressList & addr_list,int,int net_error)616 void HostResolverManager::Job::OnSystemTaskComplete(
617 base::TimeTicks start_time,
618 const AddressList& addr_list,
619 int /*os_error*/,
620 int net_error) {
621 DCHECK(system_task_);
622
623 base::TimeDelta duration = tick_clock_->NowTicks() - start_time;
624 if (net_error == OK) {
625 UMA_HISTOGRAM_LONG_TIMES_100("Net.DNS.SystemTask.SuccessTime", duration);
626 } else {
627 UMA_HISTOGRAM_LONG_TIMES_100("Net.DNS.SystemTask.FailureTime", duration);
628 }
629
630 if (dns_task_error_ != OK && net_error == OK) {
631 // This HostResolverSystemTask was a fallback resolution after a failed
632 // insecure DnsTask.
633 resolver_->OnFallbackResolve(dns_task_error_);
634 }
635
636 if (ContainsIcannNameCollisionIp(addr_list.endpoints())) {
637 net_error = ERR_ICANN_NAME_COLLISION;
638 }
639
640 base::TimeDelta ttl = base::Seconds(kNegativeCacheEntryTTLSeconds);
641 if (net_error == OK) {
642 ttl = base::Seconds(kCacheEntryTTLSeconds);
643 }
644
645 auto aliases = std::set<std::string>(addr_list.dns_aliases().begin(),
646 addr_list.dns_aliases().end());
647
648 // Source unknown because the system resolver could have gotten it from a
649 // hosts file, its own cache, a DNS lookup or somewhere else.
650 // Don't store the |ttl| in cache since it's not obtained from the server.
651 CompleteRequests(
652 HostCache::Entry(
653 net_error,
654 net_error == OK ? addr_list.endpoints() : std::vector<IPEndPoint>(),
655 std::move(aliases), HostCache::Entry::SOURCE_UNKNOWN),
656 ttl, /*allow_cache=*/true, /*secure=*/false, TaskType::SYSTEM);
657 }
658
InsecureCacheLookup()659 void HostResolverManager::Job::InsecureCacheLookup() {
660 // Insecure cache lookups for requests allowing stale results should have
661 // occurred prior to Job creation.
662 DCHECK(cache_usage_ != ResolveHostParameters::CacheUsage::STALE_ALLOWED);
663 std::optional<HostCache::EntryStaleness> stale_info;
664 std::optional<HostCache::Entry> resolved = resolver_->MaybeServeFromCache(
665 host_cache_, key_.ToCacheKey(/*secure=*/false), cache_usage_,
666 false /* ignore_secure */, net_log_, &stale_info);
667
668 if (resolved) {
669 DCHECK(stale_info);
670 DCHECK(!stale_info.value().is_stale());
671 CompleteRequestsWithoutCache(resolved.value(), std::move(stale_info),
672 TaskType::INSECURE_CACHE_LOOKUP);
673 } else {
674 RunNextTask();
675 }
676 }
677
StartDnsTask(bool secure)678 void HostResolverManager::Job::StartDnsTask(bool secure) {
679 DCHECK_EQ(secure, !dispatched_);
680 DCHECK_EQ(dispatched_ ? 1 : 0, num_occupied_job_slots_);
681 DCHECK(!resolver_->ShouldForceSystemResolverDueToTestOverride());
682
683 CHECK(!dns_task_results_manager_);
684 if (base::FeatureList::IsEnabled(features::kUseServiceEndpointRequest)) {
685 dns_task_results_manager_ = std::make_unique<DnsTaskResultsManager>(
686 this, key_.host, key_.query_types, net_log_);
687 }
688
689 // Need to create the task even if we're going to post a failure instead of
690 // running it, as a "started" job needs a task to be properly cleaned up.
691 dns_task_ = std::make_unique<HostResolverDnsTask>(
692 resolver_->dns_client_.get(), key_.host, key_.network_anonymization_key,
693 key_.query_types, &*key_.resolve_context, secure, key_.secure_dns_mode,
694 this, net_log_, tick_clock_, !tasks_.empty() /* fallback_available */,
695 https_svcb_options_);
696 dns_task_->StartNextTransaction();
697 // Schedule a second transaction, if needed. DoH queries can bypass the
698 // dispatcher and start all of their transactions immediately.
699 if (secure) {
700 while (dns_task_->num_additional_transactions_needed() >= 1) {
701 dns_task_->StartNextTransaction();
702 }
703 DCHECK_EQ(dns_task_->num_additional_transactions_needed(), 0);
704 } else if (dns_task_->num_additional_transactions_needed() >= 1) {
705 Schedule(true);
706 }
707 }
708
StartNextDnsTransaction()709 void HostResolverManager::Job::StartNextDnsTransaction() {
710 DCHECK(dns_task_);
711 DCHECK_EQ(dns_task_->secure(), !dispatched_);
712 DCHECK(!dispatched_ || num_occupied_job_slots_ ==
713 dns_task_->num_transactions_in_progress() + 1);
714 DCHECK_GE(dns_task_->num_additional_transactions_needed(), 1);
715 dns_task_->StartNextTransaction();
716 }
717
OnDnsTaskFailure(const base::WeakPtr<HostResolverDnsTask> & dns_task,base::TimeDelta duration,bool allow_fallback,const HostCache::Entry & failure_results,bool secure)718 void HostResolverManager::Job::OnDnsTaskFailure(
719 const base::WeakPtr<HostResolverDnsTask>& dns_task,
720 base::TimeDelta duration,
721 bool allow_fallback,
722 const HostCache::Entry& failure_results,
723 bool secure) {
724 DCHECK_NE(OK, failure_results.error());
725
726 if (!secure) {
727 DCHECK_NE(key_.secure_dns_mode, SecureDnsMode::kSecure);
728 UMA_HISTOGRAM_LONG_TIMES_100("Net.DNS.InsecureDnsTask.FailureTime",
729 duration);
730 }
731
732 if (!dns_task) {
733 return;
734 }
735
736 UMA_HISTOGRAM_LONG_TIMES_100("Net.DNS.JobQueueTime.Failure",
737 total_transaction_time_queued_);
738
739 // If one of the fallback tasks doesn't complete the request, store a result
740 // to use during request completion.
741 base::TimeDelta ttl =
742 failure_results.has_ttl() ? failure_results.ttl() : base::Seconds(0);
743 completion_results_.push_back({failure_results, ttl, secure});
744
745 dns_task_error_ = failure_results.error();
746 KillDnsTask();
747
748 if (!allow_fallback) {
749 tasks_.clear();
750 }
751
752 RunNextTask();
753 }
754
OnDnsTaskComplete(base::TimeTicks start_time,bool allow_fallback,HostCache::Entry results,bool secure)755 void HostResolverManager::Job::OnDnsTaskComplete(base::TimeTicks start_time,
756 bool allow_fallback,
757 HostCache::Entry results,
758 bool secure) {
759 DCHECK(dns_task_);
760
761 // Tasks containing address queries are only considered successful overall
762 // if they find address results. However, DnsTask may claim success if any
763 // transaction, e.g. a supplemental HTTPS transaction, finds results.
764 DCHECK(!key_.query_types.Has(DnsQueryType::UNSPECIFIED));
765 if (HasAddressType(key_.query_types) && results.error() == OK &&
766 results.ip_endpoints().empty()) {
767 results.set_error(ERR_NAME_NOT_RESOLVED);
768 }
769
770 base::TimeDelta duration = tick_clock_->NowTicks() - start_time;
771 if (results.error() != OK) {
772 OnDnsTaskFailure(dns_task_->AsWeakPtr(), duration, allow_fallback, results,
773 secure);
774 return;
775 }
776
777 UMA_HISTOGRAM_LONG_TIMES_100("Net.DNS.DnsTask.SuccessTime", duration);
778
779 UMA_HISTOGRAM_LONG_TIMES_100("Net.DNS.JobQueueTime.Success",
780 total_transaction_time_queued_);
781
782 // Reset the insecure DNS failure counter if an insecure DnsTask completed
783 // successfully.
784 if (!secure) {
785 resolver_->dns_client_->ClearInsecureFallbackFailures();
786 }
787
788 base::TimeDelta bounded_ttl =
789 std::max(results.ttl(), base::Seconds(kMinimumTTLSeconds));
790
791 if (ContainsIcannNameCollisionIp(results.ip_endpoints())) {
792 CompleteRequestsWithError(ERR_ICANN_NAME_COLLISION,
793 secure ? TaskType::SECURE_DNS : TaskType::DNS);
794 return;
795 }
796
797 CompleteRequests(results, bounded_ttl, true /* allow_cache */, secure,
798 secure ? TaskType::SECURE_DNS : TaskType::DNS);
799 }
800
OnIntermediateTransactionsComplete(std::optional<HostResolverDnsTask::SingleTransactionResults> single_transaction_results)801 void HostResolverManager::Job::OnIntermediateTransactionsComplete(
802 std::optional<HostResolverDnsTask::SingleTransactionResults>
803 single_transaction_results) {
804 if (dispatched_) {
805 DCHECK_GE(num_occupied_job_slots_,
806 dns_task_->num_transactions_in_progress());
807 int unused_slots =
808 num_occupied_job_slots_ - dns_task_->num_transactions_in_progress();
809
810 // Reuse vacated slots for any remaining transactions.
811 while (unused_slots > 0 &&
812 dns_task_->num_additional_transactions_needed() > 0) {
813 dns_task_->StartNextTransaction();
814 --unused_slots;
815 }
816
817 // If all remaining transactions found a slot, no more needed from the
818 // dispatcher.
819 if (is_queued() && dns_task_->num_additional_transactions_needed() == 0) {
820 resolver_->dispatcher_->Cancel(handle_);
821 handle_.Reset();
822 }
823
824 // Relinquish any remaining extra slots.
825 while (unused_slots > 0) {
826 ReduceByOneJobSlot();
827 --unused_slots;
828 }
829 } else if (dns_task_->num_additional_transactions_needed() >= 1) {
830 dns_task_->StartNextTransaction();
831 }
832
833 if (dns_task_results_manager_ && single_transaction_results.has_value()) {
834 dns_task_results_manager_->ProcessDnsTransactionResults(
835 single_transaction_results->query_type,
836 single_transaction_results->results);
837 // `this` may be deleted. Do not add code below.
838 }
839 }
840
AddTransactionTimeQueued(base::TimeDelta time_queued)841 void HostResolverManager::Job::AddTransactionTimeQueued(
842 base::TimeDelta time_queued) {
843 total_transaction_time_queued_ += time_queued;
844 }
845
OnServiceEndpointsUpdated()846 void HostResolverManager::Job::OnServiceEndpointsUpdated() {
847 // Requests could be destroyed while executing callbacks. Post tasks
848 // instead of calling callbacks synchronously to prevent requests from being
849 // destroyed in the following for loop.
850 for (auto* request = service_endpoint_requests_.head();
851 request != service_endpoint_requests_.end(); request = request->next()) {
852 base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
853 FROM_HERE,
854 base::BindOnce(&ServiceEndpointRequestImpl::OnServiceEndpointsChanged,
855 request->value()->GetWeakPtr()));
856 }
857 }
858
StartMdnsTask()859 void HostResolverManager::Job::StartMdnsTask() {
860 // No flags are supported for MDNS except
861 // HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6 (which is not actually an
862 // input flag).
863 DCHECK_EQ(0, key_.flags & ~HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6);
864
865 MDnsClient* client = nullptr;
866 int rv = resolver_->GetOrCreateMdnsClient(&client);
867 mdns_task_ = std::make_unique<HostResolverMdnsTask>(
868 client, std::string(key_.host.GetHostnameWithoutBrackets()),
869 key_.query_types);
870
871 if (rv == OK) {
872 mdns_task_->Start(
873 base::BindOnce(&Job::OnMdnsTaskComplete, base::Unretained(this)));
874 } else {
875 // Could not create an mDNS client. Since we cannot complete synchronously
876 // from here, post a failure without starting the task.
877 base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
878 FROM_HERE, base::BindOnce(&Job::OnMdnsImmediateFailure,
879 weak_ptr_factory_.GetWeakPtr(), rv));
880 }
881 }
882
OnMdnsTaskComplete()883 void HostResolverManager::Job::OnMdnsTaskComplete() {
884 DCHECK(mdns_task_);
885 // TODO(crbug.com/846423): Consider adding MDNS-specific logging.
886
887 HostCache::Entry results = mdns_task_->GetResults();
888
889 if (ContainsIcannNameCollisionIp(results.ip_endpoints())) {
890 CompleteRequestsWithError(ERR_ICANN_NAME_COLLISION, TaskType::MDNS);
891 return;
892 }
893 // MDNS uses a separate cache, so skip saving result to cache.
894 // TODO(crbug.com/926300): Consider merging caches.
895 CompleteRequestsWithoutCache(results, std::nullopt /* stale_info */,
896 TaskType::MDNS);
897 }
898
OnMdnsImmediateFailure(int rv)899 void HostResolverManager::Job::OnMdnsImmediateFailure(int rv) {
900 DCHECK(mdns_task_);
901 DCHECK_NE(OK, rv);
902
903 CompleteRequestsWithError(rv, TaskType::MDNS);
904 }
905
StartNat64Task()906 void HostResolverManager::Job::StartNat64Task() {
907 DCHECK(!nat64_task_);
908 nat64_task_ = std::make_unique<HostResolverNat64Task>(
909 key_.host.GetHostnameWithoutBrackets(), key_.network_anonymization_key,
910 net_log_, &*key_.resolve_context, resolver_);
911 nat64_task_->Start(base::BindOnce(&Job::OnNat64TaskComplete,
912 weak_ptr_factory_.GetWeakPtr()));
913 }
914
OnNat64TaskComplete()915 void HostResolverManager::Job::OnNat64TaskComplete() {
916 DCHECK(nat64_task_);
917 HostCache::Entry results = nat64_task_->GetResults();
918 CompleteRequestsWithoutCache(results, std::nullopt /* stale_info */,
919 TaskType::NAT64);
920 }
921
RecordJobHistograms(const HostCache::Entry & results,std::optional<TaskType> task_type)922 void HostResolverManager::Job::RecordJobHistograms(
923 const HostCache::Entry& results,
924 std::optional<TaskType> task_type) {
925 int error = results.error();
926 // Used in UMA_HISTOGRAM_ENUMERATION. Do not renumber entries or reuse
927 // deprecated values.
928 enum Category {
929 RESOLVE_SUCCESS = 0,
930 RESOLVE_FAIL = 1,
931 RESOLVE_SPECULATIVE_SUCCESS = 2,
932 RESOLVE_SPECULATIVE_FAIL = 3,
933 RESOLVE_ABORT = 4,
934 RESOLVE_SPECULATIVE_ABORT = 5,
935 RESOLVE_MAX, // Bounding value.
936 };
937 Category category = RESOLVE_MAX; // Illegal value for later DCHECK only.
938
939 base::TimeDelta duration = tick_clock_->NowTicks() - start_time_;
940 if (error == OK) {
941 if (had_non_speculative_request_) {
942 category = RESOLVE_SUCCESS;
943 UMA_HISTOGRAM_LONG_TIMES_100("Net.DNS.ResolveSuccessTime", duration);
944 } else {
945 category = RESOLVE_SPECULATIVE_SUCCESS;
946 }
947 } else if (error == ERR_NETWORK_CHANGED ||
948 error == ERR_HOST_RESOLVER_QUEUE_TOO_LARGE) {
949 category = had_non_speculative_request_ ? RESOLVE_ABORT
950 : RESOLVE_SPECULATIVE_ABORT;
951 } else {
952 if (had_non_speculative_request_) {
953 category = RESOLVE_FAIL;
954 UMA_HISTOGRAM_LONG_TIMES_100("Net.DNS.ResolveFailureTime", duration);
955 } else {
956 category = RESOLVE_SPECULATIVE_FAIL;
957 }
958 }
959 DCHECK_LT(static_cast<int>(category),
960 static_cast<int>(RESOLVE_MAX)); // Be sure it was set.
961 UMA_HISTOGRAM_ENUMERATION("Net.DNS.ResolveCategory", category, RESOLVE_MAX);
962
963 if (category == RESOLVE_FAIL ||
964 (start_time_ != base::TimeTicks() && category == RESOLVE_ABORT)) {
965 if (duration < base::Milliseconds(10)) {
966 base::UmaHistogramSparse("Net.DNS.ResolveError.Fast", std::abs(error));
967 } else {
968 base::UmaHistogramSparse("Net.DNS.ResolveError.Slow", std::abs(error));
969 }
970 }
971
972 if (error == OK) {
973 DCHECK(task_type.has_value());
974 // Record, for HTTPS-capable queries to a host known to serve HTTPS
975 // records, whether the HTTPS record was successfully received.
976 if (key_.query_types.Has(DnsQueryType::HTTPS) &&
977 // Skip http- and ws-schemed hosts. Although they query HTTPS records,
978 // successful queries are reported as errors, which would skew the
979 // metrics.
980 IsSchemeHttpsOrWss(key_.host) &&
981 IsGoogleHostWithAlpnH3(key_.host.GetHostnameWithoutBrackets())) {
982 bool has_metadata = !results.GetMetadatas().empty();
983 base::UmaHistogramExactLinear(
984 "Net.DNS.H3SupportedGoogleHost.TaskTypeMetadataAvailability2",
985 static_cast<int>(task_type.value()) * 2 + (has_metadata ? 1 : 0),
986 (static_cast<int>(TaskType::kMaxValue) + 1) * 2);
987 }
988 }
989 }
990
MaybeCacheResult(const HostCache::Entry & results,base::TimeDelta ttl,bool secure)991 void HostResolverManager::Job::MaybeCacheResult(const HostCache::Entry& results,
992 base::TimeDelta ttl,
993 bool secure) {
994 // If the request did not complete, don't cache it.
995 if (!results.did_complete()) {
996 return;
997 }
998 resolver_->CacheResult(host_cache_, key_.ToCacheKey(secure), results, ttl);
999 }
1000
CompleteRequests(const HostCache::Entry & results,base::TimeDelta ttl,bool allow_cache,bool secure,std::optional<TaskType> task_type)1001 void HostResolverManager::Job::CompleteRequests(
1002 const HostCache::Entry& results,
1003 base::TimeDelta ttl,
1004 bool allow_cache,
1005 bool secure,
1006 std::optional<TaskType> task_type) {
1007 CHECK(resolver_.get());
1008
1009 // This job must be removed from resolver's |jobs_| now to make room for a
1010 // new job with the same key in case one of the OnComplete callbacks decides
1011 // to spawn one. Consequently, if the job was owned by |jobs_|, the job
1012 // deletes itself when CompleteRequests is done.
1013 std::unique_ptr<Job> self_deleter;
1014 if (self_iterator_) {
1015 self_deleter = resolver_->RemoveJob(self_iterator_.value());
1016 }
1017
1018 Finish();
1019
1020 if (results.error() == ERR_DNS_REQUEST_CANCELLED) {
1021 net_log_.AddEvent(NetLogEventType::CANCELLED);
1022 net_log_.EndEventWithNetErrorCode(
1023 NetLogEventType::HOST_RESOLVER_MANAGER_JOB, OK);
1024 return;
1025 }
1026
1027 net_log_.EndEventWithNetErrorCode(NetLogEventType::HOST_RESOLVER_MANAGER_JOB,
1028 results.error());
1029
1030 // Handle all caching before completing requests as completing requests may
1031 // start new requests that rely on cached results.
1032 if (allow_cache) {
1033 MaybeCacheResult(results, ttl, secure);
1034 }
1035
1036 RecordJobHistograms(results, task_type);
1037
1038 // Complete all of the requests that were attached to the job and
1039 // detach them.
1040 while (!requests_.empty()) {
1041 RequestImpl* req = requests_.head()->value();
1042 req->RemoveFromList();
1043 CHECK(key_ == req->GetJobKey());
1044
1045 if (results.error() == OK && !req->parameters().is_speculative) {
1046 req->set_results(
1047 results.CopyWithDefaultPort(req->request_host().GetPort()));
1048 }
1049 req->OnJobCompleted(
1050 key_, results.error(),
1051 /*is_secure_network_error=*/secure && results.error() != OK);
1052
1053 // Check if the resolver was destroyed as a result of running the
1054 // callback. If it was, we could continue, but we choose to bail.
1055 if (!resolver_.get()) {
1056 return;
1057 }
1058 }
1059
1060 while (!service_endpoint_requests_.empty()) {
1061 ServiceEndpointRequestImpl* request =
1062 service_endpoint_requests_.head()->value();
1063 request->RemoveFromList();
1064 request->OnJobCompleted(results, secure);
1065 if (!resolver_.get()) {
1066 return;
1067 }
1068 }
1069
1070 // TODO(crbug.com/1200908): Call StartBootstrapFollowup() if any of the
1071 // requests have the Bootstrap policy. Note: A naive implementation could
1072 // cause an infinite loop if the bootstrap result has TTL=0.
1073 }
1074
CompleteRequestsWithoutCache(const HostCache::Entry & results,std::optional<HostCache::EntryStaleness> stale_info,TaskType task_type)1075 void HostResolverManager::Job::CompleteRequestsWithoutCache(
1076 const HostCache::Entry& results,
1077 std::optional<HostCache::EntryStaleness> stale_info,
1078 TaskType task_type) {
1079 // Record the stale_info for all non-speculative requests, if it exists.
1080 if (stale_info) {
1081 for (auto* node = requests_.head(); node != requests_.end();
1082 node = node->next()) {
1083 if (!node->value()->parameters().is_speculative) {
1084 node->value()->set_stale_info(stale_info.value());
1085 }
1086 }
1087 }
1088 CompleteRequests(results, base::TimeDelta(), false /* allow_cache */,
1089 false /* secure */, task_type);
1090 }
1091
CompleteRequestsWithError(int net_error,std::optional<TaskType> task_type)1092 void HostResolverManager::Job::CompleteRequestsWithError(
1093 int net_error,
1094 std::optional<TaskType> task_type) {
1095 DCHECK_NE(OK, net_error);
1096 CompleteRequests(
1097 HostCache::Entry(net_error, HostCache::Entry::SOURCE_UNKNOWN),
1098 base::TimeDelta(), true /* allow_cache */, false /* secure */, task_type);
1099 }
1100
priority() const1101 RequestPriority HostResolverManager::Job::priority() const {
1102 return priority_tracker_.highest_priority();
1103 }
1104
1105 } // namespace net
1106