• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 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_cache.h"
6 
7 #include <algorithm>
8 #include <iterator>
9 #include <map>
10 #include <memory>
11 #include <ostream>
12 #include <string>
13 #include <type_traits>
14 #include <unordered_set>
15 #include <utility>
16 #include <vector>
17 
18 #include "base/check_op.h"
19 #include "base/functional/bind.h"
20 #include "base/metrics/field_trial.h"
21 #include "base/metrics/histogram_macros.h"
22 #include "base/numerics/safe_conversions.h"
23 #include "base/strings/string_number_conversions.h"
24 #include "base/strings/string_piece.h"
25 #include "base/time/default_tick_clock.h"
26 #include "base/types/optional_util.h"
27 #include "base/value_iterators.h"
28 #include "net/base/address_family.h"
29 #include "net/base/ip_endpoint.h"
30 #include "net/base/trace_constants.h"
31 #include "net/base/tracing.h"
32 #include "net/dns/host_resolver.h"
33 #include "net/dns/host_resolver_internal_result.h"
34 #include "net/dns/https_record_rdata.h"
35 #include "net/dns/public/dns_protocol.h"
36 #include "net/dns/public/host_resolver_source.h"
37 #include "net/log/net_log.h"
38 #include "third_party/abseil-cpp/absl/types/optional.h"
39 #include "third_party/abseil-cpp/absl/types/variant.h"
40 #include "url/scheme_host_port.h"
41 
42 namespace net {
43 
44 namespace {
45 
46 #define CACHE_HISTOGRAM_TIME(name, time) \
47   UMA_HISTOGRAM_LONG_TIMES("DNS.HostCache." name, time)
48 
49 #define CACHE_HISTOGRAM_COUNT(name, count) \
50   UMA_HISTOGRAM_COUNTS_1000("DNS.HostCache." name, count)
51 
52 #define CACHE_HISTOGRAM_ENUM(name, value, max) \
53   UMA_HISTOGRAM_ENUMERATION("DNS.HostCache." name, value, max)
54 
55 // String constants for dictionary keys.
56 const char kSchemeKey[] = "scheme";
57 const char kHostnameKey[] = "hostname";
58 const char kPortKey[] = "port";
59 const char kDnsQueryTypeKey[] = "dns_query_type";
60 const char kFlagsKey[] = "flags";
61 const char kHostResolverSourceKey[] = "host_resolver_source";
62 const char kSecureKey[] = "secure";
63 const char kNetworkAnonymizationKey[] = "network_anonymization_key";
64 const char kExpirationKey[] = "expiration";
65 const char kTtlKey[] = "ttl";
66 const char kPinnedKey[] = "pinned";
67 const char kNetworkChangesKey[] = "network_changes";
68 const char kNetErrorKey[] = "net_error";
69 const char kIpEndpointsKey[] = "ip_endpoints";
70 const char kEndpointAddressKey[] = "endpoint_address";
71 const char kEndpointPortKey[] = "endpoint_port";
72 const char kEndpointMetadatasKey[] = "endpoint_metadatas";
73 const char kEndpointMetadataWeightKey[] = "endpoint_metadata_weight";
74 const char kEndpointMetadataValueKey[] = "endpoint_metadata_value";
75 const char kAliasesKey[] = "aliases";
76 const char kAddressesKey[] = "addresses";
77 const char kTextRecordsKey[] = "text_records";
78 const char kHostnameResultsKey[] = "hostname_results";
79 const char kHostPortsKey[] = "host_ports";
80 const char kCanonicalNamesKey[] = "canonical_names";
81 
IpEndpointToValue(const IPEndPoint & endpoint)82 base::Value IpEndpointToValue(const IPEndPoint& endpoint) {
83   base::Value::Dict dictionary;
84   dictionary.Set(kEndpointAddressKey, endpoint.ToStringWithoutPort());
85   dictionary.Set(kEndpointPortKey, endpoint.port());
86   return base::Value(std::move(dictionary));
87 }
88 
IpEndpointFromValue(const base::Value & value)89 absl::optional<IPEndPoint> IpEndpointFromValue(const base::Value& value) {
90   if (!value.is_dict())
91     return absl::nullopt;
92 
93   const base::Value::Dict& dict = value.GetDict();
94   const std::string* ip_str = dict.FindString(kEndpointAddressKey);
95   absl::optional<int> port = dict.FindInt(kEndpointPortKey);
96 
97   if (!ip_str || !port ||
98       !base::IsValueInRangeForNumericType<uint16_t>(port.value())) {
99     return absl::nullopt;
100   }
101 
102   IPAddress ip;
103   if (!ip.AssignFromIPLiteral(*ip_str))
104     return absl::nullopt;
105 
106   return IPEndPoint(ip, base::checked_cast<uint16_t>(port.value()));
107 }
108 
EndpointMetadataPairToValue(const std::pair<HttpsRecordPriority,ConnectionEndpointMetadata> & pair)109 base::Value EndpointMetadataPairToValue(
110     const std::pair<HttpsRecordPriority, ConnectionEndpointMetadata>& pair) {
111   base::Value::Dict dictionary;
112   dictionary.Set(kEndpointMetadataWeightKey, pair.first);
113   dictionary.Set(kEndpointMetadataValueKey, pair.second.ToValue());
114   return base::Value(std::move(dictionary));
115 }
116 
117 absl::optional<std::pair<HttpsRecordPriority, ConnectionEndpointMetadata>>
EndpointMetadataPairFromValue(const base::Value & value)118 EndpointMetadataPairFromValue(const base::Value& value) {
119   if (!value.is_dict())
120     return absl::nullopt;
121 
122   const base::Value::Dict& dict = value.GetDict();
123   absl::optional<int> priority = dict.FindInt(kEndpointMetadataWeightKey);
124   const base::Value* metadata_value = dict.Find(kEndpointMetadataValueKey);
125 
126   if (!priority || !base::IsValueInRangeForNumericType<HttpsRecordPriority>(
127                        priority.value())) {
128     return absl::nullopt;
129   }
130 
131   if (!metadata_value)
132     return absl::nullopt;
133   absl::optional<ConnectionEndpointMetadata> metadata =
134       ConnectionEndpointMetadata::FromValue(*metadata_value);
135   if (!metadata)
136     return absl::nullopt;
137 
138   return std::make_pair(
139       base::checked_cast<HttpsRecordPriority>(priority.value()),
140       std::move(metadata).value());
141 }
142 
IPEndPointsFromLegacyAddressListValue(const base::Value::List & value,absl::optional<std::vector<IPEndPoint>> * ip_endpoints)143 bool IPEndPointsFromLegacyAddressListValue(
144     const base::Value::List& value,
145     absl::optional<std::vector<IPEndPoint>>* ip_endpoints) {
146   ip_endpoints->emplace();
147   for (const auto& it : value) {
148     IPAddress address;
149     const std::string* addr_string = it.GetIfString();
150     if (!addr_string || !address.AssignFromIPLiteral(*addr_string)) {
151       return false;
152     }
153     ip_endpoints->value().emplace_back(address, 0);
154   }
155   return true;
156 }
157 
158 template <typename T>
MergeLists(absl::optional<T> * target,const absl::optional<T> & source)159 void MergeLists(absl::optional<T>* target, const absl::optional<T>& source) {
160   if (target->has_value() && source) {
161     target->value().insert(target->value().end(), source.value().begin(),
162                            source.value().end());
163   } else if (source) {
164     *target = source;
165   }
166 }
167 
168 template <typename T>
MergeContainers(absl::optional<T> & target,const absl::optional<T> & source)169 void MergeContainers(absl::optional<T>& target,
170                      const absl::optional<T>& source) {
171   if (target.has_value() && source.has_value()) {
172     target->insert(source->begin(), source->end());
173   } else if (source) {
174     target = source;
175   }
176 }
177 
178 // Used to reject empty and IP literal (whether or not surrounded by brackets)
179 // hostnames.
IsValidHostname(base::StringPiece hostname)180 bool IsValidHostname(base::StringPiece hostname) {
181   if (hostname.empty())
182     return false;
183 
184   IPAddress ip_address;
185   if (ip_address.AssignFromIPLiteral(hostname) ||
186       ParseURLHostnameToAddress(hostname, &ip_address)) {
187     return false;
188   }
189 
190   return true;
191 }
192 
GetHostname(const absl::variant<url::SchemeHostPort,std::string> & host)193 const std::string& GetHostname(
194     const absl::variant<url::SchemeHostPort, std::string>& host) {
195   const std::string* hostname;
196   if (absl::holds_alternative<url::SchemeHostPort>(host)) {
197     hostname = &absl::get<url::SchemeHostPort>(host).host();
198   } else {
199     DCHECK(absl::holds_alternative<std::string>(host));
200     hostname = &absl::get<std::string>(host);
201   }
202 
203   DCHECK(IsValidHostname(*hostname));
204   return *hostname;
205 }
206 
GetDnsQueryType(int dns_query_type)207 absl::optional<DnsQueryType> GetDnsQueryType(int dns_query_type) {
208   for (const auto& type : kDnsQueryTypes) {
209     if (base::strict_cast<int>(type.first) == dns_query_type)
210       return type.first;
211   }
212   return absl::nullopt;
213 }
214 
215 }  // namespace
216 
217 // Used in histograms; do not modify existing values.
218 enum HostCache::SetOutcome : int {
219   SET_INSERT = 0,
220   SET_UPDATE_VALID = 1,
221   SET_UPDATE_STALE = 2,
222   MAX_SET_OUTCOME
223 };
224 
225 // Used in histograms; do not modify existing values.
226 enum HostCache::LookupOutcome : int {
227   LOOKUP_MISS_ABSENT = 0,
228   LOOKUP_MISS_STALE = 1,
229   LOOKUP_HIT_VALID = 2,
230   LOOKUP_HIT_STALE = 3,
231   MAX_LOOKUP_OUTCOME
232 };
233 
234 // Used in histograms; do not modify existing values.
235 enum HostCache::EraseReason : int {
236   ERASE_EVICT = 0,
237   ERASE_CLEAR = 1,
238   ERASE_DESTRUCT = 2,
239   MAX_ERASE_REASON
240 };
241 
Key(absl::variant<url::SchemeHostPort,std::string> host,DnsQueryType dns_query_type,HostResolverFlags host_resolver_flags,HostResolverSource host_resolver_source,const NetworkAnonymizationKey & network_anonymization_key)242 HostCache::Key::Key(absl::variant<url::SchemeHostPort, std::string> host,
243                     DnsQueryType dns_query_type,
244                     HostResolverFlags host_resolver_flags,
245                     HostResolverSource host_resolver_source,
246                     const NetworkAnonymizationKey& network_anonymization_key)
247     : host(std::move(host)),
248       dns_query_type(dns_query_type),
249       host_resolver_flags(host_resolver_flags),
250       host_resolver_source(host_resolver_source),
251       network_anonymization_key(network_anonymization_key) {
252   DCHECK(IsValidHostname(GetHostname(this->host)));
253   if (absl::holds_alternative<url::SchemeHostPort>(this->host))
254     DCHECK(absl::get<url::SchemeHostPort>(this->host).IsValid());
255 }
256 
257 HostCache::Key::Key() = default;
258 HostCache::Key::Key(const Key& key) = default;
259 HostCache::Key::Key(Key&& key) = default;
260 
261 HostCache::Key::~Key() = default;
262 
Entry(int error,Source source,absl::optional<base::TimeDelta> ttl)263 HostCache::Entry::Entry(int error,
264                         Source source,
265                         absl::optional<base::TimeDelta> ttl)
266     : error_(error), source_(source), ttl_(ttl.value_or(kUnknownTtl)) {
267   // If |ttl| has a value, must not be negative.
268   DCHECK_GE(ttl.value_or(base::TimeDelta()), base::TimeDelta());
269   DCHECK_NE(OK, error_);
270 
271   // host_cache.h defines its own `HttpsRecordPriority` due to
272   // https_record_rdata.h not being allowed in the same places, but the types
273   // should still be the same thing.
274   static_assert(std::is_same<net::HttpsRecordPriority,
275                              HostCache::Entry::HttpsRecordPriority>::value,
276                 "`net::HttpsRecordPriority` and "
277                 "`HostCache::Entry::HttpsRecordPriority` must be same type");
278 }
279 
Entry(std::vector<std::unique_ptr<HostResolverInternalResult>> results,base::Time now,base::TimeTicks now_ticks)280 HostCache::Entry::Entry(
281     std::vector<std::unique_ptr<HostResolverInternalResult>> results,
282     base::Time now,
283     base::TimeTicks now_ticks) {
284   std::unique_ptr<HostResolverInternalResult> data_result;
285   std::unique_ptr<HostResolverInternalResult> metadata_result;
286   std::unique_ptr<HostResolverInternalResult> error_result;
287   std::vector<std::unique_ptr<HostResolverInternalResult>> alias_results;
288 
289   absl::optional<base::TimeDelta> smallest_ttl;
290   absl::optional<Source> source;
291   for (auto& result : results) {
292     if (result->expiration().has_value()) {
293       smallest_ttl = std::min(smallest_ttl.value_or(base::TimeDelta::Max()),
294                               result->expiration().value() - now_ticks);
295     }
296     if (result->timed_expiration().has_value()) {
297       smallest_ttl = std::min(smallest_ttl.value_or(base::TimeDelta::Max()),
298                               result->timed_expiration().value() - now);
299     }
300 
301     Source result_source;
302     switch (result->source()) {
303       case HostResolverInternalResult::Source::kDns:
304         result_source = SOURCE_DNS;
305         break;
306       case HostResolverInternalResult::Source::kHosts:
307         result_source = SOURCE_HOSTS;
308         break;
309       case HostResolverInternalResult::Source::kUnknown:
310         result_source = SOURCE_UNKNOWN;
311         break;
312     }
313 
314     switch (result->type()) {
315       case HostResolverInternalResult::Type::kData:
316         DCHECK(!data_result);  // Expect at most one data result.
317         data_result = std::move(result);
318         break;
319       case HostResolverInternalResult::Type::kMetadata:
320         DCHECK(!metadata_result);  // Expect at most one metadata result.
321         metadata_result = std::move(result);
322         break;
323       case HostResolverInternalResult::Type::kError:
324         DCHECK(!error_result);  // Expect at most one error result.
325         error_result = std::move(result);
326         break;
327       case HostResolverInternalResult::Type::kAlias:
328         alias_results.push_back(std::move(result));
329         break;
330     }
331 
332     // Expect all results to have the same source.
333     DCHECK(!source.has_value() || source.value() == result_source);
334     source = result_source;
335   }
336 
337   ttl_ = smallest_ttl.value_or(kUnknownTtl);
338   source_ = source.value_or(SOURCE_UNKNOWN);
339 
340   if (error_result) {
341     DCHECK(!data_result);
342     DCHECK(!metadata_result);
343 
344     error_ = error_result->AsError().error();
345 
346     // For error results, should not create entry with a TTL unless it is a
347     // cacheable error.
348     if (!error_result->expiration().has_value() &&
349         !error_result->timed_expiration().has_value()) {
350       ttl_ = kUnknownTtl;
351     }
352   } else if (!data_result && !metadata_result) {
353     // Only alias results (or completely empty results). Never cacheable due to
354     // being equivalent to an error result without TTL.
355     error_ = ERR_NAME_NOT_RESOLVED;
356     ttl_ = kUnknownTtl;
357   } else {
358     error_ = OK;
359   }
360 
361   if (data_result) {
362     DCHECK(!error_result);
363     DCHECK(!data_result->AsData().endpoints().empty() ||
364            !data_result->AsData().strings().empty() ||
365            !data_result->AsData().hosts().empty());
366     // Data results should always be cacheable.
367     DCHECK(data_result->expiration().has_value() ||
368            data_result->timed_expiration().has_value());
369 
370     ip_endpoints_ = data_result->AsData().endpoints();
371     text_records_ = data_result->AsData().strings();
372     hostnames_ = data_result->AsData().hosts();
373     canonical_names_ = {data_result->domain_name()};
374 
375     aliases_.emplace();
376     for (const auto& alias_result : alias_results) {
377       aliases_.value().insert(alias_result->domain_name());
378       aliases_.value().insert(alias_result->AsAlias().alias_target());
379     }
380     aliases_.value().insert(data_result->domain_name());
381   }
382   if (metadata_result) {
383     DCHECK(!error_result);
384     // Metadata results should always be cacheable.
385     DCHECK(metadata_result->expiration().has_value() ||
386            metadata_result->timed_expiration().has_value());
387 
388     endpoint_metadatas_ = metadata_result->AsMetadata().metadatas();
389 
390     // Even if otherwise empty, having the metadata result object signifies
391     // receiving a compatible HTTPS record.
392     https_record_compatibility_ = std::vector<bool>{true};
393 
394     if (endpoint_metadatas_.value().empty())
395       error_ = ERR_NAME_NOT_RESOLVED;
396   }
397 }
398 
399 HostCache::Entry::Entry(const Entry& entry) = default;
400 
401 HostCache::Entry::Entry(Entry&& entry) = default;
402 
403 HostCache::Entry::~Entry() = default;
404 
405 absl::optional<std::vector<HostResolverEndpointResult>>
GetEndpoints() const406 HostCache::Entry::GetEndpoints() const {
407   if (!ip_endpoints_.has_value())
408     return absl::nullopt;
409 
410   std::vector<HostResolverEndpointResult> endpoints;
411 
412   if (ip_endpoints_.value().empty())
413     return endpoints;
414   absl::optional<std::vector<ConnectionEndpointMetadata>> metadatas =
415       GetMetadatas();
416 
417   if (metadatas.has_value() && canonical_names_ &&
418       (canonical_names_->size() == 1)) {
419     // Currently Chrome uses HTTPS records only when A and AAAA records are at
420     // the same canonical name and that matches the HTTPS target name.
421     for (ConnectionEndpointMetadata& metadata : metadatas.value()) {
422       if (canonical_names_->find(metadata.target_name) ==
423           canonical_names_->end()) {
424         continue;
425       }
426       endpoints.emplace_back();
427       endpoints.back().ip_endpoints = ip_endpoints_.value();
428       endpoints.back().metadata = std::move(metadata);
429     }
430   }
431 
432   // Add a final non-protocol endpoint at the end.
433   endpoints.emplace_back();
434   endpoints.back().ip_endpoints = ip_endpoints_.value();
435 
436   return endpoints;
437 }
438 
439 absl::optional<std::vector<ConnectionEndpointMetadata>>
GetMetadatas() const440 HostCache::Entry::GetMetadatas() const {
441   if (!endpoint_metadatas_.has_value())
442     return absl::nullopt;
443 
444   std::vector<ConnectionEndpointMetadata> metadatas;
445   HttpsRecordPriority last_priority = 0;
446   for (const auto& metadata : endpoint_metadatas_.value()) {
447     // Ensure metadatas are iterated in priority order.
448     DCHECK_GE(metadata.first, last_priority);
449     last_priority = metadata.first;
450 
451     metadatas.push_back(metadata.second);
452   }
453 
454   return metadatas;
455 }
456 
GetOptionalTtl() const457 absl::optional<base::TimeDelta> HostCache::Entry::GetOptionalTtl() const {
458   if (has_ttl())
459     return ttl();
460   else
461     return absl::nullopt;
462 }
463 
464 // static
MergeEntries(Entry front,Entry back)465 HostCache::Entry HostCache::Entry::MergeEntries(Entry front, Entry back) {
466   // Only expected to merge OK or ERR_NAME_NOT_RESOLVED results.
467   DCHECK(front.error() == OK || front.error() == ERR_NAME_NOT_RESOLVED);
468   DCHECK(back.error() == OK || back.error() == ERR_NAME_NOT_RESOLVED);
469 
470   // Build results in |front| to preserve unmerged fields.
471 
472   front.error_ =
473       front.error() == OK || back.error() == OK ? OK : ERR_NAME_NOT_RESOLVED;
474 
475   MergeLists(&front.ip_endpoints_, back.ip_endpoints_);
476   MergeContainers(front.endpoint_metadatas_, back.endpoint_metadatas_);
477   MergeContainers(front.aliases_, back.aliases_);
478   MergeLists(&front.text_records_, back.text_records());
479   MergeLists(&front.hostnames_, back.hostnames());
480   MergeLists(&front.https_record_compatibility_,
481              back.https_record_compatibility_);
482   MergeContainers(front.canonical_names_, back.canonical_names_);
483 
484   // Only expected to merge entries from same source.
485   DCHECK_EQ(front.source(), back.source());
486 
487   if (front.has_ttl() && back.has_ttl()) {
488     front.ttl_ = std::min(front.ttl(), back.ttl());
489   } else if (back.has_ttl()) {
490     front.ttl_ = back.ttl();
491   }
492 
493   front.expires_ = std::min(front.expires(), back.expires());
494   front.network_changes_ =
495       std::max(front.network_changes(), back.network_changes());
496 
497   front.total_hits_ = front.total_hits_ + back.total_hits_;
498   front.stale_hits_ = front.stale_hits_ + back.stale_hits_;
499 
500   return front;
501 }
502 
CopyWithDefaultPort(uint16_t port) const503 HostCache::Entry HostCache::Entry::CopyWithDefaultPort(uint16_t port) const {
504   Entry copy(*this);
505 
506   if (copy.ip_endpoints_) {
507     for (IPEndPoint& endpoint : copy.ip_endpoints_.value()) {
508       if (endpoint.port() == 0)
509         endpoint = IPEndPoint(endpoint.address(), port);
510     }
511   }
512 
513   if (copy.hostnames_) {
514     for (HostPortPair& hostname : copy.hostnames_.value()) {
515       if (hostname.port() == 0)
516         hostname = HostPortPair(hostname.host(), port);
517     }
518   }
519 
520   return copy;
521 }
522 
523 HostCache::Entry& HostCache::Entry::operator=(const Entry& entry) = default;
524 
525 HostCache::Entry& HostCache::Entry::operator=(Entry&& entry) = default;
526 
Entry(int error,std::vector<IPEndPoint> ip_endpoints,std::set<std::string> aliases,Source source,absl::optional<base::TimeDelta> ttl)527 HostCache::Entry::Entry(int error,
528                         std::vector<IPEndPoint> ip_endpoints,
529                         std::set<std::string> aliases,
530                         Source source,
531                         absl::optional<base::TimeDelta> ttl)
532     : error_(error),
533       ip_endpoints_(std::move(ip_endpoints)),
534       aliases_(std::move(aliases)),
535       source_(source),
536       ttl_(ttl ? ttl.value() : kUnknownTtl) {
537   DCHECK(!ttl || ttl.value() >= base::TimeDelta());
538 }
539 
Entry(const HostCache::Entry & entry,base::TimeTicks now,base::TimeDelta ttl,int network_changes)540 HostCache::Entry::Entry(const HostCache::Entry& entry,
541                         base::TimeTicks now,
542                         base::TimeDelta ttl,
543                         int network_changes)
544     : error_(entry.error()),
545       ip_endpoints_(entry.ip_endpoints_),
546       endpoint_metadatas_(entry.endpoint_metadatas_),
547       aliases_(base::OptionalFromPtr(entry.aliases())),
548       text_records_(entry.text_records()),
549       hostnames_(entry.hostnames()),
550       https_record_compatibility_(entry.https_record_compatibility_),
551       source_(entry.source()),
552       pinning_(entry.pinning()),
553       canonical_names_(entry.canonical_names()),
554       ttl_(entry.ttl()),
555       expires_(now + ttl),
556       network_changes_(network_changes) {}
557 
Entry(int error,absl::optional<std::vector<IPEndPoint>> ip_endpoints,absl::optional<std::multimap<HttpsRecordPriority,ConnectionEndpointMetadata>> endpoint_metadatas,absl::optional<std::set<std::string>> aliases,absl::optional<std::vector<std::string>> && text_records,absl::optional<std::vector<HostPortPair>> && hostnames,absl::optional<std::vector<bool>> && https_record_compatibility,Source source,base::TimeTicks expires,int network_changes)558 HostCache::Entry::Entry(
559     int error,
560     absl::optional<std::vector<IPEndPoint>> ip_endpoints,
561     absl::optional<
562         std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata>>
563         endpoint_metadatas,
564     absl::optional<std::set<std::string>> aliases,
565     absl::optional<std::vector<std::string>>&& text_records,
566     absl::optional<std::vector<HostPortPair>>&& hostnames,
567     absl::optional<std::vector<bool>>&& https_record_compatibility,
568     Source source,
569     base::TimeTicks expires,
570     int network_changes)
571     : error_(error),
572       ip_endpoints_(std::move(ip_endpoints)),
573       endpoint_metadatas_(std::move(endpoint_metadatas)),
574       aliases_(std::move(aliases)),
575       text_records_(std::move(text_records)),
576       hostnames_(std::move(hostnames)),
577       https_record_compatibility_(std::move(https_record_compatibility)),
578       source_(source),
579       expires_(expires),
580       network_changes_(network_changes) {}
581 
PrepareForCacheInsertion()582 void HostCache::Entry::PrepareForCacheInsertion() {
583   https_record_compatibility_.reset();
584 }
585 
IsStale(base::TimeTicks now,int network_changes) const586 bool HostCache::Entry::IsStale(base::TimeTicks now, int network_changes) const {
587   EntryStaleness stale;
588   stale.expired_by = now - expires_;
589   stale.network_changes = network_changes - network_changes_;
590   stale.stale_hits = stale_hits_;
591   return stale.is_stale();
592 }
593 
CountHit(bool hit_is_stale)594 void HostCache::Entry::CountHit(bool hit_is_stale) {
595   ++total_hits_;
596   if (hit_is_stale)
597     ++stale_hits_;
598 }
599 
GetStaleness(base::TimeTicks now,int network_changes,EntryStaleness * out) const600 void HostCache::Entry::GetStaleness(base::TimeTicks now,
601                                     int network_changes,
602                                     EntryStaleness* out) const {
603   DCHECK(out);
604   out->expired_by = now - expires_;
605   out->network_changes = network_changes - network_changes_;
606   out->stale_hits = stale_hits_;
607 }
608 
NetLogParams() const609 base::Value HostCache::Entry::NetLogParams() const {
610   return base::Value(GetAsValue(false /* include_staleness */));
611 }
612 
GetAsValue(bool include_staleness) const613 base::Value::Dict HostCache::Entry::GetAsValue(bool include_staleness) const {
614   base::Value::Dict entry_dict;
615 
616   if (include_staleness) {
617     // The kExpirationKey value is using TimeTicks instead of Time used if
618     // |include_staleness| is false, so it cannot be used to deserialize.
619     // This is ok as it is used only for netlog.
620     entry_dict.Set(kExpirationKey, NetLog::TickCountToString(expires()));
621     entry_dict.Set(kTtlKey, base::saturated_cast<int>(ttl().InMilliseconds()));
622     entry_dict.Set(kNetworkChangesKey, network_changes());
623     // The "pinned" status is meaningful only if "network_changes" is also
624     // preserved.
625     if (pinning())
626       entry_dict.Set(kPinnedKey, *pinning());
627   } else {
628     // Convert expiration time in TimeTicks to Time for serialization, using a
629     // string because base::Value doesn't handle 64-bit integers.
630     base::Time expiration_time =
631         base::Time::Now() - (base::TimeTicks::Now() - expires());
632     entry_dict.Set(kExpirationKey,
633                    base::NumberToString(expiration_time.ToInternalValue()));
634   }
635 
636   if (error() != OK) {
637     entry_dict.Set(kNetErrorKey, error());
638   } else {
639     if (ip_endpoints_) {
640       base::Value::List ip_endpoints_list;
641       for (const IPEndPoint& ip_endpoint : ip_endpoints_.value()) {
642         ip_endpoints_list.Append(IpEndpointToValue(ip_endpoint));
643       }
644       entry_dict.Set(kIpEndpointsKey, std::move(ip_endpoints_list));
645     }
646 
647     if (endpoint_metadatas_) {
648       base::Value::List endpoint_metadatas_list;
649       for (const auto& endpoint_metadata_pair : endpoint_metadatas_.value()) {
650         endpoint_metadatas_list.Append(
651             EndpointMetadataPairToValue(endpoint_metadata_pair));
652       }
653       entry_dict.Set(kEndpointMetadatasKey, std::move(endpoint_metadatas_list));
654     }
655 
656     if (aliases()) {
657       base::Value::List alias_list;
658       for (const std::string& alias : *aliases()) {
659         alias_list.Append(alias);
660       }
661       entry_dict.Set(kAliasesKey, std::move(alias_list));
662     }
663 
664     if (text_records()) {
665       // Append all resolved text records.
666       base::Value::List text_list_value;
667       for (const std::string& text_record : text_records().value()) {
668         text_list_value.Append(text_record);
669       }
670       entry_dict.Set(kTextRecordsKey, std::move(text_list_value));
671     }
672 
673     if (hostnames()) {
674       // Append all the resolved hostnames.
675       base::Value::List hostnames_value;
676       base::Value::List host_ports_value;
677       for (const HostPortPair& hostname : hostnames().value()) {
678         hostnames_value.Append(hostname.host());
679         host_ports_value.Append(hostname.port());
680       }
681       entry_dict.Set(kHostnameResultsKey, std::move(hostnames_value));
682       entry_dict.Set(kHostPortsKey, std::move(host_ports_value));
683     }
684     if (canonical_names()) {
685       base::Value::List canonical_names_list;
686       for (const std::string& canonical_name : canonical_names().value()) {
687         canonical_names_list.Append(canonical_name);
688       }
689       entry_dict.Set(kCanonicalNamesKey, std::move(canonical_names_list));
690     }
691   }
692 
693   return entry_dict;
694 }
695 
696 // static
697 const HostCache::EntryStaleness HostCache::kNotStale = {base::Seconds(-1), 0,
698                                                         0};
699 
HostCache(size_t max_entries)700 HostCache::HostCache(size_t max_entries)
701     : max_entries_(max_entries),
702       tick_clock_(base::DefaultTickClock::GetInstance()) {}
703 
~HostCache()704 HostCache::~HostCache() {
705   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
706 }
707 
708 const std::pair<const HostCache::Key, HostCache::Entry>*
Lookup(const Key & key,base::TimeTicks now,bool ignore_secure)709 HostCache::Lookup(const Key& key, base::TimeTicks now, bool ignore_secure) {
710   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
711   if (caching_is_disabled())
712     return nullptr;
713 
714   auto* result = LookupInternalIgnoringFields(key, now, ignore_secure);
715   if (!result)
716     return nullptr;
717 
718   auto* entry = &result->second;
719   if (entry->IsStale(now, network_changes_))
720     return nullptr;
721 
722   entry->CountHit(/* hit_is_stale= */ false);
723   return result;
724 }
725 
LookupStale(const Key & key,base::TimeTicks now,HostCache::EntryStaleness * stale_out,bool ignore_secure)726 const std::pair<const HostCache::Key, HostCache::Entry>* HostCache::LookupStale(
727     const Key& key,
728     base::TimeTicks now,
729     HostCache::EntryStaleness* stale_out,
730     bool ignore_secure) {
731   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
732   if (caching_is_disabled())
733     return nullptr;
734 
735   auto* result = LookupInternalIgnoringFields(key, now, ignore_secure);
736   if (!result)
737     return nullptr;
738 
739   auto* entry = &result->second;
740   bool is_stale = entry->IsStale(now, network_changes_);
741   entry->CountHit(/* hit_is_stale= */ is_stale);
742 
743   if (stale_out)
744     entry->GetStaleness(now, network_changes_, stale_out);
745   return result;
746 }
747 
748 // static
749 std::pair<const HostCache::Key, HostCache::Entry>*
GetLessStaleMoreSecureResult(base::TimeTicks now,std::pair<const HostCache::Key,HostCache::Entry> * result1,std::pair<const HostCache::Key,HostCache::Entry> * result2)750 HostCache::GetLessStaleMoreSecureResult(
751     base::TimeTicks now,
752     std::pair<const HostCache::Key, HostCache::Entry>* result1,
753     std::pair<const HostCache::Key, HostCache::Entry>* result2) {
754   // Prefer a non-null result if possible.
755   if (!result1 && !result2)
756     return nullptr;
757   if (result1 && !result2)
758     return result1;
759   if (!result1 && result2)
760     return result2;
761 
762   // Both result1 are result2 are non-null.
763   EntryStaleness staleness1, staleness2;
764   result1->second.GetStaleness(now, 0, &staleness1);
765   result2->second.GetStaleness(now, 0, &staleness2);
766   if (staleness1.network_changes == staleness2.network_changes) {
767     // Exactly one of the results should be secure.
768     DCHECK(result1->first.secure != result2->first.secure);
769     // If the results have the same number of network changes, prefer a
770     // non-expired result.
771     if (staleness1.expired_by.is_negative() &&
772         staleness2.expired_by >= base::TimeDelta()) {
773       return result1;
774     }
775     if (staleness1.expired_by >= base::TimeDelta() &&
776         staleness2.expired_by.is_negative()) {
777       return result2;
778     }
779     // Both results are equally stale, so prefer a secure result.
780     return (result1->first.secure) ? result1 : result2;
781   }
782   // Prefer the result with the fewest network changes.
783   return (staleness1.network_changes < staleness2.network_changes) ? result1
784                                                                    : result2;
785 }
786 
787 std::pair<const HostCache::Key, HostCache::Entry>*
LookupInternalIgnoringFields(const Key & initial_key,base::TimeTicks now,bool ignore_secure)788 HostCache::LookupInternalIgnoringFields(const Key& initial_key,
789                                         base::TimeTicks now,
790                                         bool ignore_secure) {
791   std::pair<const HostCache::Key, HostCache::Entry>* preferred_result =
792       LookupInternal(initial_key);
793 
794   if (ignore_secure) {
795     Key effective_key = initial_key;
796     effective_key.secure = !initial_key.secure;
797     preferred_result = GetLessStaleMoreSecureResult(
798         now, preferred_result, LookupInternal(effective_key));
799   }
800 
801   return preferred_result;
802 }
803 
LookupInternal(const Key & key)804 std::pair<const HostCache::Key, HostCache::Entry>* HostCache::LookupInternal(
805     const Key& key) {
806   auto it = entries_.find(key);
807   return (it != entries_.end()) ? &*it : nullptr;
808 }
809 
Set(const Key & key,const Entry & entry,base::TimeTicks now,base::TimeDelta ttl)810 void HostCache::Set(const Key& key,
811                     const Entry& entry,
812                     base::TimeTicks now,
813                     base::TimeDelta ttl) {
814   TRACE_EVENT0(NetTracingCategory(), "HostCache::Set");
815   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
816   if (caching_is_disabled())
817     return;
818 
819   bool has_active_pin = false;
820   bool result_changed = false;
821   auto it = entries_.find(key);
822   if (it != entries_.end()) {
823     has_active_pin = HasActivePin(it->second);
824 
825     // TODO(juliatuttle): Remember some old metadata (hit count or frequency or
826     // something like that) if it's useful for better eviction algorithms?
827     result_changed = entry.error() == OK && !it->second.ContentsEqual(entry);
828     entries_.erase(it);
829   } else {
830     result_changed = true;
831     // This loop almost always runs at most once, for total runtime
832     // O(max_entries_).  It only runs more than once if the cache was over-full
833     // due to pinned entries, and this is the first call to Set() after
834     // Invalidate().  The amortized cost remains O(size()) per call to Set().
835     while (size() >= max_entries_ && EvictOneEntry(now)) {
836     }
837   }
838 
839   Entry entry_for_cache(entry, now, ttl, network_changes_);
840   entry_for_cache.set_pinning(entry.pinning().value_or(has_active_pin));
841   entry_for_cache.PrepareForCacheInsertion();
842   AddEntry(key, std::move(entry_for_cache));
843 
844   if (delegate_ && result_changed)
845     delegate_->ScheduleWrite();
846 }
847 
GetMatchingKeyForTesting(base::StringPiece hostname,HostCache::Entry::Source * source_out,HostCache::EntryStaleness * stale_out) const848 const HostCache::Key* HostCache::GetMatchingKeyForTesting(
849     base::StringPiece hostname,
850     HostCache::Entry::Source* source_out,
851     HostCache::EntryStaleness* stale_out) const {
852   for (const EntryMap::value_type& entry : entries_) {
853     if (GetHostname(entry.first.host) == hostname) {
854       if (source_out != nullptr)
855         *source_out = entry.second.source();
856       if (stale_out != nullptr) {
857         entry.second.GetStaleness(tick_clock_->NowTicks(), network_changes_,
858                                   stale_out);
859       }
860       return &entry.first;
861     }
862   }
863 
864   return nullptr;
865 }
866 
AddEntry(const Key & key,Entry && entry)867 void HostCache::AddEntry(const Key& key, Entry&& entry) {
868   DCHECK_EQ(0u, entries_.count(key));
869   DCHECK(entry.pinning().has_value());
870   entries_.emplace(key, std::move(entry));
871 }
872 
Invalidate()873 void HostCache::Invalidate() {
874   ++network_changes_;
875 }
876 
set_persistence_delegate(PersistenceDelegate * delegate)877 void HostCache::set_persistence_delegate(PersistenceDelegate* delegate) {
878   // A PersistenceDelegate shouldn't be added if there already was one, and
879   // shouldn't be removed (by setting to nullptr) if it wasn't previously there.
880   DCHECK_NE(delegate == nullptr, delegate_ == nullptr);
881   delegate_ = delegate;
882 }
883 
clear()884 void HostCache::clear() {
885   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
886 
887   // Don't bother scheduling a write if there's nothing to clear.
888   if (size() == 0)
889     return;
890 
891   entries_.clear();
892   if (delegate_)
893     delegate_->ScheduleWrite();
894 }
895 
ClearForHosts(const base::RepeatingCallback<bool (const std::string &)> & host_filter)896 void HostCache::ClearForHosts(
897     const base::RepeatingCallback<bool(const std::string&)>& host_filter) {
898   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
899 
900   if (host_filter.is_null()) {
901     clear();
902     return;
903   }
904 
905   bool changed = false;
906   for (auto it = entries_.begin(); it != entries_.end();) {
907     auto next_it = std::next(it);
908 
909     if (host_filter.Run(GetHostname(it->first.host))) {
910       entries_.erase(it);
911       changed = true;
912     }
913 
914     it = next_it;
915   }
916 
917   if (delegate_ && changed)
918     delegate_->ScheduleWrite();
919 }
920 
GetList(base::Value::List & entry_list,bool include_staleness,SerializationType serialization_type) const921 void HostCache::GetList(base::Value::List& entry_list,
922                         bool include_staleness,
923                         SerializationType serialization_type) const {
924   entry_list.clear();
925 
926   for (const auto& pair : entries_) {
927     const Key& key = pair.first;
928     const Entry& entry = pair.second;
929 
930     base::Value network_anonymization_key_value;
931     if (serialization_type == SerializationType::kRestorable) {
932       // Don't save entries associated with ephemeral NetworkAnonymizationKeys.
933       if (!key.network_anonymization_key.ToValue(
934               &network_anonymization_key_value)) {
935         continue;
936       }
937     } else {
938       // ToValue() fails for transient NIKs, since they should never be
939       // serialized to disk in a restorable format, so use ToDebugString() when
940       // serializing for debugging instead of for restoring from disk.
941       network_anonymization_key_value =
942           base::Value(key.network_anonymization_key.ToDebugString());
943     }
944 
945     base::Value::Dict entry_dict = entry.GetAsValue(include_staleness);
946 
947     const auto* host = absl::get_if<url::SchemeHostPort>(&key.host);
948     if (host) {
949       entry_dict.Set(kSchemeKey, host->scheme());
950       entry_dict.Set(kHostnameKey, host->host());
951       entry_dict.Set(kPortKey, host->port());
952     } else {
953       entry_dict.Set(kHostnameKey, absl::get<std::string>(key.host));
954     }
955 
956     entry_dict.Set(kDnsQueryTypeKey,
957                    base::strict_cast<int>(key.dns_query_type));
958     entry_dict.Set(kFlagsKey, key.host_resolver_flags);
959     entry_dict.Set(kHostResolverSourceKey,
960                    base::strict_cast<int>(key.host_resolver_source));
961     entry_dict.Set(kNetworkAnonymizationKey,
962                    std::move(network_anonymization_key_value));
963     entry_dict.Set(kSecureKey, key.secure);
964 
965     entry_list.Append(std::move(entry_dict));
966   }
967 }
968 
RestoreFromListValue(const base::Value::List & old_cache)969 bool HostCache::RestoreFromListValue(const base::Value::List& old_cache) {
970   // Reset the restore size to 0.
971   restore_size_ = 0;
972 
973   for (const auto& entry : old_cache) {
974     // If the cache is already full, don't bother prioritizing what to evict,
975     // just stop restoring.
976     if (size() == max_entries_)
977       break;
978 
979     if (!entry.is_dict())
980       return false;
981 
982     const base::Value::Dict& entry_dict = entry.GetDict();
983     const std::string* hostname_ptr = entry_dict.FindString(kHostnameKey);
984     if (!hostname_ptr || !IsValidHostname(*hostname_ptr)) {
985       return false;
986     }
987 
988     // Use presence of scheme to determine host type.
989     const std::string* scheme_ptr = entry_dict.FindString(kSchemeKey);
990     absl::variant<url::SchemeHostPort, std::string> host;
991     if (scheme_ptr) {
992       absl::optional<int> port = entry_dict.FindInt(kPortKey);
993       if (!port || !base::IsValueInRangeForNumericType<uint16_t>(port.value()))
994         return false;
995 
996       url::SchemeHostPort scheme_host_port(*scheme_ptr, *hostname_ptr,
997                                            port.value());
998       if (!scheme_host_port.IsValid())
999         return false;
1000       host = std::move(scheme_host_port);
1001     } else {
1002       host = *hostname_ptr;
1003     }
1004 
1005     const std::string* expiration_ptr = entry_dict.FindString(kExpirationKey);
1006     absl::optional<int> maybe_flags = entry_dict.FindInt(kFlagsKey);
1007     if (expiration_ptr == nullptr || !maybe_flags.has_value())
1008       return false;
1009     std::string expiration(*expiration_ptr);
1010     HostResolverFlags flags = maybe_flags.value();
1011 
1012     absl::optional<int> maybe_dns_query_type =
1013         entry_dict.FindInt(kDnsQueryTypeKey);
1014     if (!maybe_dns_query_type.has_value())
1015       return false;
1016     absl::optional<DnsQueryType> dns_query_type =
1017         GetDnsQueryType(maybe_dns_query_type.value());
1018     if (!dns_query_type.has_value())
1019       return false;
1020     // HostResolverSource is optional.
1021     int host_resolver_source =
1022         entry_dict.FindInt(kHostResolverSourceKey)
1023             .value_or(base::strict_cast<int>(HostResolverSource::ANY));
1024 
1025     const base::Value* network_anonymization_key_value =
1026         entry_dict.Find(kNetworkAnonymizationKey);
1027     NetworkAnonymizationKey network_anonymization_key;
1028     if (!network_anonymization_key_value ||
1029         network_anonymization_key_value->type() == base::Value::Type::STRING ||
1030         !NetworkAnonymizationKey::FromValue(*network_anonymization_key_value,
1031                                             &network_anonymization_key)) {
1032       return false;
1033     }
1034 
1035     bool secure = entry_dict.FindBool(kSecureKey).value_or(false);
1036 
1037     int error = OK;
1038     const base::Value::List* ip_endpoints_list = nullptr;
1039     const base::Value::List* endpoint_metadatas_list = nullptr;
1040     const base::Value::List* aliases_list = nullptr;
1041     const base::Value::List* legacy_addresses_list = nullptr;
1042     const base::Value::List* text_records_list = nullptr;
1043     const base::Value::List* hostname_records_list = nullptr;
1044     const base::Value::List* host_ports_list = nullptr;
1045     const base::Value::List* canonical_names_list = nullptr;
1046     absl::optional<int> maybe_error = entry_dict.FindInt(kNetErrorKey);
1047     absl::optional<bool> maybe_pinned = entry_dict.FindBool(kPinnedKey);
1048     if (maybe_error.has_value()) {
1049       error = maybe_error.value();
1050     } else {
1051       ip_endpoints_list = entry_dict.FindList(kIpEndpointsKey);
1052       endpoint_metadatas_list = entry_dict.FindList(kEndpointMetadatasKey);
1053       aliases_list = entry_dict.FindList(kAliasesKey);
1054       legacy_addresses_list = entry_dict.FindList(kAddressesKey);
1055       text_records_list = entry_dict.FindList(kTextRecordsKey);
1056       hostname_records_list = entry_dict.FindList(kHostnameResultsKey);
1057       host_ports_list = entry_dict.FindList(kHostPortsKey);
1058       canonical_names_list = entry_dict.FindList(kCanonicalNamesKey);
1059 
1060       if ((hostname_records_list == nullptr && host_ports_list != nullptr) ||
1061           (hostname_records_list != nullptr && host_ports_list == nullptr)) {
1062         return false;
1063       }
1064     }
1065 
1066     int64_t time_internal;
1067     if (!base::StringToInt64(expiration, &time_internal))
1068       return false;
1069 
1070     base::TimeTicks expiration_time =
1071         tick_clock_->NowTicks() -
1072         (base::Time::Now() - base::Time::FromInternalValue(time_internal));
1073 
1074     absl::optional<std::vector<IPEndPoint>> ip_endpoints;
1075     if (ip_endpoints_list) {
1076       ip_endpoints.emplace();
1077       for (const base::Value& ip_endpoint_value : *ip_endpoints_list) {
1078         absl::optional<IPEndPoint> ip_endpoint =
1079             IpEndpointFromValue(ip_endpoint_value);
1080         if (!ip_endpoint)
1081           return false;
1082         ip_endpoints->push_back(std::move(ip_endpoint).value());
1083       }
1084     }
1085 
1086     absl::optional<
1087         std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata>>
1088         endpoint_metadatas;
1089     if (endpoint_metadatas_list) {
1090       endpoint_metadatas.emplace();
1091       for (const base::Value& endpoint_metadata_value :
1092            *endpoint_metadatas_list) {
1093         absl::optional<
1094             std::pair<HttpsRecordPriority, ConnectionEndpointMetadata>>
1095             pair = EndpointMetadataPairFromValue(endpoint_metadata_value);
1096         if (!pair)
1097           return false;
1098         endpoint_metadatas->insert(std::move(pair).value());
1099       }
1100     }
1101 
1102     absl::optional<std::set<std::string>> aliases;
1103     if (aliases_list) {
1104       aliases.emplace();
1105       for (const base::Value& alias_value : *aliases_list) {
1106         if (!alias_value.is_string())
1107           return false;
1108         aliases->insert(alias_value.GetString());
1109       }
1110     }
1111 
1112     // `addresses` field was supported until M105. We keep reading this field
1113     // for backward compatibility for several milestones.
1114     if (legacy_addresses_list) {
1115       if (ip_endpoints)
1116         return false;
1117       if (!IPEndPointsFromLegacyAddressListValue(*legacy_addresses_list,
1118                                                  &ip_endpoints)) {
1119         return false;
1120       }
1121     }
1122 
1123     absl::optional<std::vector<std::string>> text_records;
1124     if (text_records_list) {
1125       text_records.emplace();
1126       for (const base::Value& value : *text_records_list) {
1127         if (!value.is_string())
1128           return false;
1129         text_records.value().push_back(value.GetString());
1130       }
1131     }
1132 
1133     absl::optional<std::vector<HostPortPair>> hostname_records;
1134     if (hostname_records_list) {
1135       DCHECK(host_ports_list);
1136       if (hostname_records_list->size() != host_ports_list->size()) {
1137         return false;
1138       }
1139 
1140       hostname_records.emplace();
1141       for (size_t i = 0; i < hostname_records_list->size(); ++i) {
1142         if (!(*hostname_records_list)[i].is_string() ||
1143             !(*host_ports_list)[i].is_int() ||
1144             !base::IsValueInRangeForNumericType<uint16_t>(
1145                 (*host_ports_list)[i].GetInt())) {
1146           return false;
1147         }
1148         hostname_records.value().emplace_back(
1149             (*hostname_records_list)[i].GetString(),
1150             base::checked_cast<uint16_t>((*host_ports_list)[i].GetInt()));
1151       }
1152     }
1153 
1154     absl::optional<std::set<std::string>> canonical_names;
1155     if (canonical_names_list) {
1156       canonical_names = std::set<std::string>();
1157       for (const auto& item : *canonical_names_list) {
1158         const std::string* name = item.GetIfString();
1159         if (!name)
1160           return false;
1161         canonical_names->insert(*name);
1162       }
1163     }
1164 
1165     // We do not intend to serialize experimental results with the host cache.
1166     absl::optional<std::vector<bool>> experimental_results;
1167 
1168     // Assume an empty endpoints list and an empty aliases if we have an address
1169     // type and no results.
1170     if (IsAddressType(dns_query_type.value()) && !text_records &&
1171         !hostname_records) {
1172       if (!ip_endpoints) {
1173         ip_endpoints.emplace();
1174       }
1175       if (!aliases) {
1176         aliases.emplace();
1177       }
1178     }
1179     Key key(std::move(host), dns_query_type.value(), flags,
1180             static_cast<HostResolverSource>(host_resolver_source),
1181             network_anonymization_key);
1182     key.secure = secure;
1183 
1184     // If the key is already in the cache, assume it's more recent and don't
1185     // replace the entry.
1186     auto found = entries_.find(key);
1187     if (found == entries_.end()) {
1188       Entry new_entry(error, std::move(ip_endpoints),
1189                       std::move(endpoint_metadatas), std::move(aliases),
1190                       std::move(text_records), std::move(hostname_records),
1191                       std::move(experimental_results), Entry::SOURCE_UNKNOWN,
1192                       expiration_time, network_changes_ - 1);
1193       new_entry.set_pinning(maybe_pinned.value_or(false));
1194       new_entry.set_canonical_names(std::move(canonical_names));
1195       AddEntry(key, std::move(new_entry));
1196       restore_size_++;
1197     }
1198   }
1199   return true;
1200 }
1201 
size() const1202 size_t HostCache::size() const {
1203   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1204   return entries_.size();
1205 }
1206 
max_entries() const1207 size_t HostCache::max_entries() const {
1208   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1209   return max_entries_;
1210 }
1211 
1212 // static
CreateDefaultCache()1213 std::unique_ptr<HostCache> HostCache::CreateDefaultCache() {
1214 #if defined(ENABLE_BUILT_IN_DNS)
1215   const size_t kDefaultMaxEntries = 1000;
1216 #else
1217   const size_t kDefaultMaxEntries = 100;
1218 #endif
1219   return std::make_unique<HostCache>(kDefaultMaxEntries);
1220 }
1221 
EvictOneEntry(base::TimeTicks now)1222 bool HostCache::EvictOneEntry(base::TimeTicks now) {
1223   DCHECK_LT(0u, entries_.size());
1224 
1225   absl::optional<net::HostCache::EntryMap::iterator> oldest_it;
1226   for (auto it = entries_.begin(); it != entries_.end(); ++it) {
1227     const Entry& entry = it->second;
1228     if (HasActivePin(entry)) {
1229       continue;
1230     }
1231 
1232     if (!oldest_it) {
1233       oldest_it = it;
1234       continue;
1235     }
1236 
1237     const Entry& oldest = (*oldest_it)->second;
1238     if ((entry.expires() < oldest.expires()) &&
1239         (entry.IsStale(now, network_changes_) ||
1240          !oldest.IsStale(now, network_changes_))) {
1241       oldest_it = it;
1242     }
1243   }
1244 
1245   if (oldest_it) {
1246     entries_.erase(*oldest_it);
1247     return true;
1248   }
1249   return false;
1250 }
1251 
HasActivePin(const Entry & entry)1252 bool HostCache::HasActivePin(const Entry& entry) {
1253   return entry.pinning().value_or(false) &&
1254          entry.network_changes() == network_changes();
1255 }
1256 
1257 }  // namespace net
1258 
1259 // Debug logging support
operator <<(std::ostream & out,const net::HostCache::EntryStaleness & s)1260 std::ostream& operator<<(std::ostream& out,
1261                          const net::HostCache::EntryStaleness& s) {
1262   return out << "EntryStaleness{" << s.expired_by << ", " << s.network_changes
1263              << ", " << s.stale_hits << "}";
1264 }
1265