1 // Copyright 2023 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_RESOLVER_CACHE_H_ 6 #define NET_DNS_HOST_RESOLVER_CACHE_H_ 7 8 #include <cstddef> 9 #include <map> 10 #include <memory> 11 #include <optional> 12 #include <string> 13 #include <string_view> 14 #include <tuple> 15 #include <utility> 16 #include <vector> 17 18 #include "base/memory/raw_ref.h" 19 #include "base/time/clock.h" 20 #include "base/time/default_clock.h" 21 #include "base/time/default_tick_clock.h" 22 #include "base/time/time.h" 23 #include "base/values.h" 24 #include "net/base/net_export.h" 25 #include "net/base/network_anonymization_key.h" 26 #include "net/dns/public/dns_query_type.h" 27 #include "net/dns/public/host_resolver_source.h" 28 29 namespace net { 30 31 class HostResolverInternalResult; 32 33 // Cache used by HostResolverManager to save previously resolved information. 34 class NET_EXPORT HostResolverCache final { 35 public: 36 struct StaleLookupResult { 37 StaleLookupResult(const HostResolverInternalResult& result, 38 std::optional<base::TimeDelta> expired_by, 39 bool stale_by_generation); 40 ~StaleLookupResult() = default; 41 42 const raw_ref<const HostResolverInternalResult> result; 43 44 // Time since the result's TTL has expired. nullopt if not expired. 45 const std::optional<base::TimeDelta> expired_by; 46 47 // True if result is stale due to a call to 48 // HostResolverCache::MakeAllResultsStale(). 49 const bool stale_by_generation; 50 IsStaleStaleLookupResult51 bool IsStale() const { 52 return stale_by_generation || expired_by.has_value(); 53 } 54 }; 55 56 explicit HostResolverCache( 57 size_t max_results, 58 const base::Clock& clock = *base::DefaultClock::GetInstance(), 59 const base::TickClock& tick_clock = 60 *base::DefaultTickClock::GetInstance()); 61 ~HostResolverCache(); 62 63 // Move-only. 64 HostResolverCache(HostResolverCache&&); 65 HostResolverCache& operator=(HostResolverCache&&); 66 67 // Lookup an active (non-stale) cached result matching the given criteria. If 68 // `query_type` is `DnsQueryType::UNSPECIFIED`, `source` is 69 // `HostResolverSource::ANY`, or `secure` is `std::nullopt`, it is a wildcard 70 // that can match for any cached parameter of that type. In cases where a 71 // wildcard lookup leads to multiple matching results, only one result will be 72 // returned, preferring first the most secure result and then the most 73 // recently set one. Additionally, if a cached result has 74 // `DnsQueryType::UNSPECIFIED`, it will match for any argument of 75 // `query_type`. 76 // 77 // Returns nullptr on cache miss (no active result matches the given 78 // criteria). 79 const HostResolverInternalResult* Lookup( 80 std::string_view domain_name, 81 const NetworkAnonymizationKey& network_anonymization_key, 82 DnsQueryType query_type = DnsQueryType::UNSPECIFIED, 83 HostResolverSource source = HostResolverSource::ANY, 84 std::optional<bool> secure = std::nullopt) const; 85 86 // Lookup a cached result matching the given criteria. Unlike Lookup(), may 87 // return stale results. In cases where a wildcard lookup leads to multiple 88 // matching results, only one result will be returned, preferring active 89 // (non-stale) results, then the least stale by generation, then the least 90 // stale by time expiration, then the most secure, then the most recently set. 91 // 92 // Used to implement 93 // `HostResolver::ResolveHostParameters::CacheUsage::STALE_ALLOWED` behavior, 94 // which is itself primarily for usage by cronet::StaleHostResolver, but no 95 // assumptions are made here that this is Cronet-only behavior. 96 // 97 // Returns nullopt on cache miss (no active or stale result matches the given 98 // criteria). 99 std::optional<StaleLookupResult> LookupStale( 100 std::string_view domain_name, 101 const NetworkAnonymizationKey& network_anonymization_key, 102 DnsQueryType query_type = DnsQueryType::UNSPECIFIED, 103 HostResolverSource source = HostResolverSource::ANY, 104 std::optional<bool> secure = std::nullopt) const; 105 106 // Sets the result into the cache, replacing any previous result entries that 107 // would match the same criteria, even if a previous entry would have matched 108 // more criteria than the new one, e.g. if the previous entry used a wildcard 109 // `DnsQueryType::UNSPECIFIED`. 110 void Set(std::unique_ptr<HostResolverInternalResult> result, 111 const NetworkAnonymizationKey& network_anonymization_key, 112 HostResolverSource source, 113 bool secure); 114 115 // Makes all cached results considered stale. Typically used for network 116 // change to ensure cached results are only considered active for the current 117 // network. 118 void MakeAllResultsStale(); 119 120 // Serialization to later be deserialized. Only serializes the results likely 121 // to still be of value after serialization and deserialization, that is that 122 // results with a transient anonymization key are not included. 123 // 124 // Used to implement cronet::HostCachePersistenceManager, but no assumptions 125 // are made here that this is Cronet-only functionality. 126 base::Value Serialize() const; 127 128 // Deserialize value received from Serialize(). Results already contained in 129 // the cache are preferred, thus deserialized results are ignored if any 130 // previous result entries would match the same criteria, and deserialization 131 // stops on reaching max size, rather than evicting anything. Deserialized 132 // results are also always considered stale by generation. 133 // 134 // Returns false if `value` is malformed to be deserialized. 135 // 136 // Used to implement cronet::HostCachePersistenceManager, but no assumptions 137 // are made here that this is Cronet-only functionality. 138 bool RestoreFromValue(const base::Value& value); 139 140 // Serialize for output to debug logs, e.g. netlog. Serializes all results, 141 // including those with transient anonymization keys, and also serializes 142 // cache-wide data. Incompatible with base::Values returned from Serialize(), 143 // and cannot be used in RestoreFromValue(). 144 base::Value SerializeForLogging() const; 145 AtMaxSizeForTesting()146 bool AtMaxSizeForTesting() const { return entries_.size() >= max_entries_; } 147 148 private: 149 struct Key { 150 ~Key(); 151 152 std::string domain_name; 153 NetworkAnonymizationKey network_anonymization_key; 154 }; 155 156 struct KeyRef { 157 ~KeyRef() = default; 158 159 std::string_view domain_name; 160 const raw_ref<const NetworkAnonymizationKey> network_anonymization_key; 161 }; 162 163 // Allow comparing Key to KeyRef to allow refs for entry lookup. 164 struct KeyComparator { 165 using is_transparent = void; 166 167 ~KeyComparator() = default; 168 operatorKeyComparator169 bool operator()(const Key& lhs, const Key& rhs) const { 170 return std::tie(lhs.domain_name, lhs.network_anonymization_key) < 171 std::tie(rhs.domain_name, rhs.network_anonymization_key); 172 } 173 operatorKeyComparator174 bool operator()(const Key& lhs, const KeyRef& rhs) const { 175 return std::tie(lhs.domain_name, lhs.network_anonymization_key) < 176 std::tie(rhs.domain_name, *rhs.network_anonymization_key); 177 } 178 operatorKeyComparator179 bool operator()(const KeyRef& lhs, const Key& rhs) const { 180 return std::tie(lhs.domain_name, *lhs.network_anonymization_key) < 181 std::tie(rhs.domain_name, rhs.network_anonymization_key); 182 } 183 }; 184 185 struct Entry { 186 Entry(std::unique_ptr<HostResolverInternalResult> result, 187 HostResolverSource source, 188 bool secure, 189 int staleness_generation); 190 ~Entry(); 191 192 Entry(Entry&&); 193 Entry& operator=(Entry&&); 194 195 bool IsStale(base::Time now, 196 base::TimeTicks now_ticks, 197 int current_staleness_generation) const; 198 base::TimeDelta TimeUntilExpiration(base::Time now, 199 base::TimeTicks now_ticks) const; 200 201 std::unique_ptr<HostResolverInternalResult> result; 202 HostResolverSource source; 203 bool secure; 204 205 // The `HostResolverCache::staleness_generation_` value at the time this 206 // entry was created. Entry is stale if this does not match the current 207 // value. 208 int staleness_generation; 209 }; 210 211 using EntryMap = std::multimap<Key, Entry, KeyComparator>; 212 213 // Get all matching results, from most to least recently added. 214 std::vector<EntryMap::const_iterator> LookupInternal( 215 std::string_view domain_name, 216 const NetworkAnonymizationKey& network_anonymization_key, 217 DnsQueryType query_type, 218 HostResolverSource source, 219 std::optional<bool> secure) const; 220 221 void Set(std::unique_ptr<HostResolverInternalResult> result, 222 const NetworkAnonymizationKey& network_anonymization_key, 223 HostResolverSource source, 224 bool secure, 225 bool replace_existing, 226 int staleness_generation); 227 228 void EvictEntries(); 229 230 // If `require_persistable_anonymization_key` is true, will not serialize 231 // any entries that do not have an anonymization key that supports 232 // serialization and restoration. If false, will serialize all entries, but 233 // the result may contain anonymization keys that are malformed for 234 // restoration. 235 base::Value SerializeEntries( 236 bool serialize_staleness_generation, 237 bool require_persistable_anonymization_key) const; 238 239 EntryMap entries_; 240 size_t max_entries_; 241 242 // Number of times MakeAllEntriesStale() has been called. 243 int staleness_generation_ = 0; 244 245 raw_ref<const base::Clock> clock_; 246 raw_ref<const base::TickClock> tick_clock_; 247 }; 248 249 } // namespace net 250 251 #endif // NET_DNS_HOST_RESOLVER_CACHE_H_ 252