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