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