xref: /aosp_15_r20/external/cronet/net/dns/host_resolver_cache.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1*6777b538SAndroid Build Coastguard Worker // Copyright 2023 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker 
5*6777b538SAndroid Build Coastguard Worker #include "net/dns/host_resolver_cache.h"
6*6777b538SAndroid Build Coastguard Worker 
7*6777b538SAndroid Build Coastguard Worker #include <cstddef>
8*6777b538SAndroid Build Coastguard Worker #include <memory>
9*6777b538SAndroid Build Coastguard Worker #include <optional>
10*6777b538SAndroid Build Coastguard Worker #include <string>
11*6777b538SAndroid Build Coastguard Worker #include <string_view>
12*6777b538SAndroid Build Coastguard Worker #include <utility>
13*6777b538SAndroid Build Coastguard Worker #include <vector>
14*6777b538SAndroid Build Coastguard Worker 
15*6777b538SAndroid Build Coastguard Worker #include "base/check_op.h"
16*6777b538SAndroid Build Coastguard Worker #include "base/numerics/safe_conversions.h"
17*6777b538SAndroid Build Coastguard Worker #include "base/time/clock.h"
18*6777b538SAndroid Build Coastguard Worker #include "base/time/time.h"
19*6777b538SAndroid Build Coastguard Worker #include "net/base/network_anonymization_key.h"
20*6777b538SAndroid Build Coastguard Worker #include "net/dns/host_resolver_internal_result.h"
21*6777b538SAndroid Build Coastguard Worker #include "net/dns/public/dns_query_type.h"
22*6777b538SAndroid Build Coastguard Worker #include "net/dns/public/host_resolver_source.h"
23*6777b538SAndroid Build Coastguard Worker #include "url/third_party/mozilla/url_parse.h"
24*6777b538SAndroid Build Coastguard Worker #include "url/url_canon.h"
25*6777b538SAndroid Build Coastguard Worker #include "url/url_canon_stdstring.h"
26*6777b538SAndroid Build Coastguard Worker 
27*6777b538SAndroid Build Coastguard Worker namespace net {
28*6777b538SAndroid Build Coastguard Worker 
29*6777b538SAndroid Build Coastguard Worker namespace {
30*6777b538SAndroid Build Coastguard Worker 
31*6777b538SAndroid Build Coastguard Worker constexpr std::string_view kNakKey = "network_anonymization_key";
32*6777b538SAndroid Build Coastguard Worker constexpr std::string_view kSourceKey = "source";
33*6777b538SAndroid Build Coastguard Worker constexpr std::string_view kSecureKey = "secure";
34*6777b538SAndroid Build Coastguard Worker constexpr std::string_view kResultKey = "result";
35*6777b538SAndroid Build Coastguard Worker constexpr std::string_view kStalenessGenerationKey = "staleness_generation";
36*6777b538SAndroid Build Coastguard Worker constexpr std::string_view kMaxEntriesKey = "max_entries";
37*6777b538SAndroid Build Coastguard Worker constexpr std::string_view kEntriesKey = "entries";
38*6777b538SAndroid Build Coastguard Worker 
39*6777b538SAndroid Build Coastguard Worker }  // namespace
40*6777b538SAndroid Build Coastguard Worker 
41*6777b538SAndroid Build Coastguard Worker HostResolverCache::Key::~Key() = default;
42*6777b538SAndroid Build Coastguard Worker 
StaleLookupResult(const HostResolverInternalResult & result,std::optional<base::TimeDelta> expired_by,bool stale_by_generation)43*6777b538SAndroid Build Coastguard Worker HostResolverCache::StaleLookupResult::StaleLookupResult(
44*6777b538SAndroid Build Coastguard Worker     const HostResolverInternalResult& result,
45*6777b538SAndroid Build Coastguard Worker     std::optional<base::TimeDelta> expired_by,
46*6777b538SAndroid Build Coastguard Worker     bool stale_by_generation)
47*6777b538SAndroid Build Coastguard Worker     : result(result),
48*6777b538SAndroid Build Coastguard Worker       expired_by(expired_by),
49*6777b538SAndroid Build Coastguard Worker       stale_by_generation(stale_by_generation) {}
50*6777b538SAndroid Build Coastguard Worker 
HostResolverCache(size_t max_results,const base::Clock & clock,const base::TickClock & tick_clock)51*6777b538SAndroid Build Coastguard Worker HostResolverCache::HostResolverCache(size_t max_results,
52*6777b538SAndroid Build Coastguard Worker                                      const base::Clock& clock,
53*6777b538SAndroid Build Coastguard Worker                                      const base::TickClock& tick_clock)
54*6777b538SAndroid Build Coastguard Worker     : max_entries_(max_results), clock_(clock), tick_clock_(tick_clock) {
55*6777b538SAndroid Build Coastguard Worker   DCHECK_GT(max_entries_, 0u);
56*6777b538SAndroid Build Coastguard Worker }
57*6777b538SAndroid Build Coastguard Worker 
58*6777b538SAndroid Build Coastguard Worker HostResolverCache::~HostResolverCache() = default;
59*6777b538SAndroid Build Coastguard Worker 
60*6777b538SAndroid Build Coastguard Worker HostResolverCache::HostResolverCache(HostResolverCache&&) = default;
61*6777b538SAndroid Build Coastguard Worker 
62*6777b538SAndroid Build Coastguard Worker HostResolverCache& HostResolverCache::operator=(HostResolverCache&&) = default;
63*6777b538SAndroid Build Coastguard Worker 
Lookup(std::string_view domain_name,const NetworkAnonymizationKey & network_anonymization_key,DnsQueryType query_type,HostResolverSource source,std::optional<bool> secure) const64*6777b538SAndroid Build Coastguard Worker const HostResolverInternalResult* HostResolverCache::Lookup(
65*6777b538SAndroid Build Coastguard Worker     std::string_view domain_name,
66*6777b538SAndroid Build Coastguard Worker     const NetworkAnonymizationKey& network_anonymization_key,
67*6777b538SAndroid Build Coastguard Worker     DnsQueryType query_type,
68*6777b538SAndroid Build Coastguard Worker     HostResolverSource source,
69*6777b538SAndroid Build Coastguard Worker     std::optional<bool> secure) const {
70*6777b538SAndroid Build Coastguard Worker   std::vector<EntryMap::const_iterator> candidates = LookupInternal(
71*6777b538SAndroid Build Coastguard Worker       domain_name, network_anonymization_key, query_type, source, secure);
72*6777b538SAndroid Build Coastguard Worker 
73*6777b538SAndroid Build Coastguard Worker   // Get the most secure, last-matching (which is first in the vector returned
74*6777b538SAndroid Build Coastguard Worker   // by LookupInternal()) non-expired result.
75*6777b538SAndroid Build Coastguard Worker   base::TimeTicks now_ticks = tick_clock_->NowTicks();
76*6777b538SAndroid Build Coastguard Worker   base::Time now = clock_->Now();
77*6777b538SAndroid Build Coastguard Worker   HostResolverInternalResult* most_secure_result = nullptr;
78*6777b538SAndroid Build Coastguard Worker   for (const EntryMap::const_iterator& candidate : candidates) {
79*6777b538SAndroid Build Coastguard Worker     DCHECK(candidate->second.result->timed_expiration().has_value());
80*6777b538SAndroid Build Coastguard Worker 
81*6777b538SAndroid Build Coastguard Worker     if (candidate->second.IsStale(now, now_ticks, staleness_generation_)) {
82*6777b538SAndroid Build Coastguard Worker       continue;
83*6777b538SAndroid Build Coastguard Worker     }
84*6777b538SAndroid Build Coastguard Worker 
85*6777b538SAndroid Build Coastguard Worker     // If the candidate is secure, or all results are insecure, no need to check
86*6777b538SAndroid Build Coastguard Worker     // any more.
87*6777b538SAndroid Build Coastguard Worker     if (candidate->second.secure || !secure.value_or(true)) {
88*6777b538SAndroid Build Coastguard Worker       return candidate->second.result.get();
89*6777b538SAndroid Build Coastguard Worker     } else if (most_secure_result == nullptr) {
90*6777b538SAndroid Build Coastguard Worker       most_secure_result = candidate->second.result.get();
91*6777b538SAndroid Build Coastguard Worker     }
92*6777b538SAndroid Build Coastguard Worker   }
93*6777b538SAndroid Build Coastguard Worker 
94*6777b538SAndroid Build Coastguard Worker   return most_secure_result;
95*6777b538SAndroid Build Coastguard Worker }
96*6777b538SAndroid Build Coastguard Worker 
97*6777b538SAndroid Build Coastguard Worker std::optional<HostResolverCache::StaleLookupResult>
LookupStale(std::string_view domain_name,const NetworkAnonymizationKey & network_anonymization_key,DnsQueryType query_type,HostResolverSource source,std::optional<bool> secure) const98*6777b538SAndroid Build Coastguard Worker HostResolverCache::LookupStale(
99*6777b538SAndroid Build Coastguard Worker     std::string_view domain_name,
100*6777b538SAndroid Build Coastguard Worker     const NetworkAnonymizationKey& network_anonymization_key,
101*6777b538SAndroid Build Coastguard Worker     DnsQueryType query_type,
102*6777b538SAndroid Build Coastguard Worker     HostResolverSource source,
103*6777b538SAndroid Build Coastguard Worker     std::optional<bool> secure) const {
104*6777b538SAndroid Build Coastguard Worker   std::vector<EntryMap::const_iterator> candidates = LookupInternal(
105*6777b538SAndroid Build Coastguard Worker       domain_name, network_anonymization_key, query_type, source, secure);
106*6777b538SAndroid Build Coastguard Worker 
107*6777b538SAndroid Build Coastguard Worker   // Get the least expired, most secure result.
108*6777b538SAndroid Build Coastguard Worker   base::TimeTicks now_ticks = tick_clock_->NowTicks();
109*6777b538SAndroid Build Coastguard Worker   base::Time now = clock_->Now();
110*6777b538SAndroid Build Coastguard Worker   const Entry* best_match = nullptr;
111*6777b538SAndroid Build Coastguard Worker   base::TimeDelta best_match_time_until_expiration;
112*6777b538SAndroid Build Coastguard Worker   for (const EntryMap::const_iterator& candidate : candidates) {
113*6777b538SAndroid Build Coastguard Worker     DCHECK(candidate->second.result->timed_expiration().has_value());
114*6777b538SAndroid Build Coastguard Worker 
115*6777b538SAndroid Build Coastguard Worker     base::TimeDelta candidate_time_until_expiration =
116*6777b538SAndroid Build Coastguard Worker         candidate->second.TimeUntilExpiration(now, now_ticks);
117*6777b538SAndroid Build Coastguard Worker 
118*6777b538SAndroid Build Coastguard Worker     if (!candidate->second.IsStale(now, now_ticks, staleness_generation_) &&
119*6777b538SAndroid Build Coastguard Worker         (candidate->second.secure || !secure.value_or(true))) {
120*6777b538SAndroid Build Coastguard Worker       // If a non-stale candidate is secure, or all results are insecure, no
121*6777b538SAndroid Build Coastguard Worker       // need to check any more.
122*6777b538SAndroid Build Coastguard Worker       best_match = &candidate->second;
123*6777b538SAndroid Build Coastguard Worker       best_match_time_until_expiration = candidate_time_until_expiration;
124*6777b538SAndroid Build Coastguard Worker       break;
125*6777b538SAndroid Build Coastguard Worker     } else if (best_match == nullptr ||
126*6777b538SAndroid Build Coastguard Worker                (!candidate->second.IsStale(now, now_ticks,
127*6777b538SAndroid Build Coastguard Worker                                            staleness_generation_) &&
128*6777b538SAndroid Build Coastguard Worker                 best_match->IsStale(now, now_ticks, staleness_generation_)) ||
129*6777b538SAndroid Build Coastguard Worker                candidate->second.staleness_generation >
130*6777b538SAndroid Build Coastguard Worker                    best_match->staleness_generation ||
131*6777b538SAndroid Build Coastguard Worker                (candidate->second.staleness_generation ==
132*6777b538SAndroid Build Coastguard Worker                     best_match->staleness_generation &&
133*6777b538SAndroid Build Coastguard Worker                 candidate_time_until_expiration >
134*6777b538SAndroid Build Coastguard Worker                     best_match_time_until_expiration) ||
135*6777b538SAndroid Build Coastguard Worker                (candidate->second.staleness_generation ==
136*6777b538SAndroid Build Coastguard Worker                     best_match->staleness_generation &&
137*6777b538SAndroid Build Coastguard Worker                 candidate_time_until_expiration ==
138*6777b538SAndroid Build Coastguard Worker                     best_match_time_until_expiration &&
139*6777b538SAndroid Build Coastguard Worker                 candidate->second.secure && !best_match->secure)) {
140*6777b538SAndroid Build Coastguard Worker       best_match = &candidate->second;
141*6777b538SAndroid Build Coastguard Worker       best_match_time_until_expiration = candidate_time_until_expiration;
142*6777b538SAndroid Build Coastguard Worker     }
143*6777b538SAndroid Build Coastguard Worker   }
144*6777b538SAndroid Build Coastguard Worker 
145*6777b538SAndroid Build Coastguard Worker   if (best_match == nullptr) {
146*6777b538SAndroid Build Coastguard Worker     return std::nullopt;
147*6777b538SAndroid Build Coastguard Worker   } else {
148*6777b538SAndroid Build Coastguard Worker     std::optional<base::TimeDelta> expired_by;
149*6777b538SAndroid Build Coastguard Worker     if (best_match_time_until_expiration.is_negative()) {
150*6777b538SAndroid Build Coastguard Worker       expired_by = best_match_time_until_expiration.magnitude();
151*6777b538SAndroid Build Coastguard Worker     }
152*6777b538SAndroid Build Coastguard Worker     return StaleLookupResult(
153*6777b538SAndroid Build Coastguard Worker         *best_match->result, expired_by,
154*6777b538SAndroid Build Coastguard Worker         best_match->staleness_generation != staleness_generation_);
155*6777b538SAndroid Build Coastguard Worker   }
156*6777b538SAndroid Build Coastguard Worker }
157*6777b538SAndroid Build Coastguard Worker 
Set(std::unique_ptr<HostResolverInternalResult> result,const NetworkAnonymizationKey & network_anonymization_key,HostResolverSource source,bool secure)158*6777b538SAndroid Build Coastguard Worker void HostResolverCache::Set(
159*6777b538SAndroid Build Coastguard Worker     std::unique_ptr<HostResolverInternalResult> result,
160*6777b538SAndroid Build Coastguard Worker     const NetworkAnonymizationKey& network_anonymization_key,
161*6777b538SAndroid Build Coastguard Worker     HostResolverSource source,
162*6777b538SAndroid Build Coastguard Worker     bool secure) {
163*6777b538SAndroid Build Coastguard Worker   Set(std::move(result), network_anonymization_key, source, secure,
164*6777b538SAndroid Build Coastguard Worker       /*replace_existing=*/true, staleness_generation_);
165*6777b538SAndroid Build Coastguard Worker }
166*6777b538SAndroid Build Coastguard Worker 
MakeAllResultsStale()167*6777b538SAndroid Build Coastguard Worker void HostResolverCache::MakeAllResultsStale() {
168*6777b538SAndroid Build Coastguard Worker   ++staleness_generation_;
169*6777b538SAndroid Build Coastguard Worker }
170*6777b538SAndroid Build Coastguard Worker 
Serialize() const171*6777b538SAndroid Build Coastguard Worker base::Value HostResolverCache::Serialize() const {
172*6777b538SAndroid Build Coastguard Worker   // Do not serialize any entries without a persistable anonymization key
173*6777b538SAndroid Build Coastguard Worker   // because it is required to store and restore entries with the correct
174*6777b538SAndroid Build Coastguard Worker   // annonymization key. A non-persistable anonymization key is typically used
175*6777b538SAndroid Build Coastguard Worker   // for short-lived contexts, and associated entries are not expected to be
176*6777b538SAndroid Build Coastguard Worker   // useful after persistence to disk anyway.
177*6777b538SAndroid Build Coastguard Worker   return SerializeEntries(/*serialize_staleness_generation=*/false,
178*6777b538SAndroid Build Coastguard Worker                           /*require_persistable_anonymization_key=*/true);
179*6777b538SAndroid Build Coastguard Worker }
180*6777b538SAndroid Build Coastguard Worker 
RestoreFromValue(const base::Value & value)181*6777b538SAndroid Build Coastguard Worker bool HostResolverCache::RestoreFromValue(const base::Value& value) {
182*6777b538SAndroid Build Coastguard Worker   const base::Value::List* list = value.GetIfList();
183*6777b538SAndroid Build Coastguard Worker   if (!list) {
184*6777b538SAndroid Build Coastguard Worker     return false;
185*6777b538SAndroid Build Coastguard Worker   }
186*6777b538SAndroid Build Coastguard Worker 
187*6777b538SAndroid Build Coastguard Worker   for (const base::Value& list_value : *list) {
188*6777b538SAndroid Build Coastguard Worker     // Simply stop on reaching max size rather than attempting to figure out if
189*6777b538SAndroid Build Coastguard Worker     // any current entries should be evicted over the deserialized entries.
190*6777b538SAndroid Build Coastguard Worker     if (entries_.size() == max_entries_) {
191*6777b538SAndroid Build Coastguard Worker       return true;
192*6777b538SAndroid Build Coastguard Worker     }
193*6777b538SAndroid Build Coastguard Worker 
194*6777b538SAndroid Build Coastguard Worker     const base::Value::Dict* dict = list_value.GetIfDict();
195*6777b538SAndroid Build Coastguard Worker     if (!dict) {
196*6777b538SAndroid Build Coastguard Worker       return false;
197*6777b538SAndroid Build Coastguard Worker     }
198*6777b538SAndroid Build Coastguard Worker 
199*6777b538SAndroid Build Coastguard Worker     const base::Value* anonymization_key_value = dict->Find(kNakKey);
200*6777b538SAndroid Build Coastguard Worker     NetworkAnonymizationKey anonymization_key;
201*6777b538SAndroid Build Coastguard Worker     if (!anonymization_key_value ||
202*6777b538SAndroid Build Coastguard Worker         !NetworkAnonymizationKey::FromValue(*anonymization_key_value,
203*6777b538SAndroid Build Coastguard Worker                                             &anonymization_key)) {
204*6777b538SAndroid Build Coastguard Worker       return false;
205*6777b538SAndroid Build Coastguard Worker     }
206*6777b538SAndroid Build Coastguard Worker 
207*6777b538SAndroid Build Coastguard Worker     const base::Value* source_value = dict->Find(kSourceKey);
208*6777b538SAndroid Build Coastguard Worker     std::optional<HostResolverSource> source =
209*6777b538SAndroid Build Coastguard Worker         source_value == nullptr ? std::nullopt
210*6777b538SAndroid Build Coastguard Worker                                 : HostResolverSourceFromValue(*source_value);
211*6777b538SAndroid Build Coastguard Worker     if (!source.has_value()) {
212*6777b538SAndroid Build Coastguard Worker       return false;
213*6777b538SAndroid Build Coastguard Worker     }
214*6777b538SAndroid Build Coastguard Worker 
215*6777b538SAndroid Build Coastguard Worker     std::optional<bool> secure = dict->FindBool(kSecureKey);
216*6777b538SAndroid Build Coastguard Worker     if (!secure.has_value()) {
217*6777b538SAndroid Build Coastguard Worker       return false;
218*6777b538SAndroid Build Coastguard Worker     }
219*6777b538SAndroid Build Coastguard Worker 
220*6777b538SAndroid Build Coastguard Worker     const base::Value* result_value = dict->Find(kResultKey);
221*6777b538SAndroid Build Coastguard Worker     std::unique_ptr<HostResolverInternalResult> result =
222*6777b538SAndroid Build Coastguard Worker         result_value == nullptr
223*6777b538SAndroid Build Coastguard Worker             ? nullptr
224*6777b538SAndroid Build Coastguard Worker             : HostResolverInternalResult::FromValue(*result_value);
225*6777b538SAndroid Build Coastguard Worker     if (!result || !result->timed_expiration().has_value()) {
226*6777b538SAndroid Build Coastguard Worker       return false;
227*6777b538SAndroid Build Coastguard Worker     }
228*6777b538SAndroid Build Coastguard Worker 
229*6777b538SAndroid Build Coastguard Worker     // `staleness_generation_ - 1` to make entry stale-by-generation.
230*6777b538SAndroid Build Coastguard Worker     Set(std::move(result), anonymization_key, source.value(), secure.value(),
231*6777b538SAndroid Build Coastguard Worker         /*replace_existing=*/false, staleness_generation_ - 1);
232*6777b538SAndroid Build Coastguard Worker   }
233*6777b538SAndroid Build Coastguard Worker 
234*6777b538SAndroid Build Coastguard Worker   CHECK_LE(entries_.size(), max_entries_);
235*6777b538SAndroid Build Coastguard Worker   return true;
236*6777b538SAndroid Build Coastguard Worker }
237*6777b538SAndroid Build Coastguard Worker 
SerializeForLogging() const238*6777b538SAndroid Build Coastguard Worker base::Value HostResolverCache::SerializeForLogging() const {
239*6777b538SAndroid Build Coastguard Worker   base::Value::Dict dict;
240*6777b538SAndroid Build Coastguard Worker 
241*6777b538SAndroid Build Coastguard Worker   dict.Set(kMaxEntriesKey, base::checked_cast<int>(max_entries_));
242*6777b538SAndroid Build Coastguard Worker   dict.Set(kStalenessGenerationKey, staleness_generation_);
243*6777b538SAndroid Build Coastguard Worker 
244*6777b538SAndroid Build Coastguard Worker   // Include entries with non-persistable anonymization keys, so the log can
245*6777b538SAndroid Build Coastguard Worker   // contain all entries. Restoring from this serialization is not supported.
246*6777b538SAndroid Build Coastguard Worker   dict.Set(kEntriesKey,
247*6777b538SAndroid Build Coastguard Worker            SerializeEntries(/*serialize_staleness_generation=*/true,
248*6777b538SAndroid Build Coastguard Worker                             /*require_persistable_anonymization_key=*/false));
249*6777b538SAndroid Build Coastguard Worker 
250*6777b538SAndroid Build Coastguard Worker   return base::Value(std::move(dict));
251*6777b538SAndroid Build Coastguard Worker }
252*6777b538SAndroid Build Coastguard Worker 
Entry(std::unique_ptr<HostResolverInternalResult> result,HostResolverSource source,bool secure,int staleness_generation)253*6777b538SAndroid Build Coastguard Worker HostResolverCache::Entry::Entry(
254*6777b538SAndroid Build Coastguard Worker     std::unique_ptr<HostResolverInternalResult> result,
255*6777b538SAndroid Build Coastguard Worker     HostResolverSource source,
256*6777b538SAndroid Build Coastguard Worker     bool secure,
257*6777b538SAndroid Build Coastguard Worker     int staleness_generation)
258*6777b538SAndroid Build Coastguard Worker     : result(std::move(result)),
259*6777b538SAndroid Build Coastguard Worker       source(source),
260*6777b538SAndroid Build Coastguard Worker       secure(secure),
261*6777b538SAndroid Build Coastguard Worker       staleness_generation(staleness_generation) {}
262*6777b538SAndroid Build Coastguard Worker 
263*6777b538SAndroid Build Coastguard Worker HostResolverCache::Entry::~Entry() = default;
264*6777b538SAndroid Build Coastguard Worker 
265*6777b538SAndroid Build Coastguard Worker HostResolverCache::Entry::Entry(Entry&&) = default;
266*6777b538SAndroid Build Coastguard Worker 
267*6777b538SAndroid Build Coastguard Worker HostResolverCache::Entry& HostResolverCache::Entry::operator=(Entry&&) =
268*6777b538SAndroid Build Coastguard Worker     default;
269*6777b538SAndroid Build Coastguard Worker 
IsStale(base::Time now,base::TimeTicks now_ticks,int current_staleness_generation) const270*6777b538SAndroid Build Coastguard Worker bool HostResolverCache::Entry::IsStale(base::Time now,
271*6777b538SAndroid Build Coastguard Worker                                        base::TimeTicks now_ticks,
272*6777b538SAndroid Build Coastguard Worker                                        int current_staleness_generation) const {
273*6777b538SAndroid Build Coastguard Worker   return staleness_generation != current_staleness_generation ||
274*6777b538SAndroid Build Coastguard Worker          TimeUntilExpiration(now, now_ticks).is_negative();
275*6777b538SAndroid Build Coastguard Worker }
276*6777b538SAndroid Build Coastguard Worker 
TimeUntilExpiration(base::Time now,base::TimeTicks now_ticks) const277*6777b538SAndroid Build Coastguard Worker base::TimeDelta HostResolverCache::Entry::TimeUntilExpiration(
278*6777b538SAndroid Build Coastguard Worker     base::Time now,
279*6777b538SAndroid Build Coastguard Worker     base::TimeTicks now_ticks) const {
280*6777b538SAndroid Build Coastguard Worker   if (result->expiration().has_value()) {
281*6777b538SAndroid Build Coastguard Worker     return result->expiration().value() - now_ticks;
282*6777b538SAndroid Build Coastguard Worker   } else {
283*6777b538SAndroid Build Coastguard Worker     DCHECK(result->timed_expiration().has_value());
284*6777b538SAndroid Build Coastguard Worker     return result->timed_expiration().value() - now;
285*6777b538SAndroid Build Coastguard Worker   }
286*6777b538SAndroid Build Coastguard Worker }
287*6777b538SAndroid Build Coastguard Worker 
288*6777b538SAndroid Build Coastguard Worker std::vector<HostResolverCache::EntryMap::const_iterator>
LookupInternal(std::string_view domain_name,const NetworkAnonymizationKey & network_anonymization_key,DnsQueryType query_type,HostResolverSource source,std::optional<bool> secure) const289*6777b538SAndroid Build Coastguard Worker HostResolverCache::LookupInternal(
290*6777b538SAndroid Build Coastguard Worker     std::string_view domain_name,
291*6777b538SAndroid Build Coastguard Worker     const NetworkAnonymizationKey& network_anonymization_key,
292*6777b538SAndroid Build Coastguard Worker     DnsQueryType query_type,
293*6777b538SAndroid Build Coastguard Worker     HostResolverSource source,
294*6777b538SAndroid Build Coastguard Worker     std::optional<bool> secure) const {
295*6777b538SAndroid Build Coastguard Worker   auto matches = std::vector<EntryMap::const_iterator>();
296*6777b538SAndroid Build Coastguard Worker 
297*6777b538SAndroid Build Coastguard Worker   if (entries_.empty()) {
298*6777b538SAndroid Build Coastguard Worker     return matches;
299*6777b538SAndroid Build Coastguard Worker   }
300*6777b538SAndroid Build Coastguard Worker 
301*6777b538SAndroid Build Coastguard Worker   std::string canonicalized;
302*6777b538SAndroid Build Coastguard Worker   url::StdStringCanonOutput output(&canonicalized);
303*6777b538SAndroid Build Coastguard Worker   url::CanonHostInfo host_info;
304*6777b538SAndroid Build Coastguard Worker 
305*6777b538SAndroid Build Coastguard Worker   url::CanonicalizeHostVerbose(domain_name.data(),
306*6777b538SAndroid Build Coastguard Worker                                url::Component(0, domain_name.size()), &output,
307*6777b538SAndroid Build Coastguard Worker                                &host_info);
308*6777b538SAndroid Build Coastguard Worker 
309*6777b538SAndroid Build Coastguard Worker   // For performance, when canonicalization can't canonicalize, minimize string
310*6777b538SAndroid Build Coastguard Worker   // copies and just reuse the input StringPiece. This optimization prevents
311*6777b538SAndroid Build Coastguard Worker   // easily reusing a MaybeCanoncalize util with similar code.
312*6777b538SAndroid Build Coastguard Worker   std::string_view lookup_name = domain_name;
313*6777b538SAndroid Build Coastguard Worker   if (host_info.family == url::CanonHostInfo::Family::NEUTRAL) {
314*6777b538SAndroid Build Coastguard Worker     output.Complete();
315*6777b538SAndroid Build Coastguard Worker     lookup_name = canonicalized;
316*6777b538SAndroid Build Coastguard Worker   }
317*6777b538SAndroid Build Coastguard Worker 
318*6777b538SAndroid Build Coastguard Worker   auto range = entries_.equal_range(
319*6777b538SAndroid Build Coastguard Worker       KeyRef{lookup_name, raw_ref(network_anonymization_key)});
320*6777b538SAndroid Build Coastguard Worker   if (range.first == entries_.cend() || range.second == entries_.cbegin() ||
321*6777b538SAndroid Build Coastguard Worker       range.first == range.second) {
322*6777b538SAndroid Build Coastguard Worker     return matches;
323*6777b538SAndroid Build Coastguard Worker   }
324*6777b538SAndroid Build Coastguard Worker 
325*6777b538SAndroid Build Coastguard Worker   // Iterate in reverse order to return most-recently-added entry first.
326*6777b538SAndroid Build Coastguard Worker   auto it = --range.second;
327*6777b538SAndroid Build Coastguard Worker   while (true) {
328*6777b538SAndroid Build Coastguard Worker     if ((query_type == DnsQueryType::UNSPECIFIED ||
329*6777b538SAndroid Build Coastguard Worker          it->second.result->query_type() == DnsQueryType::UNSPECIFIED ||
330*6777b538SAndroid Build Coastguard Worker          query_type == it->second.result->query_type()) &&
331*6777b538SAndroid Build Coastguard Worker         (source == HostResolverSource::ANY || source == it->second.source) &&
332*6777b538SAndroid Build Coastguard Worker         (!secure.has_value() || secure.value() == it->second.secure)) {
333*6777b538SAndroid Build Coastguard Worker       matches.push_back(it);
334*6777b538SAndroid Build Coastguard Worker     }
335*6777b538SAndroid Build Coastguard Worker 
336*6777b538SAndroid Build Coastguard Worker     if (it == range.first) {
337*6777b538SAndroid Build Coastguard Worker       break;
338*6777b538SAndroid Build Coastguard Worker     }
339*6777b538SAndroid Build Coastguard Worker     --it;
340*6777b538SAndroid Build Coastguard Worker   }
341*6777b538SAndroid Build Coastguard Worker 
342*6777b538SAndroid Build Coastguard Worker   return matches;
343*6777b538SAndroid Build Coastguard Worker }
344*6777b538SAndroid Build Coastguard Worker 
Set(std::unique_ptr<HostResolverInternalResult> result,const NetworkAnonymizationKey & network_anonymization_key,HostResolverSource source,bool secure,bool replace_existing,int staleness_generation)345*6777b538SAndroid Build Coastguard Worker void HostResolverCache::Set(
346*6777b538SAndroid Build Coastguard Worker     std::unique_ptr<HostResolverInternalResult> result,
347*6777b538SAndroid Build Coastguard Worker     const NetworkAnonymizationKey& network_anonymization_key,
348*6777b538SAndroid Build Coastguard Worker     HostResolverSource source,
349*6777b538SAndroid Build Coastguard Worker     bool secure,
350*6777b538SAndroid Build Coastguard Worker     bool replace_existing,
351*6777b538SAndroid Build Coastguard Worker     int staleness_generation) {
352*6777b538SAndroid Build Coastguard Worker   DCHECK(result);
353*6777b538SAndroid Build Coastguard Worker   // Result must have at least a timed expiration to be a cacheable result.
354*6777b538SAndroid Build Coastguard Worker   DCHECK(result->timed_expiration().has_value());
355*6777b538SAndroid Build Coastguard Worker 
356*6777b538SAndroid Build Coastguard Worker   std::vector<EntryMap::const_iterator> matches =
357*6777b538SAndroid Build Coastguard Worker       LookupInternal(result->domain_name(), network_anonymization_key,
358*6777b538SAndroid Build Coastguard Worker                      result->query_type(), source, secure);
359*6777b538SAndroid Build Coastguard Worker 
360*6777b538SAndroid Build Coastguard Worker   if (!matches.empty() && !replace_existing) {
361*6777b538SAndroid Build Coastguard Worker     // Matches already present that are not to be replaced.
362*6777b538SAndroid Build Coastguard Worker     return;
363*6777b538SAndroid Build Coastguard Worker   }
364*6777b538SAndroid Build Coastguard Worker 
365*6777b538SAndroid Build Coastguard Worker   for (const EntryMap::const_iterator& match : matches) {
366*6777b538SAndroid Build Coastguard Worker     entries_.erase(match);
367*6777b538SAndroid Build Coastguard Worker   }
368*6777b538SAndroid Build Coastguard Worker 
369*6777b538SAndroid Build Coastguard Worker   std::string domain_name = result->domain_name();
370*6777b538SAndroid Build Coastguard Worker   entries_.emplace(
371*6777b538SAndroid Build Coastguard Worker       Key(std::move(domain_name), network_anonymization_key),
372*6777b538SAndroid Build Coastguard Worker       Entry(std::move(result), source, secure, staleness_generation));
373*6777b538SAndroid Build Coastguard Worker 
374*6777b538SAndroid Build Coastguard Worker   if (entries_.size() > max_entries_) {
375*6777b538SAndroid Build Coastguard Worker     EvictEntries();
376*6777b538SAndroid Build Coastguard Worker   }
377*6777b538SAndroid Build Coastguard Worker }
378*6777b538SAndroid Build Coastguard Worker 
379*6777b538SAndroid Build Coastguard Worker // Remove all stale entries, or if none stale, the soonest-to-expire,
380*6777b538SAndroid Build Coastguard Worker // least-secure entry.
EvictEntries()381*6777b538SAndroid Build Coastguard Worker void HostResolverCache::EvictEntries() {
382*6777b538SAndroid Build Coastguard Worker   base::TimeTicks now_ticks = tick_clock_->NowTicks();
383*6777b538SAndroid Build Coastguard Worker   base::Time now = clock_->Now();
384*6777b538SAndroid Build Coastguard Worker 
385*6777b538SAndroid Build Coastguard Worker   bool stale_found = false;
386*6777b538SAndroid Build Coastguard Worker   base::TimeDelta soonest_time_till_expriation = base::TimeDelta::Max();
387*6777b538SAndroid Build Coastguard Worker   std::optional<EntryMap::const_iterator> best_for_removal;
388*6777b538SAndroid Build Coastguard Worker 
389*6777b538SAndroid Build Coastguard Worker   auto it = entries_.cbegin();
390*6777b538SAndroid Build Coastguard Worker   while (it != entries_.cend()) {
391*6777b538SAndroid Build Coastguard Worker     if (it->second.IsStale(now, now_ticks, staleness_generation_)) {
392*6777b538SAndroid Build Coastguard Worker       stale_found = true;
393*6777b538SAndroid Build Coastguard Worker       it = entries_.erase(it);
394*6777b538SAndroid Build Coastguard Worker     } else {
395*6777b538SAndroid Build Coastguard Worker       base::TimeDelta time_till_expiration =
396*6777b538SAndroid Build Coastguard Worker           it->second.TimeUntilExpiration(now, now_ticks);
397*6777b538SAndroid Build Coastguard Worker 
398*6777b538SAndroid Build Coastguard Worker       if (!best_for_removal.has_value() ||
399*6777b538SAndroid Build Coastguard Worker           time_till_expiration < soonest_time_till_expriation ||
400*6777b538SAndroid Build Coastguard Worker           (time_till_expiration == soonest_time_till_expriation &&
401*6777b538SAndroid Build Coastguard Worker            best_for_removal.value()->second.secure && !it->second.secure)) {
402*6777b538SAndroid Build Coastguard Worker         soonest_time_till_expriation = time_till_expiration;
403*6777b538SAndroid Build Coastguard Worker         best_for_removal = it;
404*6777b538SAndroid Build Coastguard Worker       }
405*6777b538SAndroid Build Coastguard Worker 
406*6777b538SAndroid Build Coastguard Worker       ++it;
407*6777b538SAndroid Build Coastguard Worker     }
408*6777b538SAndroid Build Coastguard Worker   }
409*6777b538SAndroid Build Coastguard Worker 
410*6777b538SAndroid Build Coastguard Worker   if (!stale_found) {
411*6777b538SAndroid Build Coastguard Worker     CHECK(best_for_removal.has_value());
412*6777b538SAndroid Build Coastguard Worker     entries_.erase(best_for_removal.value());
413*6777b538SAndroid Build Coastguard Worker   }
414*6777b538SAndroid Build Coastguard Worker 
415*6777b538SAndroid Build Coastguard Worker   CHECK_LE(entries_.size(), max_entries_);
416*6777b538SAndroid Build Coastguard Worker }
417*6777b538SAndroid Build Coastguard Worker 
SerializeEntries(bool serialize_staleness_generation,bool require_persistable_anonymization_key) const418*6777b538SAndroid Build Coastguard Worker base::Value HostResolverCache::SerializeEntries(
419*6777b538SAndroid Build Coastguard Worker     bool serialize_staleness_generation,
420*6777b538SAndroid Build Coastguard Worker     bool require_persistable_anonymization_key) const {
421*6777b538SAndroid Build Coastguard Worker   base::Value::List list;
422*6777b538SAndroid Build Coastguard Worker 
423*6777b538SAndroid Build Coastguard Worker   for (const auto& [key, entry] : entries_) {
424*6777b538SAndroid Build Coastguard Worker     base::Value::Dict dict;
425*6777b538SAndroid Build Coastguard Worker 
426*6777b538SAndroid Build Coastguard Worker     if (serialize_staleness_generation) {
427*6777b538SAndroid Build Coastguard Worker       dict.Set(kStalenessGenerationKey, entry.staleness_generation);
428*6777b538SAndroid Build Coastguard Worker     }
429*6777b538SAndroid Build Coastguard Worker 
430*6777b538SAndroid Build Coastguard Worker     base::Value anonymization_key_value;
431*6777b538SAndroid Build Coastguard Worker     if (!key.network_anonymization_key.ToValue(&anonymization_key_value)) {
432*6777b538SAndroid Build Coastguard Worker       if (require_persistable_anonymization_key) {
433*6777b538SAndroid Build Coastguard Worker         continue;
434*6777b538SAndroid Build Coastguard Worker       } else {
435*6777b538SAndroid Build Coastguard Worker         // If the caller doesn't care about anonymization keys that can be
436*6777b538SAndroid Build Coastguard Worker         // serialized and restored, construct a serialization just for the sake
437*6777b538SAndroid Build Coastguard Worker         // of logging information.
438*6777b538SAndroid Build Coastguard Worker         anonymization_key_value =
439*6777b538SAndroid Build Coastguard Worker             base::Value("Non-persistable network anonymization key: " +
440*6777b538SAndroid Build Coastguard Worker                         key.network_anonymization_key.ToDebugString());
441*6777b538SAndroid Build Coastguard Worker       }
442*6777b538SAndroid Build Coastguard Worker     }
443*6777b538SAndroid Build Coastguard Worker 
444*6777b538SAndroid Build Coastguard Worker     dict.Set(kNakKey, std::move(anonymization_key_value));
445*6777b538SAndroid Build Coastguard Worker     dict.Set(kSourceKey, ToValue(entry.source));
446*6777b538SAndroid Build Coastguard Worker     dict.Set(kSecureKey, entry.secure);
447*6777b538SAndroid Build Coastguard Worker     dict.Set(kResultKey, entry.result->ToValue());
448*6777b538SAndroid Build Coastguard Worker 
449*6777b538SAndroid Build Coastguard Worker     list.Append(std::move(dict));
450*6777b538SAndroid Build Coastguard Worker   }
451*6777b538SAndroid Build Coastguard Worker 
452*6777b538SAndroid Build Coastguard Worker   return base::Value(std::move(list));
453*6777b538SAndroid Build Coastguard Worker }
454*6777b538SAndroid Build Coastguard Worker 
455*6777b538SAndroid Build Coastguard Worker }  // namespace net
456