• 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 <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