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_dns_task.h"
6
7 #include <string_view>
8
9 #include "base/metrics/histogram_functions.h"
10 #include "base/metrics/histogram_macros.h"
11 #include "base/ranges/algorithm.h"
12 #include "base/time/tick_clock.h"
13 #include "net/base/features.h"
14 #include "net/dns/address_sorter.h"
15 #include "net/dns/dns_client.h"
16 #include "net/dns/dns_names_util.h"
17 #include "net/dns/dns_response.h"
18 #include "net/dns/dns_transaction.h"
19 #include "net/dns/dns_util.h"
20 #include "net/dns/host_resolver.h"
21 #include "net/dns/host_resolver_cache.h"
22 #include "net/dns/host_resolver_internal_result.h"
23 #include "net/dns/public/util.h"
24 #include "third_party/abseil-cpp/absl/types/variant.h"
25
26 namespace net {
27
28 namespace {
29
CreateFakeEmptyResponse(std::string_view hostname,DnsQueryType query_type)30 DnsResponse CreateFakeEmptyResponse(std::string_view hostname,
31 DnsQueryType query_type) {
32 std::optional<std::vector<uint8_t>> qname =
33 dns_names_util::DottedNameToNetwork(
34 hostname, /*require_valid_internet_hostname=*/true);
35 CHECK(qname.has_value());
36 return DnsResponse::CreateEmptyNoDataResponse(
37 /*id=*/0u, /*is_authoritative=*/true, qname.value(),
38 DnsQueryTypeToQtype(query_type));
39 }
40
NetLogDnsTaskExtractionFailureParams(DnsResponseResultExtractor::ExtractionError extraction_error,DnsQueryType dns_query_type)41 base::Value::Dict NetLogDnsTaskExtractionFailureParams(
42 DnsResponseResultExtractor::ExtractionError extraction_error,
43 DnsQueryType dns_query_type) {
44 base::Value::Dict dict;
45 dict.Set("extraction_error", base::strict_cast<int>(extraction_error));
46 dict.Set("dns_query_type", kDnsQueryTypes.at(dns_query_type));
47 return dict;
48 }
49
50 // Creates NetLog parameters when the DnsTask failed.
NetLogDnsTaskFailedParams(int net_error,std::optional<DnsQueryType> failed_transaction_type,std::optional<base::TimeDelta> ttl,const HostCache::Entry * saved_results)51 base::Value::Dict NetLogDnsTaskFailedParams(
52 int net_error,
53 std::optional<DnsQueryType> failed_transaction_type,
54 std::optional<base::TimeDelta> ttl,
55 const HostCache::Entry* saved_results) {
56 base::Value::Dict dict;
57 if (failed_transaction_type) {
58 dict.Set("dns_query_type", kDnsQueryTypes.at(*failed_transaction_type));
59 }
60 if (ttl) {
61 dict.Set("error_ttl_sec",
62 base::saturated_cast<int>(ttl.value().InSeconds()));
63 }
64 dict.Set("net_error", net_error);
65 if (saved_results) {
66 dict.Set("saved_results", saved_results->NetLogParams());
67 }
68 return dict;
69 }
70
NetLogResults(const HostCache::Entry & results)71 base::Value::Dict NetLogResults(const HostCache::Entry& results) {
72 base::Value::Dict dict;
73 dict.Set("results", results.NetLogParams());
74 return dict;
75 }
76
RecordResolveTimeDiffForBucket(const char * histogram_variant,const char * histogram_bucket,base::TimeDelta diff)77 void RecordResolveTimeDiffForBucket(const char* histogram_variant,
78 const char* histogram_bucket,
79 base::TimeDelta diff) {
80 base::UmaHistogramTimes(
81 base::StrCat({"Net.Dns.ResolveTimeDiff.", histogram_variant,
82 ".FirstRecord", histogram_bucket}),
83 diff);
84 }
85
RecordResolveTimeDiff(const char * histogram_variant,base::TimeTicks start_time,base::TimeTicks first_record_end_time,base::TimeTicks second_record_end_time)86 void RecordResolveTimeDiff(const char* histogram_variant,
87 base::TimeTicks start_time,
88 base::TimeTicks first_record_end_time,
89 base::TimeTicks second_record_end_time) {
90 CHECK_LE(start_time, first_record_end_time);
91 CHECK_LE(first_record_end_time, second_record_end_time);
92 base::TimeDelta first_elapsed = first_record_end_time - start_time;
93 base::TimeDelta diff = second_record_end_time - first_record_end_time;
94
95 if (first_elapsed < base::Milliseconds(10)) {
96 RecordResolveTimeDiffForBucket(histogram_variant, "FasterThan10ms", diff);
97 } else if (first_elapsed < base::Milliseconds(25)) {
98 RecordResolveTimeDiffForBucket(histogram_variant, "10msTo25ms", diff);
99 } else if (first_elapsed < base::Milliseconds(50)) {
100 RecordResolveTimeDiffForBucket(histogram_variant, "25msTo50ms", diff);
101 } else if (first_elapsed < base::Milliseconds(100)) {
102 RecordResolveTimeDiffForBucket(histogram_variant, "50msTo100ms", diff);
103 } else if (first_elapsed < base::Milliseconds(250)) {
104 RecordResolveTimeDiffForBucket(histogram_variant, "100msTo250ms", diff);
105 } else if (first_elapsed < base::Milliseconds(500)) {
106 RecordResolveTimeDiffForBucket(histogram_variant, "250msTo500ms", diff);
107 } else if (first_elapsed < base::Seconds(1)) {
108 RecordResolveTimeDiffForBucket(histogram_variant, "500msTo1s", diff);
109 } else {
110 RecordResolveTimeDiffForBucket(histogram_variant, "SlowerThan1s", diff);
111 }
112 }
113
114 } // namespace
115
SingleTransactionResults(DnsQueryType query_type,Results results)116 HostResolverDnsTask::SingleTransactionResults::SingleTransactionResults(
117 DnsQueryType query_type,
118 Results results)
119 : query_type(query_type), results(std::move(results)) {}
120
121 HostResolverDnsTask::SingleTransactionResults::~SingleTransactionResults() =
122 default;
123
124 HostResolverDnsTask::SingleTransactionResults::SingleTransactionResults(
125 SingleTransactionResults&&) = default;
126
127 HostResolverDnsTask::SingleTransactionResults&
128 HostResolverDnsTask::SingleTransactionResults::operator=(
129 SingleTransactionResults&&) = default;
130
TransactionInfo(DnsQueryType type,TransactionErrorBehavior error_behavior)131 HostResolverDnsTask::TransactionInfo::TransactionInfo(
132 DnsQueryType type,
133 TransactionErrorBehavior error_behavior)
134 : type(type), error_behavior(error_behavior) {}
135
136 HostResolverDnsTask::TransactionInfo::~TransactionInfo() = default;
137
138 HostResolverDnsTask::TransactionInfo::TransactionInfo(
139 HostResolverDnsTask::TransactionInfo&& other) = default;
140
141 HostResolverDnsTask::TransactionInfo&
142 HostResolverDnsTask::TransactionInfo::operator=(
143 HostResolverDnsTask::TransactionInfo&& other) = default;
144
operator <(const HostResolverDnsTask::TransactionInfo & other) const145 bool HostResolverDnsTask::TransactionInfo::operator<(
146 const HostResolverDnsTask::TransactionInfo& other) const {
147 return std::tie(type, error_behavior, transaction) <
148 std::tie(other.type, other.error_behavior, other.transaction);
149 }
150
HostResolverDnsTask(DnsClient * client,HostResolver::Host host,NetworkAnonymizationKey anonymization_key,DnsQueryTypeSet query_types,ResolveContext * resolve_context,bool secure,SecureDnsMode secure_dns_mode,Delegate * delegate,const NetLogWithSource & job_net_log,const base::TickClock * tick_clock,bool fallback_available,const HostResolver::HttpsSvcbOptions & https_svcb_options)151 HostResolverDnsTask::HostResolverDnsTask(
152 DnsClient* client,
153 HostResolver::Host host,
154 NetworkAnonymizationKey anonymization_key,
155 DnsQueryTypeSet query_types,
156 ResolveContext* resolve_context,
157 bool secure,
158 SecureDnsMode secure_dns_mode,
159 Delegate* delegate,
160 const NetLogWithSource& job_net_log,
161 const base::TickClock* tick_clock,
162 bool fallback_available,
163 const HostResolver::HttpsSvcbOptions& https_svcb_options)
164 : client_(client),
165 host_(std::move(host)),
166 anonymization_key_(std::move(anonymization_key)),
167 resolve_context_(resolve_context->AsSafeRef()),
168 secure_(secure),
169 secure_dns_mode_(secure_dns_mode),
170 delegate_(delegate),
171 net_log_(job_net_log),
172 tick_clock_(tick_clock),
173 task_start_time_(tick_clock_->NowTicks()),
174 fallback_available_(fallback_available),
175 https_svcb_options_(https_svcb_options) {
176 DCHECK(client_);
177 DCHECK(delegate_);
178
179 if (!secure_) {
180 DCHECK(client_->CanUseInsecureDnsTransactions());
181 }
182
183 PushTransactionsNeeded(MaybeDisableAdditionalQueries(query_types));
184 }
185
186 HostResolverDnsTask::~HostResolverDnsTask() = default;
187
StartNextTransaction()188 void HostResolverDnsTask::StartNextTransaction() {
189 DCHECK_GE(num_additional_transactions_needed(), 1);
190
191 if (!any_transaction_started_) {
192 net_log_.BeginEvent(NetLogEventType::HOST_RESOLVER_DNS_TASK,
193 [&] { return NetLogDnsTaskCreationParams(); });
194 }
195 any_transaction_started_ = true;
196
197 TransactionInfo transaction_info = std::move(transactions_needed_.front());
198 transactions_needed_.pop_front();
199
200 DCHECK(IsAddressType(transaction_info.type) || secure_ ||
201 client_->CanQueryAdditionalTypesViaInsecureDns());
202
203 // Record how long this transaction has been waiting to be created.
204 base::TimeDelta time_queued = tick_clock_->NowTicks() - task_start_time_;
205 UMA_HISTOGRAM_LONG_TIMES_100("Net.DNS.JobQueueTime.PerTransaction",
206 time_queued);
207 delegate_->AddTransactionTimeQueued(time_queued);
208
209 CreateAndStartTransaction(std::move(transaction_info));
210 }
211
NetLogDnsTaskCreationParams()212 base::Value::Dict HostResolverDnsTask::NetLogDnsTaskCreationParams() {
213 base::Value::Dict dict;
214 dict.Set("secure", secure());
215
216 base::Value::List transactions_needed_value;
217 for (const TransactionInfo& info : transactions_needed_) {
218 base::Value::Dict transaction_dict;
219 transaction_dict.Set("dns_query_type", kDnsQueryTypes.at(info.type));
220 transactions_needed_value.Append(std::move(transaction_dict));
221 }
222 dict.Set("transactions_needed", std::move(transactions_needed_value));
223
224 return dict;
225 }
226
NetLogDnsTaskTimeoutParams()227 base::Value::Dict HostResolverDnsTask::NetLogDnsTaskTimeoutParams() {
228 base::Value::Dict dict;
229
230 if (!transactions_in_progress_.empty()) {
231 base::Value::List list;
232 for (const TransactionInfo& info : transactions_in_progress_) {
233 base::Value::Dict transaction_dict;
234 transaction_dict.Set("dns_query_type", kDnsQueryTypes.at(info.type));
235 list.Append(std::move(transaction_dict));
236 }
237 dict.Set("started_transactions", std::move(list));
238 }
239
240 if (!transactions_needed_.empty()) {
241 base::Value::List list;
242 for (const TransactionInfo& info : transactions_needed_) {
243 base::Value::Dict transaction_dict;
244 transaction_dict.Set("dns_query_type", kDnsQueryTypes.at(info.type));
245 list.Append(std::move(transaction_dict));
246 }
247 dict.Set("queued_transactions", std::move(list));
248 }
249
250 return dict;
251 }
252
MaybeDisableAdditionalQueries(DnsQueryTypeSet types)253 DnsQueryTypeSet HostResolverDnsTask::MaybeDisableAdditionalQueries(
254 DnsQueryTypeSet types) {
255 DCHECK(!types.empty());
256 DCHECK(!types.Has(DnsQueryType::UNSPECIFIED));
257
258 // No-op if the caller explicitly requested this one query type.
259 if (types.size() == 1) {
260 return types;
261 }
262
263 if (types.Has(DnsQueryType::HTTPS)) {
264 if (!secure_ && !client_->CanQueryAdditionalTypesViaInsecureDns()) {
265 types.Remove(DnsQueryType::HTTPS);
266 } else {
267 DCHECK(!httpssvc_metrics_);
268 httpssvc_metrics_.emplace(secure_);
269 }
270 }
271 DCHECK(!types.empty());
272 return types;
273 }
274
PushTransactionsNeeded(DnsQueryTypeSet query_types)275 void HostResolverDnsTask::PushTransactionsNeeded(DnsQueryTypeSet query_types) {
276 DCHECK(transactions_needed_.empty());
277
278 if (query_types.Has(DnsQueryType::HTTPS) &&
279 features::kUseDnsHttpsSvcbEnforceSecureResponse.Get() && secure_) {
280 query_types.Remove(DnsQueryType::HTTPS);
281 transactions_needed_.emplace_back(DnsQueryType::HTTPS,
282 TransactionErrorBehavior::kFatalOrEmpty);
283 }
284
285 // Give AAAA/A queries a head start by pushing them to the queue first.
286 constexpr DnsQueryType kHighPriorityQueries[] = {DnsQueryType::AAAA,
287 DnsQueryType::A};
288 for (DnsQueryType high_priority_query : kHighPriorityQueries) {
289 if (query_types.Has(high_priority_query)) {
290 query_types.Remove(high_priority_query);
291 transactions_needed_.emplace_back(high_priority_query);
292 }
293 }
294 for (DnsQueryType remaining_query : query_types) {
295 if (remaining_query == DnsQueryType::HTTPS) {
296 // Ignore errors for these types. In most cases treating them normally
297 // would only result in fallback to resolution without querying the
298 // type. Instead, synthesize empty results.
299 transactions_needed_.emplace_back(
300 remaining_query, TransactionErrorBehavior::kSynthesizeEmpty);
301 } else {
302 transactions_needed_.emplace_back(remaining_query);
303 }
304 }
305 }
306
CreateAndStartTransaction(TransactionInfo transaction_info)307 void HostResolverDnsTask::CreateAndStartTransaction(
308 TransactionInfo transaction_info) {
309 DCHECK(!transaction_info.transaction);
310 DCHECK_NE(DnsQueryType::UNSPECIFIED, transaction_info.type);
311
312 std::string transaction_hostname(host_.GetHostnameWithoutBrackets());
313
314 // For HTTPS, prepend "_<port>._https." for any non-default port.
315 uint16_t request_port = 0;
316 if (transaction_info.type == DnsQueryType::HTTPS && host_.HasScheme()) {
317 const auto& scheme_host_port = host_.AsSchemeHostPort();
318 transaction_hostname =
319 dns_util::GetNameForHttpsQuery(scheme_host_port, &request_port);
320 }
321
322 transaction_info.transaction =
323 client_->GetTransactionFactory()->CreateTransaction(
324 std::move(transaction_hostname),
325 DnsQueryTypeToQtype(transaction_info.type), net_log_, secure_,
326 secure_dns_mode_, &*resolve_context_,
327 fallback_available_ /* fast_timeout */);
328 transaction_info.transaction->SetRequestPriority(delegate_->priority());
329
330 auto transaction_info_it =
331 transactions_in_progress_.insert(std::move(transaction_info)).first;
332
333 // Safe to pass `transaction_info_it` because it is only modified/removed
334 // after async completion of this call or by destruction (which cancels the
335 // transaction and prevents callback because it owns the `DnsTransaction`
336 // object).
337 transaction_info_it->transaction->Start(base::BindOnce(
338 &HostResolverDnsTask::OnDnsTransactionComplete, base::Unretained(this),
339 transaction_info_it, request_port));
340 }
341
OnTimeout()342 void HostResolverDnsTask::OnTimeout() {
343 net_log_.AddEvent(NetLogEventType::HOST_RESOLVER_DNS_TASK_TIMEOUT,
344 [&] { return NetLogDnsTaskTimeoutParams(); });
345
346 for (const TransactionInfo& transaction : transactions_in_progress_) {
347 base::TimeDelta elapsed_time = tick_clock_->NowTicks() - task_start_time_;
348
349 switch (transaction.type) {
350 case DnsQueryType::HTTPS:
351 DCHECK(!secure_ ||
352 !features::kUseDnsHttpsSvcbEnforceSecureResponse.Get());
353 if (httpssvc_metrics_) {
354 // Don't record provider ID for timeouts. It is not precisely known
355 // at this level which provider is actually to blame for the
356 // timeout, and breaking metrics out by provider is no longer
357 // important for current experimentation goals.
358 httpssvc_metrics_->SaveForHttps(HttpssvcDnsRcode::kTimedOut,
359 /*condensed_records=*/{},
360 elapsed_time);
361 }
362 break;
363 default:
364 // The timeout timer is only started when all other transactions have
365 // completed.
366 NOTREACHED();
367 }
368 }
369
370 // Clear in-progress and scheduled transactions so that
371 // OnTransactionsFinished() doesn't call delegate's
372 // OnIntermediateTransactionComplete().
373 transactions_needed_.clear();
374 transactions_in_progress_.clear();
375
376 OnTransactionsFinished(/*single_transaction_results=*/std::nullopt);
377 }
378
OnDnsTransactionComplete(std::set<TransactionInfo>::iterator transaction_info_it,uint16_t request_port,int net_error,const DnsResponse * response)379 void HostResolverDnsTask::OnDnsTransactionComplete(
380 std::set<TransactionInfo>::iterator transaction_info_it,
381 uint16_t request_port,
382 int net_error,
383 const DnsResponse* response) {
384 DCHECK(transaction_info_it != transactions_in_progress_.end());
385 DCHECK(base::Contains(transactions_in_progress_, *transaction_info_it));
386
387 // Pull the TransactionInfo out of `transactions_in_progress_` now, so it
388 // and its underlying DnsTransaction will be deleted on completion of
389 // OnTransactionComplete. Note: Once control leaves OnTransactionComplete,
390 // there's no further need for the transaction object. On the other hand,
391 // since it owns `*response`, it should stay around while
392 // OnTransactionComplete executes.
393 TransactionInfo transaction_info =
394 std::move(transactions_in_progress_.extract(transaction_info_it).value());
395
396 const base::TimeTicks now = tick_clock_->NowTicks();
397 base::TimeDelta elapsed_time = now - task_start_time_;
398 enum HttpssvcDnsRcode rcode_for_httpssvc = HttpssvcDnsRcode::kNoError;
399 if (httpssvc_metrics_) {
400 if (net_error == ERR_DNS_TIMED_OUT) {
401 rcode_for_httpssvc = HttpssvcDnsRcode::kTimedOut;
402 } else if (net_error == ERR_NAME_NOT_RESOLVED) {
403 rcode_for_httpssvc = HttpssvcDnsRcode::kNoError;
404 } else if (response == nullptr) {
405 rcode_for_httpssvc = HttpssvcDnsRcode::kMissingDnsResponse;
406 } else {
407 rcode_for_httpssvc =
408 TranslateDnsRcodeForHttpssvcExperiment(response->rcode());
409 }
410 }
411
412 // Handle network errors. Note that for NXDOMAIN, DnsTransaction returns
413 // ERR_NAME_NOT_RESOLVED, so that is not a network error if received with a
414 // valid response.
415 bool fatal_error =
416 IsFatalTransactionFailure(net_error, transaction_info, response);
417 std::optional<DnsResponse> fake_response;
418 if (net_error != OK && !(net_error == ERR_NAME_NOT_RESOLVED && response &&
419 response->IsValid())) {
420 if (transaction_info.error_behavior ==
421 TransactionErrorBehavior::kFallback ||
422 fatal_error) {
423 // Fail task (or maybe Job) completely on network failure.
424 OnFailure(net_error, /*allow_fallback=*/!fatal_error,
425 /*ttl=*/std::nullopt, transaction_info.type);
426 return;
427 } else {
428 DCHECK((transaction_info.error_behavior ==
429 TransactionErrorBehavior::kFatalOrEmpty &&
430 !fatal_error) ||
431 transaction_info.error_behavior ==
432 TransactionErrorBehavior::kSynthesizeEmpty);
433 // For non-fatal failures, synthesize an empty response.
434 fake_response = CreateFakeEmptyResponse(
435 host_.GetHostnameWithoutBrackets(), transaction_info.type);
436 response = &fake_response.value();
437 }
438 }
439
440 DCHECK(response);
441
442 DnsResponseResultExtractor::ResultsOrError results;
443 {
444 // Scope the extractor to ensure it is destroyed before `response`.
445 DnsResponseResultExtractor extractor(*response);
446 results = extractor.ExtractDnsResults(
447 transaction_info.type,
448 /*original_domain_name=*/host_.GetHostnameWithoutBrackets(),
449 request_port);
450 }
451
452 DCHECK_NE(results.error_or(DnsResponseResultExtractor::ExtractionError::kOk),
453 DnsResponseResultExtractor::ExtractionError::kUnexpected);
454
455 if (!results.has_value()) {
456 net_log_.AddEvent(
457 NetLogEventType::HOST_RESOLVER_DNS_TASK_EXTRACTION_FAILURE, [&] {
458 return NetLogDnsTaskExtractionFailureParams(results.error(),
459 transaction_info.type);
460 });
461 if (transaction_info.error_behavior ==
462 TransactionErrorBehavior::kFatalOrEmpty ||
463 transaction_info.error_behavior ==
464 TransactionErrorBehavior::kSynthesizeEmpty) {
465 // No extraction errors are currently considered fatal, otherwise, there
466 // would need to be a call to some sort of
467 // IsFatalTransactionExtractionError() function.
468 DCHECK(!fatal_error);
469 DCHECK_EQ(transaction_info.type, DnsQueryType::HTTPS);
470 results = Results();
471 } else {
472 OnFailure(ERR_DNS_MALFORMED_RESPONSE, /*allow_fallback=*/true,
473 /*ttl=*/std::nullopt, transaction_info.type);
474 return;
475 }
476 }
477 CHECK(results.has_value());
478 net_log_.AddEvent(NetLogEventType::HOST_RESOLVER_DNS_TASK_EXTRACTION_RESULTS,
479 [&] {
480 base::Value::List list;
481 list.reserve(results.value().size());
482 for (const auto& result : results.value()) {
483 list.Append(result->ToValue());
484 }
485 base::Value::Dict dict;
486 dict.Set("results", std::move(list));
487 return dict;
488 });
489
490 if (httpssvc_metrics_) {
491 if (transaction_info.type == DnsQueryType::HTTPS) {
492 bool has_compatible_https = base::ranges::any_of(
493 results.value(),
494 [](const std::unique_ptr<HostResolverInternalResult>& result) {
495 return result->type() ==
496 HostResolverInternalResult::Type::kMetadata;
497 });
498 if (has_compatible_https) {
499 httpssvc_metrics_->SaveForHttps(rcode_for_httpssvc,
500 std::vector<bool>{true}, elapsed_time);
501 } else {
502 httpssvc_metrics_->SaveForHttps(rcode_for_httpssvc, std::vector<bool>(),
503 elapsed_time);
504 }
505 } else {
506 httpssvc_metrics_->SaveForAddressQuery(elapsed_time, rcode_for_httpssvc);
507 }
508 }
509
510 switch (transaction_info.type) {
511 case DnsQueryType::A:
512 a_record_end_time_ = now;
513 if (!aaaa_record_end_time_.is_null()) {
514 RecordResolveTimeDiff("AAAABeforeA", task_start_time_,
515 aaaa_record_end_time_, a_record_end_time_);
516 }
517 break;
518 case DnsQueryType::AAAA:
519 aaaa_record_end_time_ = now;
520 if (!a_record_end_time_.is_null()) {
521 RecordResolveTimeDiff("ABeforeAAAA", task_start_time_,
522 a_record_end_time_, aaaa_record_end_time_);
523 }
524 break;
525 case DnsQueryType::HTTPS: {
526 base::TimeTicks first_address_end_time =
527 std::min(a_record_end_time_, aaaa_record_end_time_);
528 if (!first_address_end_time.is_null()) {
529 RecordResolveTimeDiff("AddressRecordBeforeHTTPS", task_start_time_,
530 first_address_end_time, now);
531 }
532 break;
533 }
534 default:
535 break;
536 }
537
538 if (base::FeatureList::IsEnabled(features::kUseHostResolverCache) ||
539 base::FeatureList::IsEnabled(features::kUseServiceEndpointRequest)) {
540 SortTransactionAndHandleResults(std::move(transaction_info),
541 std::move(results).value());
542 } else {
543 HandleTransactionResults(std::move(transaction_info),
544 std::move(results).value());
545 }
546 }
547
IsFatalTransactionFailure(int transaction_error,const TransactionInfo & transaction_info,const DnsResponse * response)548 bool HostResolverDnsTask::IsFatalTransactionFailure(
549 int transaction_error,
550 const TransactionInfo& transaction_info,
551 const DnsResponse* response) {
552 if (transaction_info.type != DnsQueryType::HTTPS) {
553 DCHECK(transaction_info.error_behavior !=
554 TransactionErrorBehavior::kFatalOrEmpty);
555 return false;
556 }
557
558 // These values are logged to UMA. Entries should not be renumbered and
559 // numeric values should never be reused. Please keep in sync with
560 // "DNS.SvcbHttpsTransactionError" in
561 // src/tools/metrics/histograms/enums.xml.
562 enum class HttpsTransactionError {
563 kNoError = 0,
564 kInsecureError = 1,
565 kNonFatalError = 2,
566 kFatalErrorDisabled = 3,
567 kFatalErrorEnabled = 4,
568 kMaxValue = kFatalErrorEnabled
569 } error;
570
571 if (transaction_error == OK || (transaction_error == ERR_NAME_NOT_RESOLVED &&
572 response && response->IsValid())) {
573 error = HttpsTransactionError::kNoError;
574 } else if (!secure_) {
575 // HTTPS failures are never fatal via insecure DNS.
576 DCHECK(transaction_info.error_behavior !=
577 TransactionErrorBehavior::kFatalOrEmpty);
578 error = HttpsTransactionError::kInsecureError;
579 } else if (transaction_error == ERR_DNS_SERVER_FAILED && response &&
580 response->rcode() != dns_protocol::kRcodeSERVFAIL) {
581 // For server failures, only SERVFAIL is fatal.
582 error = HttpsTransactionError::kNonFatalError;
583 } else if (features::kUseDnsHttpsSvcbEnforceSecureResponse.Get()) {
584 DCHECK(transaction_info.error_behavior ==
585 TransactionErrorBehavior::kFatalOrEmpty);
586 error = HttpsTransactionError::kFatalErrorEnabled;
587 } else {
588 DCHECK(transaction_info.error_behavior !=
589 TransactionErrorBehavior::kFatalOrEmpty);
590 error = HttpsTransactionError::kFatalErrorDisabled;
591 }
592
593 UMA_HISTOGRAM_ENUMERATION("Net.DNS.DnsTask.SvcbHttpsTransactionError", error);
594 return error == HttpsTransactionError::kFatalErrorEnabled;
595 }
596
SortTransactionAndHandleResults(TransactionInfo transaction_info,Results transaction_results)597 void HostResolverDnsTask::SortTransactionAndHandleResults(
598 TransactionInfo transaction_info,
599 Results transaction_results) {
600 // Expect at most 1 data result in an individual transaction.
601 CHECK_LE(base::ranges::count_if(
602 transaction_results,
603 [](const std::unique_ptr<HostResolverInternalResult>& result) {
604 return result->type() ==
605 HostResolverInternalResult::Type::kData;
606 }),
607 1);
608
609 auto data_result_it = base::ranges::find_if(
610 transaction_results,
611 [](const std::unique_ptr<HostResolverInternalResult>& result) {
612 return result->type() == HostResolverInternalResult::Type::kData;
613 });
614
615 std::vector<IPEndPoint> endpoints_to_sort;
616 if (data_result_it != transaction_results.end()) {
617 const HostResolverInternalDataResult& data_result =
618 (*data_result_it)->AsData();
619 endpoints_to_sort.insert(endpoints_to_sort.end(),
620 data_result.endpoints().begin(),
621 data_result.endpoints().end());
622 }
623
624 if (!endpoints_to_sort.empty()) {
625 // More async work to do, so insert `transaction_info` back onto
626 // `transactions_in_progress_`.
627 auto insertion_result =
628 transactions_in_progress_.insert(std::move(transaction_info));
629 CHECK(insertion_result.second);
630
631 // Sort() potentially calls OnTransactionSorted() synchronously.
632 client_->GetAddressSorter()->Sort(
633 endpoints_to_sort,
634 base::BindOnce(&HostResolverDnsTask::OnTransactionSorted, AsWeakPtr(),
635 insertion_result.first, std::move(transaction_results)));
636 } else {
637 HandleTransactionResults(std::move(transaction_info),
638 std::move(transaction_results));
639 }
640 }
641
OnTransactionSorted(std::set<TransactionInfo>::iterator transaction_info_it,Results transaction_results,bool success,std::vector<IPEndPoint> sorted)642 void HostResolverDnsTask::OnTransactionSorted(
643 std::set<TransactionInfo>::iterator transaction_info_it,
644 Results transaction_results,
645 bool success,
646 std::vector<IPEndPoint> sorted) {
647 CHECK(transaction_info_it != transactions_in_progress_.end());
648
649 if (transactions_in_progress_.find(*transaction_info_it) ==
650 transactions_in_progress_.end()) {
651 // If no longer in `transactions_in_progress_`, transaction was cancelled.
652 // Do nothing.
653 return;
654 }
655 TransactionInfo transaction_info =
656 std::move(transactions_in_progress_.extract(transaction_info_it).value());
657
658 // Expect exactly one data result.
659 auto data_result_it = base::ranges::find_if(
660 transaction_results,
661 [](const std::unique_ptr<HostResolverInternalResult>& result) {
662 return result->type() == HostResolverInternalResult::Type::kData;
663 });
664 CHECK(data_result_it != transaction_results.end());
665 DCHECK_EQ(base::ranges::count_if(
666 transaction_results,
667 [](const std::unique_ptr<HostResolverInternalResult>& result) {
668 return result->type() ==
669 HostResolverInternalResult::Type::kData;
670 }),
671 1);
672
673 if (!success) {
674 // If sort failed, replace data result with a TTL-containing error result.
675 auto error_replacement = std::make_unique<HostResolverInternalErrorResult>(
676 (*data_result_it)->domain_name(), (*data_result_it)->query_type(),
677 (*data_result_it)->expiration(), (*data_result_it)->timed_expiration(),
678 HostResolverInternalResult::Source::kUnknown, ERR_DNS_SORT_ERROR);
679 CHECK(error_replacement->expiration().has_value());
680 CHECK(error_replacement->timed_expiration().has_value());
681
682 transaction_results.erase(data_result_it);
683 transaction_results.insert(std::move(error_replacement));
684 } else if (sorted.empty()) {
685 // Sorter prunes unusable destinations. If all addresses are pruned,
686 // remove the data result and replace with TTL-containing error result.
687 auto error_replacement = std::make_unique<HostResolverInternalErrorResult>(
688 (*data_result_it)->domain_name(), (*data_result_it)->query_type(),
689 (*data_result_it)->expiration(), (*data_result_it)->timed_expiration(),
690 (*data_result_it)->source(), ERR_NAME_NOT_RESOLVED);
691 CHECK(error_replacement->expiration().has_value());
692 CHECK(error_replacement->timed_expiration().has_value());
693
694 transaction_results.erase(data_result_it);
695 transaction_results.insert(std::move(error_replacement));
696 } else {
697 (*data_result_it)->AsData().set_endpoints(std::move(sorted));
698 }
699
700 HandleTransactionResults(std::move(transaction_info),
701 std::move(transaction_results));
702 }
703
HandleTransactionResults(TransactionInfo transaction_info,Results transaction_results)704 void HostResolverDnsTask::HandleTransactionResults(
705 TransactionInfo transaction_info,
706 Results transaction_results) {
707 CHECK(transactions_in_progress_.find(transaction_info) ==
708 transactions_in_progress_.end());
709
710 if (base::FeatureList::IsEnabled(features::kUseHostResolverCache) &&
711 resolve_context_->host_resolver_cache() != nullptr) {
712 for (const std::unique_ptr<HostResolverInternalResult>& result :
713 transaction_results) {
714 resolve_context_->host_resolver_cache()->Set(
715 result->Clone(), anonymization_key_, HostResolverSource::DNS,
716 secure_);
717 }
718 }
719
720 // Trigger HTTP->HTTPS upgrade if an HTTPS record is received for an "http"
721 // or "ws" request.
722 if (transaction_info.type == DnsQueryType::HTTPS &&
723 ShouldTriggerHttpToHttpsUpgrade(transaction_results)) {
724 // Disallow fallback. Otherwise DNS could be reattempted without HTTPS
725 // queries, and that would hide this error instead of triggering upgrade.
726 OnFailure(
727 ERR_DNS_NAME_HTTPS_ONLY, /*allow_fallback=*/false,
728 HostCache::Entry::TtlFromInternalResults(
729 transaction_results, base::Time::Now(), tick_clock_->NowTicks()),
730 transaction_info.type);
731 return;
732 }
733
734 // Failures other than ERR_NAME_NOT_RESOLVED cannot be merged with other
735 // transactions.
736 auto failure_result_it = base::ranges::find_if(
737 transaction_results,
738 [](const std::unique_ptr<HostResolverInternalResult>& result) {
739 return result->type() == HostResolverInternalResult::Type::kError;
740 });
741 DCHECK_LE(base::ranges::count_if(
742 transaction_results,
743 [](const std::unique_ptr<HostResolverInternalResult>& result) {
744 return result->type() ==
745 HostResolverInternalResult::Type::kError;
746 }),
747 1);
748 if (failure_result_it != transaction_results.end() &&
749 (*failure_result_it)->AsError().error() != ERR_NAME_NOT_RESOLVED) {
750 OnFailure(
751 (*failure_result_it)->AsError().error(), /*allow_fallback=*/true,
752 HostCache::Entry::TtlFromInternalResults(
753 transaction_results, base::Time::Now(), tick_clock_->NowTicks()),
754 transaction_info.type);
755 return;
756 }
757
758 // TODO(crbug.com/1381506): Use new results type directly instead of
759 // converting to HostCache::Entry.
760 HostCache::Entry legacy_results(transaction_results, base::Time::Now(),
761 tick_clock_->NowTicks(),
762 HostCache::Entry::SOURCE_DNS);
763
764 // Merge results with saved results from previous transactions.
765 if (saved_results_) {
766 // If saved result is a deferred failure, try again to complete with that
767 // failure.
768 if (saved_results_is_failure_) {
769 OnFailure(saved_results_.value().error(), /*allow_fallback=*/true,
770 saved_results_.value().GetOptionalTtl());
771 return;
772 }
773
774 switch (transaction_info.type) {
775 case DnsQueryType::A:
776 // Canonical names from A results have lower priority than those
777 // from AAAA results, so merge to the back.
778 legacy_results = HostCache::Entry::MergeEntries(
779 std::move(saved_results_).value(), std::move(legacy_results));
780 break;
781 case DnsQueryType::AAAA:
782 // Canonical names from AAAA results take priority over those
783 // from A results, so merge to the front.
784 legacy_results = HostCache::Entry::MergeEntries(
785 std::move(legacy_results), std::move(saved_results_).value());
786 break;
787 case DnsQueryType::HTTPS:
788 // No particular importance to order.
789 legacy_results = HostCache::Entry::MergeEntries(
790 std::move(legacy_results), std::move(saved_results_).value());
791 break;
792 default:
793 // Only expect address query types with multiple transactions.
794 NOTREACHED();
795 }
796 }
797
798 saved_results_ = std::move(legacy_results);
799
800 OnTransactionsFinished(SingleTransactionResults(
801 transaction_info.type, std::move(transaction_results)));
802 }
803
OnTransactionsFinished(std::optional<SingleTransactionResults> single_transaction_results)804 void HostResolverDnsTask::OnTransactionsFinished(
805 std::optional<SingleTransactionResults> single_transaction_results) {
806 if (!transactions_in_progress_.empty() || !transactions_needed_.empty()) {
807 MaybeStartTimeoutTimer();
808 delegate_->OnIntermediateTransactionsComplete(
809 std::move(single_transaction_results));
810 // `this` may be deleted by `delegate_`. Do not add code below.
811 return;
812 }
813
814 DCHECK(saved_results_.has_value());
815 HostCache::Entry results = std::move(*saved_results_);
816
817 timeout_timer_.Stop();
818
819 // If using HostResolverCache, transactions are already invidvidually sorted
820 // on completion.
821 if (!base::FeatureList::IsEnabled(features::kUseHostResolverCache)) {
822 std::vector<IPEndPoint> ip_endpoints = results.ip_endpoints();
823
824 // If there are multiple addresses, and at least one is IPv6, need to
825 // sort them.
826 bool at_least_one_ipv6_address = base::ranges::any_of(
827 ip_endpoints,
828 [](auto& e) { return e.GetFamily() == ADDRESS_FAMILY_IPV6; });
829
830 if (at_least_one_ipv6_address) {
831 // Sort addresses if needed. Sort could complete synchronously.
832 client_->GetAddressSorter()->Sort(
833 ip_endpoints,
834 base::BindOnce(&HostResolverDnsTask::OnSortComplete, AsWeakPtr(),
835 tick_clock_->NowTicks(), std::move(results), secure_));
836 return;
837 }
838 }
839
840 OnSuccess(std::move(results));
841 }
842
OnSortComplete(base::TimeTicks sort_start_time,HostCache::Entry results,bool secure,bool success,std::vector<IPEndPoint> sorted)843 void HostResolverDnsTask::OnSortComplete(base::TimeTicks sort_start_time,
844 HostCache::Entry results,
845 bool secure,
846 bool success,
847 std::vector<IPEndPoint> sorted) {
848 results.set_ip_endpoints(std::move(sorted));
849
850 if (!success) {
851 OnFailure(ERR_DNS_SORT_ERROR, /*allow_fallback=*/true,
852 results.GetOptionalTtl());
853 return;
854 }
855
856 // AddressSorter prunes unusable destinations.
857 if (results.ip_endpoints().empty() && results.text_records().empty() &&
858 results.hostnames().empty()) {
859 LOG(WARNING) << "Address list empty after RFC3484 sort";
860 OnFailure(ERR_NAME_NOT_RESOLVED, /*allow_fallback=*/true,
861 results.GetOptionalTtl());
862 return;
863 }
864
865 OnSuccess(std::move(results));
866 }
867
AnyPotentiallyFatalTransactionsRemain()868 bool HostResolverDnsTask::AnyPotentiallyFatalTransactionsRemain() {
869 auto is_fatal_or_empty_error = [](TransactionErrorBehavior behavior) {
870 return behavior == TransactionErrorBehavior::kFatalOrEmpty;
871 };
872
873 return base::ranges::any_of(transactions_needed_, is_fatal_or_empty_error,
874 &TransactionInfo::error_behavior) ||
875 base::ranges::any_of(transactions_in_progress_,
876 is_fatal_or_empty_error,
877 &TransactionInfo::error_behavior);
878 }
879
CancelNonFatalTransactions()880 void HostResolverDnsTask::CancelNonFatalTransactions() {
881 auto has_non_fatal_or_empty_error = [](const TransactionInfo& info) {
882 return info.error_behavior != TransactionErrorBehavior::kFatalOrEmpty;
883 };
884
885 base::EraseIf(transactions_needed_, has_non_fatal_or_empty_error);
886 std::erase_if(transactions_in_progress_, has_non_fatal_or_empty_error);
887 }
888
OnFailure(int net_error,bool allow_fallback,std::optional<base::TimeDelta> ttl,std::optional<DnsQueryType> failed_transaction_type)889 void HostResolverDnsTask::OnFailure(
890 int net_error,
891 bool allow_fallback,
892 std::optional<base::TimeDelta> ttl,
893 std::optional<DnsQueryType> failed_transaction_type) {
894 if (httpssvc_metrics_ && failed_transaction_type.has_value() &&
895 IsAddressType(failed_transaction_type.value())) {
896 httpssvc_metrics_->SaveAddressQueryFailure();
897 }
898
899 DCHECK_NE(OK, net_error);
900 HostCache::Entry results(net_error, HostCache::Entry::SOURCE_UNKNOWN, ttl);
901
902 // On non-fatal errors, if any potentially fatal transactions remain, need
903 // to defer ending the task in case any of those remaining transactions end
904 // with a fatal failure.
905 if (allow_fallback && AnyPotentiallyFatalTransactionsRemain()) {
906 saved_results_ = std::move(results);
907 saved_results_is_failure_ = true;
908
909 CancelNonFatalTransactions();
910 OnTransactionsFinished(/*single_transaction_results=*/std::nullopt);
911 return;
912 }
913
914 net_log_.EndEvent(NetLogEventType::HOST_RESOLVER_DNS_TASK, [&] {
915 return NetLogDnsTaskFailedParams(net_error, failed_transaction_type, ttl,
916 base::OptionalToPtr(saved_results_));
917 });
918
919 // Expect this to result in destroying `this` and thus cancelling any
920 // remaining transactions.
921 delegate_->OnDnsTaskComplete(task_start_time_, allow_fallback,
922 std::move(results), secure_);
923 }
924
OnSuccess(HostCache::Entry results)925 void HostResolverDnsTask::OnSuccess(HostCache::Entry results) {
926 net_log_.EndEvent(NetLogEventType::HOST_RESOLVER_DNS_TASK,
927 [&] { return NetLogResults(results); });
928 delegate_->OnDnsTaskComplete(task_start_time_, /*allow_fallback=*/true,
929 std::move(results), secure_);
930 }
931
AnyOfTypeTransactionsRemain(std::initializer_list<DnsQueryType> types) const932 bool HostResolverDnsTask::AnyOfTypeTransactionsRemain(
933 std::initializer_list<DnsQueryType> types) const {
934 // Should only be called if some transactions are still running or waiting
935 // to run.
936 DCHECK(!transactions_needed_.empty() || !transactions_in_progress_.empty());
937
938 // Check running transactions.
939 if (base::ranges::find_first_of(transactions_in_progress_, types,
940 /*pred=*/{},
941 /*proj1=*/&TransactionInfo::type) !=
942 transactions_in_progress_.end()) {
943 return true;
944 }
945
946 // Check queued transactions, in case it ever becomes possible to get here
947 // without the transactions being started first.
948 return base::ranges::find_first_of(transactions_needed_, types, /*pred=*/{},
949 /*proj1=*/&TransactionInfo::type) !=
950 transactions_needed_.end();
951 }
952
MaybeStartTimeoutTimer()953 void HostResolverDnsTask::MaybeStartTimeoutTimer() {
954 // Should only be called if some transactions are still running or waiting
955 // to run.
956 DCHECK(!transactions_in_progress_.empty() || !transactions_needed_.empty());
957
958 // Timer already running.
959 if (timeout_timer_.IsRunning()) {
960 return;
961 }
962
963 // Always wait for address transactions.
964 if (AnyOfTypeTransactionsRemain({DnsQueryType::A, DnsQueryType::AAAA})) {
965 return;
966 }
967
968 base::TimeDelta timeout_max;
969 int extra_time_percent = 0;
970 base::TimeDelta timeout_min;
971
972 if (AnyOfTypeTransactionsRemain({DnsQueryType::HTTPS})) {
973 DCHECK(https_svcb_options_.enable);
974
975 if (secure_) {
976 timeout_max = https_svcb_options_.secure_extra_time_max;
977 extra_time_percent = https_svcb_options_.secure_extra_time_percent;
978 timeout_min = https_svcb_options_.secure_extra_time_min;
979 } else {
980 timeout_max = https_svcb_options_.insecure_extra_time_max;
981 extra_time_percent = https_svcb_options_.insecure_extra_time_percent;
982 timeout_min = https_svcb_options_.insecure_extra_time_min;
983 }
984
985 // Skip timeout for secure requests if the timeout would be a fatal
986 // failure.
987 if (secure_ && features::kUseDnsHttpsSvcbEnforceSecureResponse.Get()) {
988 timeout_max = base::TimeDelta();
989 extra_time_percent = 0;
990 timeout_min = base::TimeDelta();
991 }
992 } else {
993 // Unhandled supplemental type.
994 NOTREACHED();
995 }
996
997 base::TimeDelta timeout;
998 if (extra_time_percent > 0) {
999 base::TimeDelta total_time_for_other_transactions =
1000 tick_clock_->NowTicks() - task_start_time_;
1001 timeout = total_time_for_other_transactions * extra_time_percent / 100;
1002 // Use at least 1ms to ensure timeout doesn't occur immediately in tests.
1003 timeout = std::max(timeout, base::Milliseconds(1));
1004
1005 if (!timeout_max.is_zero()) {
1006 timeout = std::min(timeout, timeout_max);
1007 }
1008 if (!timeout_min.is_zero()) {
1009 timeout = std::max(timeout, timeout_min);
1010 }
1011 } else {
1012 // If no relative timeout, use a non-zero min/max as timeout. If both are
1013 // non-zero, that's not very sensible, but arbitrarily take the higher
1014 // timeout.
1015 timeout = std::max(timeout_min, timeout_max);
1016 }
1017
1018 if (!timeout.is_zero()) {
1019 timeout_timer_.Start(FROM_HERE, timeout,
1020 base::BindOnce(&HostResolverDnsTask::OnTimeout,
1021 base::Unretained(this)));
1022 }
1023 }
1024
ShouldTriggerHttpToHttpsUpgrade(const Results & results)1025 bool HostResolverDnsTask::ShouldTriggerHttpToHttpsUpgrade(
1026 const Results& results) {
1027 // Upgrade if at least one HTTPS record was compatible, and the host uses an
1028 // upgradable scheme.
1029
1030 if (!host_.HasScheme()) {
1031 return false;
1032 }
1033
1034 const std::string& scheme = host_.GetScheme();
1035 if (scheme != url::kHttpScheme && scheme != url::kWsScheme) {
1036 return false;
1037 }
1038
1039 return base::ranges::any_of(
1040 results, [](const std::unique_ptr<HostResolverInternalResult>& result) {
1041 return result->type() == HostResolverInternalResult::Type::kMetadata;
1042 });
1043 }
1044
1045 } // namespace net
1046