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 #ifndef NET_DNS_HOST_CACHE_H_ 6 #define NET_DNS_HOST_CACHE_H_ 7 8 #include <stddef.h> 9 10 #include <functional> 11 #include <map> 12 #include <memory> 13 #include <ostream> 14 #include <set> 15 #include <string> 16 #include <tuple> 17 #include <utility> 18 #include <vector> 19 20 #include "base/check.h" 21 #include "base/gtest_prod_util.h" 22 #include "base/memory/raw_ptr.h" 23 #include "base/numerics/clamped_math.h" 24 #include "base/threading/thread_checker.h" 25 #include "base/time/time.h" 26 #include "base/types/optional_util.h" 27 #include "base/values.h" 28 #include "net/base/address_family.h" 29 #include "net/base/connection_endpoint_metadata.h" 30 #include "net/base/expiring_cache.h" 31 #include "net/base/host_port_pair.h" 32 #include "net/base/ip_endpoint.h" 33 #include "net/base/net_errors.h" 34 #include "net/base/net_export.h" 35 #include "net/base/network_anonymization_key.h" 36 #include "net/dns/public/dns_query_type.h" 37 #include "net/dns/public/host_resolver_results.h" 38 #include "net/dns/public/host_resolver_source.h" 39 #include "net/log/net_log_capture_mode.h" 40 #include "third_party/abseil-cpp/absl/types/optional.h" 41 #include "third_party/abseil-cpp/absl/types/variant.h" 42 #include "url/scheme_host_port.h" 43 44 namespace base { 45 class TickClock; 46 } // namespace base 47 48 namespace net { 49 50 class HostResolverInternalResult; 51 52 // Cache used by HostResolver to map hostnames to their resolved result. 53 class NET_EXPORT HostCache { 54 public: 55 struct NET_EXPORT Key { 56 // Hostnames in `host` must not be IP literals. IP literals should be 57 // resolved directly to the IP address and not be stored/queried in 58 // HostCache. 59 Key(absl::variant<url::SchemeHostPort, std::string> host, 60 DnsQueryType dns_query_type, 61 HostResolverFlags host_resolver_flags, 62 HostResolverSource host_resolver_source, 63 const NetworkAnonymizationKey& network_anonymization_key); 64 Key(); 65 Key(const Key& key); 66 Key(Key&& key); 67 ~Key(); 68 69 // This is a helper used in comparing keys. The order of comparisons of 70 // `Key` fields is arbitrary, but the tuple is constructed with 71 // `dns_query_type` and `host_resolver_flags` before `host` under the 72 // assumption that integer comparisons are faster than string comparisons. GetTupleKey73 static auto GetTuple(const Key* key) { 74 return std::tie(key->dns_query_type, key->host_resolver_flags, key->host, 75 key->host_resolver_source, key->network_anonymization_key, 76 key->secure); 77 } 78 79 bool operator==(const Key& other) const { 80 return GetTuple(this) == GetTuple(&other); 81 } 82 83 bool operator!=(const Key& other) const { 84 return GetTuple(this) != GetTuple(&other); 85 } 86 87 bool operator<(const Key& other) const { 88 return GetTuple(this) < GetTuple(&other); 89 } 90 91 absl::variant<url::SchemeHostPort, std::string> host; 92 DnsQueryType dns_query_type = DnsQueryType::UNSPECIFIED; 93 HostResolverFlags host_resolver_flags = 0; 94 HostResolverSource host_resolver_source = HostResolverSource::ANY; 95 NetworkAnonymizationKey network_anonymization_key; 96 bool secure = false; 97 }; 98 99 struct NET_EXPORT EntryStaleness { 100 // Time since the entry's TTL has expired. Negative if not expired. 101 base::TimeDelta expired_by; 102 103 // Number of network changes since this result was cached. 104 int network_changes; 105 106 // Number of hits to the cache entry while stale (expired or past-network). 107 int stale_hits; 108 is_staleEntryStaleness109 bool is_stale() const { 110 return network_changes > 0 || expired_by >= base::TimeDelta(); 111 } 112 }; 113 114 // Stores the latest address list that was looked up for a hostname. 115 class NET_EXPORT Entry { 116 public: 117 enum Source : int { 118 // Address list was obtained from an unknown source. 119 SOURCE_UNKNOWN, 120 // Address list was obtained via a DNS lookup. 121 SOURCE_DNS, 122 // Address list was obtained by searching a HOSTS file. 123 SOURCE_HOSTS, 124 // Address list was a preset from the DnsConfig. 125 SOURCE_CONFIG, 126 }; 127 128 // |ttl=absl::nullopt| for unknown TTL. 129 template <typename T> Entry(int error,T && results,Source source,absl::optional<base::TimeDelta> ttl)130 Entry(int error, 131 T&& results, 132 Source source, 133 absl::optional<base::TimeDelta> ttl) 134 : error_(error), 135 source_(source), 136 ttl_(ttl ? ttl.value() : kUnknownTtl) { 137 DCHECK(!ttl || ttl.value() >= base::TimeDelta()); 138 SetResult(std::forward<T>(results)); 139 } 140 141 // Use when |ttl| is unknown. 142 template <typename T> Entry(int error,T && results,Source source)143 Entry(int error, T&& results, Source source) 144 : Entry(error, std::forward<T>(results), source, absl::nullopt) {} 145 146 // Use for address entries. 147 Entry(int error, 148 std::vector<IPEndPoint> ip_endpoints, 149 std::set<std::string> aliases, 150 Source source, 151 absl::optional<base::TimeDelta> ttl = absl::nullopt); 152 153 // For errors with no |results|. 154 Entry(int error, 155 Source source, 156 absl::optional<base::TimeDelta> ttl = absl::nullopt); 157 158 // Adaptor to construct from HostResolverInternalResults. Only supports 159 // results extracted from a single DnsTransaction. 160 Entry(std::vector<std::unique_ptr<HostResolverInternalResult>> results, 161 base::Time now, 162 base::TimeTicks now_ticks); 163 164 Entry(const Entry& entry); 165 Entry(Entry&& entry); 166 ~Entry(); 167 168 Entry& operator=(const Entry& entry); 169 Entry& operator=(Entry&& entry); 170 171 bool operator==(const Entry& other) const { 172 return ContentsEqual(other) && 173 std::tie(source_, pinning_, ttl_, expires_, network_changes_, 174 total_hits_, stale_hits_) == 175 std::tie(other.source_, other.pinning_, other.ttl_, 176 other.expires_, other.network_changes_, 177 other.total_hits_, other.stale_hits_); 178 } 179 ContentsEqual(const Entry & other)180 bool ContentsEqual(const Entry& other) const { 181 return std::tie(error_, ip_endpoints_, endpoint_metadatas_, aliases_, 182 text_records_, hostnames_, https_record_compatibility_, 183 canonical_names_) == 184 std::tie( 185 other.error_, other.ip_endpoints_, other.endpoint_metadatas_, 186 other.aliases_, other.text_records_, other.hostnames_, 187 other.https_record_compatibility_, other.canonical_names_); 188 } 189 error()190 int error() const { return error_; } did_complete()191 bool did_complete() const { 192 return error_ != ERR_NETWORK_CHANGED && 193 error_ != ERR_HOST_RESOLVER_QUEUE_TOO_LARGE; 194 } set_error(int error)195 void set_error(int error) { error_ = error; } 196 absl::optional<std::vector<HostResolverEndpointResult>> GetEndpoints() 197 const; ip_endpoints()198 const std::vector<IPEndPoint>* ip_endpoints() const { 199 return base::OptionalToPtr(ip_endpoints_); 200 } set_ip_endpoints(absl::optional<std::vector<IPEndPoint>> ip_endpoints)201 void set_ip_endpoints( 202 absl::optional<std::vector<IPEndPoint>> ip_endpoints) { 203 ip_endpoints_ = std::move(ip_endpoints); 204 } 205 absl::optional<std::vector<ConnectionEndpointMetadata>> GetMetadatas() 206 const; ClearMetadatas()207 void ClearMetadatas() { endpoint_metadatas_.reset(); } aliases()208 const std::set<std::string>* aliases() const { 209 return base::OptionalToPtr(aliases_); 210 } set_aliases(std::set<std::string> aliases)211 void set_aliases(std::set<std::string> aliases) { 212 aliases_ = std::move(aliases); 213 } text_records()214 const absl::optional<std::vector<std::string>>& text_records() const { 215 return text_records_; 216 } set_text_records(absl::optional<std::vector<std::string>> text_records)217 void set_text_records( 218 absl::optional<std::vector<std::string>> text_records) { 219 text_records_ = std::move(text_records); 220 } hostnames()221 const absl::optional<std::vector<HostPortPair>>& hostnames() const { 222 return hostnames_; 223 } set_hostnames(absl::optional<std::vector<HostPortPair>> hostnames)224 void set_hostnames(absl::optional<std::vector<HostPortPair>> hostnames) { 225 hostnames_ = std::move(hostnames); 226 } https_record_compatibility()227 const std::vector<bool>* https_record_compatibility() const { 228 return base::OptionalToPtr(https_record_compatibility_); 229 } set_https_record_compatibility(absl::optional<std::vector<bool>> https_record_compatibility)230 void set_https_record_compatibility( 231 absl::optional<std::vector<bool>> https_record_compatibility) { 232 https_record_compatibility_ = std::move(https_record_compatibility); 233 } pinning()234 absl::optional<bool> pinning() const { return pinning_; } set_pinning(absl::optional<bool> pinning)235 void set_pinning(absl::optional<bool> pinning) { pinning_ = pinning; } 236 canonical_names()237 const absl::optional<std::set<std::string>>& canonical_names() const { 238 return canonical_names_; 239 } set_canonical_names(absl::optional<std::set<std::string>> canonical_names)240 void set_canonical_names( 241 absl::optional<std::set<std::string>> canonical_names) { 242 canonical_names_ = std::move(canonical_names); 243 } 244 source()245 Source source() const { return source_; } has_ttl()246 bool has_ttl() const { return ttl_ >= base::TimeDelta(); } ttl()247 base::TimeDelta ttl() const { return ttl_; } 248 absl::optional<base::TimeDelta> GetOptionalTtl() const; set_ttl(base::TimeDelta ttl)249 void set_ttl(base::TimeDelta ttl) { ttl_ = ttl; } 250 expires()251 base::TimeTicks expires() const { return expires_; } 252 253 // Public for the net-internals UI. network_changes()254 int network_changes() const { return network_changes_; } 255 256 // Merge |front| and |back|, representing results from multiple transactions 257 // for the same overall host resolution query. 258 // 259 // Merges lists, placing elements from |front| before elements from |back|. 260 // Further, dedupes legacy address lists and moves IPv6 addresses before 261 // IPv4 addresses (maintaining stable order otherwise). 262 // 263 // Fields that cannot be merged take precedence from |front|. 264 static Entry MergeEntries(Entry front, Entry back); 265 266 // Creates a value representation of the entry for use with NetLog. 267 base::Value NetLogParams() const; 268 269 // Creates a copy of |this| with the port of all address and hostname values 270 // set to |port| if the current port is 0. Preserves any non-zero ports. 271 HostCache::Entry CopyWithDefaultPort(uint16_t port) const; 272 273 private: 274 using HttpsRecordPriority = uint16_t; 275 276 friend class HostCache; 277 278 static constexpr base::TimeDelta kUnknownTtl = base::Seconds(-1); 279 280 Entry(const Entry& entry, 281 base::TimeTicks now, 282 base::TimeDelta ttl, 283 int network_changes); 284 285 Entry(int error, 286 absl::optional<std::vector<IPEndPoint>> ip_endpoints, 287 absl::optional< 288 std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata>> 289 endpoint_metadatas, 290 absl::optional<std::set<std::string>> aliases, 291 absl::optional<std::vector<std::string>>&& text_results, 292 absl::optional<std::vector<HostPortPair>>&& hostnames, 293 absl::optional<std::vector<bool>>&& https_record_compatibility, 294 Source source, 295 base::TimeTicks expires, 296 int network_changes); 297 298 void PrepareForCacheInsertion(); 299 SetResult(std::multimap<HttpsRecordPriority,ConnectionEndpointMetadata> endpoint_metadatas)300 void SetResult( 301 std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata> 302 endpoint_metadatas) { 303 endpoint_metadatas_ = std::move(endpoint_metadatas); 304 } SetResult(std::vector<std::string> text_records)305 void SetResult(std::vector<std::string> text_records) { 306 text_records_ = std::move(text_records); 307 } SetResult(std::vector<HostPortPair> hostnames)308 void SetResult(std::vector<HostPortPair> hostnames) { 309 hostnames_ = std::move(hostnames); 310 } SetResult(std::vector<bool> https_record_compatibility)311 void SetResult(std::vector<bool> https_record_compatibility) { 312 https_record_compatibility_ = std::move(https_record_compatibility); 313 } 314 total_hits()315 int total_hits() const { return total_hits_; } stale_hits()316 int stale_hits() const { return stale_hits_; } 317 318 bool IsStale(base::TimeTicks now, int network_changes) const; 319 void CountHit(bool hit_is_stale); 320 void GetStaleness(base::TimeTicks now, 321 int network_changes, 322 EntryStaleness* out) const; 323 324 base::Value::Dict GetAsValue(bool include_staleness) const; 325 326 // The resolve results for this entry. 327 int error_ = ERR_FAILED; 328 absl::optional<std::vector<IPEndPoint>> ip_endpoints_; 329 absl::optional< 330 std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata>> 331 endpoint_metadatas_; 332 absl::optional<std::set<std::string>> aliases_; 333 absl::optional<std::vector<std::string>> text_records_; 334 absl::optional<std::vector<HostPortPair>> hostnames_; 335 336 // Bool of whether each HTTPS record received is compatible 337 // (draft-ietf-dnsop-svcb-https-08#section-8), considering alias records to 338 // always be compatible. 339 // 340 // This field may be reused for experimental query types to record 341 // successfully received records of that experimental type. 342 // 343 // For either usage, cleared before inserting in cache. 344 absl::optional<std::vector<bool>> https_record_compatibility_; 345 346 // Where results were obtained (e.g. DNS lookup, hosts file, etc). 347 Source source_ = SOURCE_UNKNOWN; 348 // If true, this entry cannot be evicted from the cache until after the next 349 // network change. When an Entry is replaced by one whose pinning flag 350 // is not set, HostCache will copy this flag to the replacement. 351 // If this flag is null, HostCache will set it to false for simplicity. 352 // Note: This flag is not yet used, and should be removed if the proposals 353 // for followup queries after insecure/expired bootstrap are abandoned (see 354 // TODO(crbug.com/1200908) in HostResolverManager). 355 absl::optional<bool> pinning_; 356 357 // The final name at the end of the alias chain that was the record name for 358 // the A/AAAA records. 359 absl::optional<std::set<std::string>> canonical_names_; 360 361 // TTL obtained from the nameserver. Negative if unknown. 362 base::TimeDelta ttl_ = kUnknownTtl; 363 364 base::TimeTicks expires_; 365 // Copied from the cache's network_changes_ when the entry is set; can 366 // later be compared to it to see if the entry was received on the current 367 // network. 368 int network_changes_ = -1; 369 // Use clamped math to cap hit counts at INT_MAX. 370 base::ClampedNumeric<int> total_hits_ = 0; 371 base::ClampedNumeric<int> stale_hits_ = 0; 372 }; 373 374 // Interface for interacting with persistent storage, to be provided by the 375 // embedder. Does not include support for writes that must happen immediately. 376 class PersistenceDelegate { 377 public: 378 // Calling ScheduleWrite() signals that data has changed and should be 379 // written to persistent storage. The write might be delayed. 380 virtual void ScheduleWrite() = 0; 381 }; 382 383 using EntryMap = std::map<Key, Entry>; 384 385 // The two ways to serialize the cache to a value. 386 enum class SerializationType { 387 // Entries with transient NetworkAnonymizationKeys are not serialized, and 388 // RestoreFromListValue() can load the returned value. 389 kRestorable, 390 // Entries with transient NetworkAnonymizationKeys are serialized, and 391 // RestoreFromListValue() cannot load the returned value, since the debug 392 // serialization of NetworkAnonymizationKeys is used instead of the 393 // deserializable representation. 394 kDebug, 395 }; 396 397 // A HostCache::EntryStaleness representing a non-stale (fresh) cache entry. 398 static const HostCache::EntryStaleness kNotStale; 399 400 // Constructs a HostCache that stores up to |max_entries|. 401 explicit HostCache(size_t max_entries); 402 403 HostCache(const HostCache&) = delete; 404 HostCache& operator=(const HostCache&) = delete; 405 406 ~HostCache(); 407 408 // Returns a pointer to the matching (key, entry) pair, which is valid at time 409 // |now|. If |ignore_secure| is true, ignores the secure field in |key| when 410 // looking for a match. If there is no matching entry, returns NULL. 411 const std::pair<const Key, Entry>* Lookup(const Key& key, 412 base::TimeTicks now, 413 bool ignore_secure = false); 414 415 // Returns a pointer to the matching (key, entry) pair, whether it is valid or 416 // stale at time |now|. Fills in |stale_out| with information about how stale 417 // it is. If |ignore_secure| is true, ignores the secure field in |key| when 418 // looking for a match. If there is no matching entry, returns NULL. 419 const std::pair<const Key, Entry>* LookupStale(const Key& key, 420 base::TimeTicks now, 421 EntryStaleness* stale_out, 422 bool ignore_secure = false); 423 424 // Overwrites or creates an entry for |key|. 425 // |entry| is the value to set, |now| is the current time 426 // |ttl| is the "time to live". 427 void Set(const Key& key, 428 const Entry& entry, 429 base::TimeTicks now, 430 base::TimeDelta ttl); 431 432 // Checks whether an entry exists for `hostname`. 433 // If so, returns the matching key and writes the source (e.g. DNS, HOSTS 434 // file, etc.) to `source_out` and the staleness to `stale_out` (if they are 435 // not null). If no entry exists, returns nullptr. 436 // 437 // For testing use only and not very performant. Production code should only 438 // do lookups by precise Key. 439 const HostCache::Key* GetMatchingKeyForTesting( 440 base::StringPiece hostname, 441 HostCache::Entry::Source* source_out = nullptr, 442 HostCache::EntryStaleness* stale_out = nullptr) const; 443 444 // Marks all entries as stale on account of a network change. 445 void Invalidate(); 446 447 void set_persistence_delegate(PersistenceDelegate* delegate); 448 set_tick_clock_for_testing(const base::TickClock * tick_clock)449 void set_tick_clock_for_testing(const base::TickClock* tick_clock) { 450 tick_clock_ = tick_clock; 451 } 452 453 // Empties the cache. 454 void clear(); 455 456 // Clears hosts matching |host_filter| from the cache. 457 void ClearForHosts( 458 const base::RepeatingCallback<bool(const std::string&)>& host_filter); 459 460 // Fills the provided base::Value with the contents of the cache for 461 // serialization. `entry_list` must be non-null list, and will be cleared 462 // before adding the cache contents. 463 void GetList(base::Value::List& entry_list, 464 bool include_staleness, 465 SerializationType serialization_type) const; 466 // Takes a base::Value list representing cache entries and stores them in the 467 // cache, skipping any that already have entries. Returns true on success, 468 // false on failure. 469 bool RestoreFromListValue(const base::Value::List& old_cache); 470 // Returns the number of entries that were restored in the last call to 471 // RestoreFromListValue(). last_restore_size()472 size_t last_restore_size() const { return restore_size_; } 473 474 // Returns the number of entries in the cache. 475 size_t size() const; 476 477 // Following are used by net_internals UI. 478 size_t max_entries() const; network_changes()479 int network_changes() const { return network_changes_; } entries()480 const EntryMap& entries() const { return entries_; } 481 482 // Creates a default cache. 483 static std::unique_ptr<HostCache> CreateDefaultCache(); 484 485 private: 486 FRIEND_TEST_ALL_PREFIXES(HostCacheTest, NoCache); 487 488 enum SetOutcome : int; 489 enum LookupOutcome : int; 490 enum EraseReason : int; 491 492 // Returns the result that is least stale, based on the number of network 493 // changes since the result was cached. If the results are equally stale, 494 // prefers a securely retrieved result. Returns nullptr if both results are 495 // nullptr. 496 static std::pair<const HostCache::Key, HostCache::Entry>* 497 GetLessStaleMoreSecureResult( 498 base::TimeTicks now, 499 std::pair<const HostCache::Key, HostCache::Entry>* result1, 500 std::pair<const HostCache::Key, HostCache::Entry>* result2); 501 502 // Returns matching key and entry from cache and nullptr if no match. Ignores 503 // the secure field in |initial_key| if |ignore_secure| is true. 504 std::pair<const Key, Entry>* LookupInternalIgnoringFields( 505 const Key& initial_key, 506 base::TimeTicks now, 507 bool ignore_secure); 508 509 // Returns matching key and entry from cache and nullptr if no match. An exact 510 // match for |key| is required. 511 std::pair<const Key, Entry>* LookupInternal(const Key& key); 512 513 // Returns true if this HostCache can contain no entries. caching_is_disabled()514 bool caching_is_disabled() const { return max_entries_ == 0; } 515 516 // Returns true if an entry was removed. 517 bool EvictOneEntry(base::TimeTicks now); 518 // Helper to check if an Entry is currently pinned in the cache. 519 bool HasActivePin(const Entry& entry); 520 // Helper to insert an Entry into the cache. 521 void AddEntry(const Key& key, Entry&& entry); 522 523 // Map from hostname (presumably in lowercase canonicalized format) to 524 // a resolved result entry. 525 EntryMap entries_; 526 size_t max_entries_; 527 int network_changes_ = 0; 528 // Number of cache entries that were restored in the last call to 529 // RestoreFromListValue(). Used in histograms. 530 size_t restore_size_ = 0; 531 532 raw_ptr<PersistenceDelegate> delegate_ = nullptr; 533 // Shared tick clock, overridden for testing. 534 raw_ptr<const base::TickClock> tick_clock_; 535 536 THREAD_CHECKER(thread_checker_); 537 }; 538 539 } // namespace net 540 541 // Debug logging support 542 std::ostream& operator<<(std::ostream& out, 543 const net::HostCache::EntryStaleness& s); 544 545 #endif // NET_DNS_HOST_CACHE_H_ 546