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