xref: /aosp_15_r20/external/cronet/net/dns/host_resolver_dns_task.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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