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