xref: /aosp_15_r20/external/cronet/net/dns/mdns_cache.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1*6777b538SAndroid Build Coastguard Worker // Copyright 2013 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/mdns_cache.h"
6*6777b538SAndroid Build Coastguard Worker 
7*6777b538SAndroid Build Coastguard Worker #include <algorithm>
8*6777b538SAndroid Build Coastguard Worker #include <tuple>
9*6777b538SAndroid Build Coastguard Worker #include <utility>
10*6777b538SAndroid Build Coastguard Worker 
11*6777b538SAndroid Build Coastguard Worker #include "base/containers/contains.h"
12*6777b538SAndroid Build Coastguard Worker #include "base/strings/string_number_conversions.h"
13*6777b538SAndroid Build Coastguard Worker #include "base/strings/string_util.h"
14*6777b538SAndroid Build Coastguard Worker #include "net/dns/public/dns_protocol.h"
15*6777b538SAndroid Build Coastguard Worker #include "net/dns/record_parsed.h"
16*6777b538SAndroid Build Coastguard Worker #include "net/dns/record_rdata.h"
17*6777b538SAndroid Build Coastguard Worker 
18*6777b538SAndroid Build Coastguard Worker // TODO(noamsml): Recursive CNAME closure (backwards and forwards).
19*6777b538SAndroid Build Coastguard Worker 
20*6777b538SAndroid Build Coastguard Worker namespace net {
21*6777b538SAndroid Build Coastguard Worker 
22*6777b538SAndroid Build Coastguard Worker namespace {
23*6777b538SAndroid Build Coastguard Worker constexpr size_t kDefaultEntryLimit = 100'000;
24*6777b538SAndroid Build Coastguard Worker }  // namespace
25*6777b538SAndroid Build Coastguard Worker 
26*6777b538SAndroid Build Coastguard Worker // The effective TTL given to records with a nominal zero TTL.
27*6777b538SAndroid Build Coastguard Worker // Allows time for hosts to send updated records, as detailed in RFC 6762
28*6777b538SAndroid Build Coastguard Worker // Section 10.1.
29*6777b538SAndroid Build Coastguard Worker static const unsigned kZeroTTLSeconds = 1;
30*6777b538SAndroid Build Coastguard Worker 
Key(unsigned type,const std::string & name,const std::string & optional)31*6777b538SAndroid Build Coastguard Worker MDnsCache::Key::Key(unsigned type,
32*6777b538SAndroid Build Coastguard Worker                     const std::string& name,
33*6777b538SAndroid Build Coastguard Worker                     const std::string& optional)
34*6777b538SAndroid Build Coastguard Worker     : type_(type),
35*6777b538SAndroid Build Coastguard Worker       name_lowercase_(base::ToLowerASCII(name)),
36*6777b538SAndroid Build Coastguard Worker       optional_(optional) {}
37*6777b538SAndroid Build Coastguard Worker 
38*6777b538SAndroid Build Coastguard Worker MDnsCache::Key::Key(const MDnsCache::Key& other) = default;
39*6777b538SAndroid Build Coastguard Worker 
40*6777b538SAndroid Build Coastguard Worker MDnsCache::Key& MDnsCache::Key::operator=(const MDnsCache::Key& other) =
41*6777b538SAndroid Build Coastguard Worker     default;
42*6777b538SAndroid Build Coastguard Worker 
43*6777b538SAndroid Build Coastguard Worker MDnsCache::Key::~Key() = default;
44*6777b538SAndroid Build Coastguard Worker 
operator <(const MDnsCache::Key & other) const45*6777b538SAndroid Build Coastguard Worker bool MDnsCache::Key::operator<(const MDnsCache::Key& other) const {
46*6777b538SAndroid Build Coastguard Worker   return std::tie(name_lowercase_, type_, optional_) <
47*6777b538SAndroid Build Coastguard Worker          std::tie(other.name_lowercase_, other.type_, other.optional_);
48*6777b538SAndroid Build Coastguard Worker }
49*6777b538SAndroid Build Coastguard Worker 
operator ==(const MDnsCache::Key & key) const50*6777b538SAndroid Build Coastguard Worker bool MDnsCache::Key::operator==(const MDnsCache::Key& key) const {
51*6777b538SAndroid Build Coastguard Worker   return type_ == key.type_ && name_lowercase_ == key.name_lowercase_ &&
52*6777b538SAndroid Build Coastguard Worker          optional_ == key.optional_;
53*6777b538SAndroid Build Coastguard Worker }
54*6777b538SAndroid Build Coastguard Worker 
55*6777b538SAndroid Build Coastguard Worker // static
CreateFor(const RecordParsed * record)56*6777b538SAndroid Build Coastguard Worker MDnsCache::Key MDnsCache::Key::CreateFor(const RecordParsed* record) {
57*6777b538SAndroid Build Coastguard Worker   return Key(record->type(),
58*6777b538SAndroid Build Coastguard Worker              record->name(),
59*6777b538SAndroid Build Coastguard Worker              GetOptionalFieldForRecord(record));
60*6777b538SAndroid Build Coastguard Worker }
61*6777b538SAndroid Build Coastguard Worker 
MDnsCache()62*6777b538SAndroid Build Coastguard Worker MDnsCache::MDnsCache() : entry_limit_(kDefaultEntryLimit) {}
63*6777b538SAndroid Build Coastguard Worker 
64*6777b538SAndroid Build Coastguard Worker MDnsCache::~MDnsCache() = default;
65*6777b538SAndroid Build Coastguard Worker 
LookupKey(const Key & key)66*6777b538SAndroid Build Coastguard Worker const RecordParsed* MDnsCache::LookupKey(const Key& key) {
67*6777b538SAndroid Build Coastguard Worker   auto found = mdns_cache_.find(key);
68*6777b538SAndroid Build Coastguard Worker   if (found != mdns_cache_.end()) {
69*6777b538SAndroid Build Coastguard Worker     return found->second.get();
70*6777b538SAndroid Build Coastguard Worker   }
71*6777b538SAndroid Build Coastguard Worker   return nullptr;
72*6777b538SAndroid Build Coastguard Worker }
73*6777b538SAndroid Build Coastguard Worker 
UpdateDnsRecord(std::unique_ptr<const RecordParsed> record)74*6777b538SAndroid Build Coastguard Worker MDnsCache::UpdateType MDnsCache::UpdateDnsRecord(
75*6777b538SAndroid Build Coastguard Worker     std::unique_ptr<const RecordParsed> record) {
76*6777b538SAndroid Build Coastguard Worker   Key cache_key = Key::CreateFor(record.get());
77*6777b538SAndroid Build Coastguard Worker 
78*6777b538SAndroid Build Coastguard Worker   // Ignore "goodbye" packets for records not in cache.
79*6777b538SAndroid Build Coastguard Worker   if (record->ttl() == 0 && !base::Contains(mdns_cache_, cache_key)) {
80*6777b538SAndroid Build Coastguard Worker     return NoChange;
81*6777b538SAndroid Build Coastguard Worker   }
82*6777b538SAndroid Build Coastguard Worker 
83*6777b538SAndroid Build Coastguard Worker   base::Time new_expiration = GetEffectiveExpiration(record.get());
84*6777b538SAndroid Build Coastguard Worker   if (next_expiration_ != base::Time())
85*6777b538SAndroid Build Coastguard Worker     new_expiration = std::min(new_expiration, next_expiration_);
86*6777b538SAndroid Build Coastguard Worker 
87*6777b538SAndroid Build Coastguard Worker   std::pair<RecordMap::iterator, bool> insert_result =
88*6777b538SAndroid Build Coastguard Worker       mdns_cache_.emplace(cache_key, nullptr);
89*6777b538SAndroid Build Coastguard Worker   UpdateType type = NoChange;
90*6777b538SAndroid Build Coastguard Worker   if (insert_result.second) {
91*6777b538SAndroid Build Coastguard Worker     type = RecordAdded;
92*6777b538SAndroid Build Coastguard Worker   } else {
93*6777b538SAndroid Build Coastguard Worker     if (record->ttl() != 0 &&
94*6777b538SAndroid Build Coastguard Worker         !record->IsEqual(insert_result.first->second.get(), true)) {
95*6777b538SAndroid Build Coastguard Worker       type = RecordChanged;
96*6777b538SAndroid Build Coastguard Worker     }
97*6777b538SAndroid Build Coastguard Worker   }
98*6777b538SAndroid Build Coastguard Worker 
99*6777b538SAndroid Build Coastguard Worker   insert_result.first->second = std::move(record);
100*6777b538SAndroid Build Coastguard Worker   next_expiration_ = new_expiration;
101*6777b538SAndroid Build Coastguard Worker   return type;
102*6777b538SAndroid Build Coastguard Worker }
103*6777b538SAndroid Build Coastguard Worker 
CleanupRecords(base::Time now,const RecordRemovedCallback & record_removed_callback)104*6777b538SAndroid Build Coastguard Worker void MDnsCache::CleanupRecords(
105*6777b538SAndroid Build Coastguard Worker     base::Time now,
106*6777b538SAndroid Build Coastguard Worker     const RecordRemovedCallback& record_removed_callback) {
107*6777b538SAndroid Build Coastguard Worker   base::Time next_expiration;
108*6777b538SAndroid Build Coastguard Worker 
109*6777b538SAndroid Build Coastguard Worker   // TODO(crbug.com/946688): Make overfill pruning more intelligent than a bulk
110*6777b538SAndroid Build Coastguard Worker   // clearing of everything.
111*6777b538SAndroid Build Coastguard Worker   bool clear_cache = IsCacheOverfilled();
112*6777b538SAndroid Build Coastguard Worker 
113*6777b538SAndroid Build Coastguard Worker   // We are guaranteed that |next_expiration_| will be at or before the next
114*6777b538SAndroid Build Coastguard Worker   // expiration. This allows clients to eagrely call CleanupRecords with
115*6777b538SAndroid Build Coastguard Worker   // impunity.
116*6777b538SAndroid Build Coastguard Worker   if (now < next_expiration_ && !clear_cache)
117*6777b538SAndroid Build Coastguard Worker     return;
118*6777b538SAndroid Build Coastguard Worker 
119*6777b538SAndroid Build Coastguard Worker   for (auto i = mdns_cache_.begin(); i != mdns_cache_.end();) {
120*6777b538SAndroid Build Coastguard Worker     base::Time expiration = GetEffectiveExpiration(i->second.get());
121*6777b538SAndroid Build Coastguard Worker     if (clear_cache || now >= expiration) {
122*6777b538SAndroid Build Coastguard Worker       record_removed_callback.Run(i->second.get());
123*6777b538SAndroid Build Coastguard Worker       i = mdns_cache_.erase(i);
124*6777b538SAndroid Build Coastguard Worker     } else {
125*6777b538SAndroid Build Coastguard Worker       if (next_expiration == base::Time() ||  expiration < next_expiration) {
126*6777b538SAndroid Build Coastguard Worker         next_expiration = expiration;
127*6777b538SAndroid Build Coastguard Worker       }
128*6777b538SAndroid Build Coastguard Worker       ++i;
129*6777b538SAndroid Build Coastguard Worker     }
130*6777b538SAndroid Build Coastguard Worker   }
131*6777b538SAndroid Build Coastguard Worker 
132*6777b538SAndroid Build Coastguard Worker   next_expiration_ = next_expiration;
133*6777b538SAndroid Build Coastguard Worker }
134*6777b538SAndroid Build Coastguard Worker 
FindDnsRecords(unsigned type,const std::string & name,std::vector<const RecordParsed * > * results,base::Time now) const135*6777b538SAndroid Build Coastguard Worker void MDnsCache::FindDnsRecords(unsigned type,
136*6777b538SAndroid Build Coastguard Worker                                const std::string& name,
137*6777b538SAndroid Build Coastguard Worker                                std::vector<const RecordParsed*>* results,
138*6777b538SAndroid Build Coastguard Worker                                base::Time now) const {
139*6777b538SAndroid Build Coastguard Worker   DCHECK(results);
140*6777b538SAndroid Build Coastguard Worker   results->clear();
141*6777b538SAndroid Build Coastguard Worker 
142*6777b538SAndroid Build Coastguard Worker   const std::string name_lowercase = base::ToLowerASCII(name);
143*6777b538SAndroid Build Coastguard Worker   auto i = mdns_cache_.lower_bound(Key(type, name, ""));
144*6777b538SAndroid Build Coastguard Worker   for (; i != mdns_cache_.end(); ++i) {
145*6777b538SAndroid Build Coastguard Worker     if (i->first.name_lowercase() != name_lowercase ||
146*6777b538SAndroid Build Coastguard Worker         (type != 0 && i->first.type() != type)) {
147*6777b538SAndroid Build Coastguard Worker       break;
148*6777b538SAndroid Build Coastguard Worker     }
149*6777b538SAndroid Build Coastguard Worker 
150*6777b538SAndroid Build Coastguard Worker     const RecordParsed* record = i->second.get();
151*6777b538SAndroid Build Coastguard Worker 
152*6777b538SAndroid Build Coastguard Worker     // Records are deleted only upon request.
153*6777b538SAndroid Build Coastguard Worker     if (now >= GetEffectiveExpiration(record)) continue;
154*6777b538SAndroid Build Coastguard Worker 
155*6777b538SAndroid Build Coastguard Worker     results->push_back(record);
156*6777b538SAndroid Build Coastguard Worker   }
157*6777b538SAndroid Build Coastguard Worker }
158*6777b538SAndroid Build Coastguard Worker 
RemoveRecord(const RecordParsed * record)159*6777b538SAndroid Build Coastguard Worker std::unique_ptr<const RecordParsed> MDnsCache::RemoveRecord(
160*6777b538SAndroid Build Coastguard Worker     const RecordParsed* record) {
161*6777b538SAndroid Build Coastguard Worker   Key key = Key::CreateFor(record);
162*6777b538SAndroid Build Coastguard Worker   auto found = mdns_cache_.find(key);
163*6777b538SAndroid Build Coastguard Worker 
164*6777b538SAndroid Build Coastguard Worker   if (found != mdns_cache_.end() && found->second.get() == record) {
165*6777b538SAndroid Build Coastguard Worker     std::unique_ptr<const RecordParsed> result = std::move(found->second);
166*6777b538SAndroid Build Coastguard Worker     mdns_cache_.erase(key);
167*6777b538SAndroid Build Coastguard Worker     return result;
168*6777b538SAndroid Build Coastguard Worker   }
169*6777b538SAndroid Build Coastguard Worker 
170*6777b538SAndroid Build Coastguard Worker   return nullptr;
171*6777b538SAndroid Build Coastguard Worker }
172*6777b538SAndroid Build Coastguard Worker 
IsCacheOverfilled() const173*6777b538SAndroid Build Coastguard Worker bool MDnsCache::IsCacheOverfilled() const {
174*6777b538SAndroid Build Coastguard Worker   return mdns_cache_.size() > entry_limit_;
175*6777b538SAndroid Build Coastguard Worker }
176*6777b538SAndroid Build Coastguard Worker 
177*6777b538SAndroid Build Coastguard Worker // static
GetOptionalFieldForRecord(const RecordParsed * record)178*6777b538SAndroid Build Coastguard Worker std::string MDnsCache::GetOptionalFieldForRecord(const RecordParsed* record) {
179*6777b538SAndroid Build Coastguard Worker   switch (record->type()) {
180*6777b538SAndroid Build Coastguard Worker     case PtrRecordRdata::kType: {
181*6777b538SAndroid Build Coastguard Worker       const PtrRecordRdata* rdata = record->rdata<PtrRecordRdata>();
182*6777b538SAndroid Build Coastguard Worker       return rdata->ptrdomain();
183*6777b538SAndroid Build Coastguard Worker     }
184*6777b538SAndroid Build Coastguard Worker     default:  // Most records are considered unique for our purposes
185*6777b538SAndroid Build Coastguard Worker       return "";
186*6777b538SAndroid Build Coastguard Worker   }
187*6777b538SAndroid Build Coastguard Worker }
188*6777b538SAndroid Build Coastguard Worker 
189*6777b538SAndroid Build Coastguard Worker // static
GetEffectiveExpiration(const RecordParsed * record)190*6777b538SAndroid Build Coastguard Worker base::Time MDnsCache::GetEffectiveExpiration(const RecordParsed* record) {
191*6777b538SAndroid Build Coastguard Worker   base::TimeDelta ttl;
192*6777b538SAndroid Build Coastguard Worker 
193*6777b538SAndroid Build Coastguard Worker   if (record->ttl()) {
194*6777b538SAndroid Build Coastguard Worker     ttl = base::Seconds(record->ttl());
195*6777b538SAndroid Build Coastguard Worker   } else {
196*6777b538SAndroid Build Coastguard Worker     ttl = base::Seconds(kZeroTTLSeconds);
197*6777b538SAndroid Build Coastguard Worker   }
198*6777b538SAndroid Build Coastguard Worker 
199*6777b538SAndroid Build Coastguard Worker   return record->time_created() + ttl;
200*6777b538SAndroid Build Coastguard Worker }
201*6777b538SAndroid Build Coastguard Worker 
202*6777b538SAndroid Build Coastguard Worker }  // namespace net
203