return ret;
}
+int32_t MemRecursorCache::handleHit(cache_t::iterator entry, const DNSName& qname, const ComboAddress& who, vector<DNSRecord>* res, vector<std::shared_ptr<RRSIGRecordContent>>* signatures, std::vector<std::shared_ptr<DNSRecord>>* authorityRecs, bool* variable, vState* state, bool* wasAuth)
+{
+ int32_t ttd = entry->d_ttd;
+
+ if(variable && !entry->d_netmask.empty()) {
+ *variable = true;
+ }
+
+ // cerr<<"Looking at "<<entry->d_records.size()<<" records for this name"<<endl;
+ if (res) {
+ for(const auto& k : entry->d_records) {
+ DNSRecord dr;
+ dr.d_name = qname;
+ dr.d_type = entry->d_qtype;
+ dr.d_class = QClass::IN;
+ dr.d_content = k;
+ dr.d_ttl = static_cast<uint32_t>(entry->d_ttd);
+ dr.d_place = DNSResourceRecord::ANSWER;
+ res->push_back(dr);
+ }
+ }
+
+ if(signatures) { // if you do an ANY lookup you are hosed XXXX
+ *signatures = entry->d_signatures;
+ }
+
+ if(authorityRecs) {
+ *authorityRecs = entry->d_authorityRecs;
+ }
+
+ if (state) {
+ *state = entry->d_state;
+ }
+
+ if (wasAuth) {
+ *wasAuth = entry->d_auth;
+ }
+
+ moveCacheItemToBack(d_cache, entry);
+
+ return ttd;
+}
+
+MemRecursorCache::cache_t::const_iterator MemRecursorCache::getEntryUsingIndex(time_t now, const DNSName &qname, uint16_t qtype, bool requireAuth, const ComboAddress& who)
+{
+ auto indexKey = tie(qname, qtype);
+ auto index = d_index.find(indexKey);
+ if (index != d_index.end() && !index->isEmpty()) {
+ /* we have netmask-specific entries, let's see if we match one */
+ while (true) {
+ const Netmask best = index->lookupBestMatch(who);
+ if (best.empty()) {
+ /* we have nothing more specific for you */
+ break;
+ }
+
+ auto key = boost::make_tuple(qname, qtype, best);
+ auto entry = d_cache.find(key);
+ if (entry == d_cache.end()) {
+ /* index is not up-to-date */
+ index->removeNetmask(best);
+ if (index->isEmpty()) {
+ d_index.erase(index);
+ break;
+ }
+ continue;
+ }
+
+ if (entry->d_ttd > now) {
+ if (!requireAuth || entry->d_auth) {
+ return entry;
+ }
+ }
+ else {
+ /* this netmask-specific entry has expired */
+ moveCacheItemToFront(d_cache, entry);
+ index->removeNetmask(best);
+ if (index->isEmpty()) {
+ d_index.erase(index);
+ break;
+ }
+ }
+ }
+ }
+
+ /* we have nothing specific, let's see if we have a generic one */
+ auto key = boost::make_tuple(qname, qtype, Netmask());
+ auto entry = d_cache.find(key);
+ if (entry != d_cache.end()) {
+ if (entry->d_ttd > now) {
+ if (!requireAuth || entry->d_auth) {
+ return entry;
+ }
+ }
+ else {
+ moveCacheItemToFront(d_cache, entry);
+ }
+ }
+
+ /* nothing for you, sorry */
+ return d_cache.end();
+}
+
// returns -1 for no hits
std::pair<MemRecursorCache::cache_t::const_iterator, MemRecursorCache::cache_t::const_iterator> MemRecursorCache::getEntries(const DNSName &qname, const QType& qt)
{
// cerr<<"looking up "<< qname<<"|"+qt.getName()<<"\n";
-
if(!d_cachecachevalid || d_cachedqname!= qname) {
// cerr<<"had cache cache miss"<<endl;
d_cachedqname=qname;
return d_cachecache;
}
-bool MemRecursorCache::entryMatches(cache_t::const_iterator& entry, const QType& qt, bool requireAuth, const ComboAddress& who)
+bool MemRecursorCache::entryMatches(cache_t::const_iterator& entry, uint16_t qt, bool requireAuth, const ComboAddress& who)
{
if (requireAuth && !entry->d_auth)
return false;
- return ((entry->d_qtype == qt.getCode() || qt.getCode()==QType::ANY ||
- (qt.getCode()==QType::ADDR && (entry->d_qtype == QType::A || entry->d_qtype == QType::AAAA)))
+ return ((entry->d_qtype == qt || qt == QType::ANY ||
+ (qt == QType::ADDR && (entry->d_qtype == QType::A || entry->d_qtype == QType::AAAA)))
&& (entry->d_netmask.empty() || entry->d_netmask.match(who)));
}
{
time_t ttd=0;
// cerr<<"looking up "<< qname<<"|"+qt.getName()<<"\n";
+ if(res) {
+ res->clear();
+ }
- auto entries = getEntries(qname, qt);
+ const uint16_t qtype = qt.getCode();
+ /* If we don't have any netmask-specific entries at all, let's just skip this
+ to be able to use the nice d_cachecache hack. */
+ if (qtype != QType::ANY && !d_index.empty()) {
+ if (qtype == QType::ADDR) {
+ int32_t ret = -1;
- if(res)
- res->clear();
+ auto entryA = getEntryUsingIndex(now, qname, QType::A, requireAuth, who);
+ if (entryA != d_cache.end()) {
+ ret = handleHit(entryA, qname, who, res, signatures, authorityRecs, variable, state, wasAuth);
+ }
+ auto entryAAAA = getEntryUsingIndex(now, qname, QType::AAAA, requireAuth, who);
+ if (entryAAAA != d_cache.end()) {
+ int32_t ttdAAAA = handleHit(entryA, qname, who, res, signatures, authorityRecs, variable, state, wasAuth);
+ if (ret > 0) {
+ ret = std::min(ret, ttdAAAA);
+ } else {
+ ret = ttdAAAA;
+ }
+ }
+ return ret > 0 ? static_cast<int32_t>(ret-now) : ret;
+ }
+ else {
+ auto entry = getEntryUsingIndex(now, qname, qtype, requireAuth, who);
+ if (entry != d_cache.end()) {
+ return static_cast<int32_t>(handleHit(entry, qname, who, res, signatures, authorityRecs, variable, state, wasAuth) - now);
+ }
+ return -1;
+ }
+ }
+
+ auto entries = getEntries(qname, qt);
if(entries.first!=entries.second) {
for(cache_t::const_iterator i=entries.first; i != entries.second; ++i) {
continue;
}
- if (!entryMatches(i, qt, requireAuth, who))
+ if (!entryMatches(i, qtype, requireAuth, who))
continue;
- if(variable && !i->d_netmask.empty()) {
- *variable=true;
- }
-
- ttd = i->d_ttd;
-
- // cerr<<"Looking at "<<i->d_records.size()<<" records for this name"<<endl;
- for(auto k=i->d_records.begin(); k != i->d_records.end(); ++k) {
- if(res) {
- DNSRecord dr;
- dr.d_name = qname;
- dr.d_type = i->d_qtype;
- dr.d_class = QClass::IN;
- dr.d_content = *k;
- dr.d_ttl = static_cast<uint32_t>(i->d_ttd);
- dr.d_place = DNSResourceRecord::ANSWER;
- res->push_back(dr);
- }
- }
-
- if(signatures) // if you do an ANY lookup you are hosed XXXX
- *signatures=i->d_signatures;
-
- if(authorityRecs) // if you do an ANY lookup you are hosed here too XXXX
- *authorityRecs=i->d_authorityRecs;
-
- moveCacheItemToBack(d_cache, i);
-
- if(state) {
- *state = i->d_state;
- }
-
- if(wasAuth) {
- *wasAuth = i->d_auth;
- }
+ ttd = handleHit(i, qname, who, res, signatures, authorityRecs, variable, state, wasAuth);
if(qt.getCode()!=QType::ANY && qt.getCode()!=QType::ADDR) // normally if we have a hit, we are done
break;
return true;
}
-void MemRecursorCache::replace(time_t now, const DNSName &qname, const QType& qt, const vector<DNSRecord>& content, const vector<shared_ptr<RRSIGRecordContent>>& signatures, const std::vector<std::shared_ptr<DNSRecord>>& authorityRecs, bool auth, boost::optional<Netmask> ednsmask, vState state)
+void MemRecursorCache::replace(time_t now, const DNSName &qname, const QType& qt, const vector<DNSRecord>& content, const vector<shared_ptr<RRSIGRecordContent>>& signatures, const std::vector<std::shared_ptr<DNSRecord>>& authorityRecs, bool auth, boost::optional<Netmask> ednsmask, vState state)
{
- d_cachecachevalid=false;
- cache_t::iterator stored;
+ d_cachecachevalid = false;
+
+ auto key = boost::make_tuple(qname, qt.getCode(), ednsmask ? *ednsmask : Netmask());
bool isNew = false;
- auto key=boost::make_tuple(qname, qt.getCode(), ednsmask ? *ednsmask : Netmask());
- stored=d_cache.find(key);
- if(stored == d_cache.end()) {
- stored=d_cache.insert(CacheEntry(key,CacheEntry::records_t(), auth)).first;
+ cache_t::iterator stored = d_cache.find(key);
+ if (stored == d_cache.end()) {
+ stored = d_cache.insert(CacheEntry(key, CacheEntry::records_t(), auth)).first;
isNew = true;
+
+ /* don't bother building an index if we don't have any netmask-specific entries */
+ if (ednsmask && !ednsmask->empty()) {
+ auto indexKey = boost::make_tuple(qname, qt.getCode());
+ auto index = d_index.find(indexKey);
+ if (index == d_index.end()) {
+ index = d_index.insert(IndexEntry(qname, qt.getCode())).first;
+ }
+ index->addMask(*ednsmask);
+ }
}
time_t maxTTD=std::numeric_limits<time_t>::max();
//else cerr<<"\tNot nuking"<<endl;
- for(auto i=content.cbegin(); i != content.cend(); ++i) {
-
+ for(const auto i : content) {
/* Yes, we have altered the d_ttl value by adding time(nullptr) to it
prior to calling this function, so the TTL actually holds a TTD. */
- ce.d_ttd=min(maxTTD, static_cast<time_t>(i->d_ttl)); // XXX this does weird things if TTLs differ in the set
- // cerr<<"To store: "<<i->d_content->getZoneRepresentation()<<" with ttl/ttd "<<i->d_ttl<<", capped at: "<<maxTTD<<endl;
- ce.d_records.push_back(i->d_content);
+ ce.d_ttd=min(maxTTD, static_cast<time_t>(i.d_ttl)); // XXX this does weird things if TTLs differ in the set
+ // cerr<<"To store: "<<i.d_content->getZoneRepresentation()<<" with ttl/ttd "<<i.d_ttl<<", capped at: "<<maxTTD<<endl;
+ ce.d_records.push_back(i.d_content);
// there was code here that did things with TTL and auth. Unsure if it was good. XXX
}
pair<cache_t::iterator, cache_t::iterator> range;
if(!sub) {
- if(qtype==0xffff)
- range=d_cache.equal_range(tie(name));
- else
+ pair<index_t::iterator, index_t::iterator> indexRange;
+ if(qtype==0xffff) {
+ range = d_cache.equal_range(tie(name));
+ indexRange = d_index.equal_range(tie(name));
+ }
+ else {
range=d_cache.equal_range(tie(name, qtype));
+ indexRange = d_index.equal_range(tie(name, qtype));
+ }
for(cache_t::const_iterator i=range.first; i != range.second; ) {
count++;
d_cache.erase(i++);
}
+ for(auto i = indexRange.first; i != indexRange.second; ) {
+ d_index.erase(i++);
+ }
}
else {
for(auto iter = d_cache.lower_bound(tie(name)); iter != d_cache.end(); ) {
else
iter++;
}
+ for(auto iter = d_index.lower_bound(tie(name)); iter != d_index.end(); ) {
+ if(!iter->d_qname.isPartOf(name))
+ break;
+ if(iter->d_qtype == qtype || qtype == 0xffff) {
+ d_index.erase(iter++);
+ }
+ else {
+ iter++;
+ }
+ }
}
return count;
}
return false;
}
-bool MemRecursorCache::updateValidationStatus(const DNSName &qname, const QType& qt, const ComboAddress& who, bool requireAuth, vState newState)
+bool MemRecursorCache::updateValidationStatus(time_t now, const DNSName &qname, const QType& qt, const ComboAddress& who, bool requireAuth, vState newState)
{
bool updated = false;
+ uint16_t qtype = qt.getCode();
+ if (qtype != QType::ANY && qtype != QType::ADDR && !d_index.empty()) {
+ auto entry = getEntryUsingIndex(now, qname, qtype, requireAuth, who);
+ if (entry == d_cache.end()) {
+ return false;
+ }
+
+ entry->d_state = newState;
+ return true;
+ }
+
auto entries = getEntries(qname, qt);
for(auto i = entries.first; i != entries.second; ++i) {
-
- if (!entryMatches(i, qt, requireAuth, who))
+ if (!entryMatches(i, qtype, requireAuth, who))
continue;
i->d_state = newState;
updated = true;
- if(qt.getCode()!=QType::ANY && qt.getCode()!=QType::ADDR) // normally if we have a hit, we are done
+ if(qtype != QType::ANY && qtype != QType::ADDR) // normally if we have a hit, we are done
break;
-
}
return updated;
uint64_t count=0;
time_t now=time(0);
- for(auto i=sidx.cbegin(); i != sidx.cend(); ++i) {
- for(auto j=i->d_records.cbegin(); j != i->d_records.cend(); ++j) {
+ for(const auto i : sidx) {
+ for(const auto j : i.d_records) {
count++;
try {
- fprintf(fp, "%s %" PRId64 " IN %s %s ; %s\n", i->d_qname.toString().c_str(), static_cast<int64_t>(i->d_ttd - now), DNSRecordContent::NumberToType(i->d_qtype).c_str(), (*j)->getZoneRepresentation().c_str(), i->d_netmask.empty() ? "" : i->d_netmask.toString().c_str());
+ fprintf(fp, "%s %" PRId64 " IN %s %s ; %s\n", i.d_qname.toString().c_str(), static_cast<int64_t>(i.d_ttd - now), DNSRecordContent::NumberToType(i.d_qtype).c_str(), j->getZoneRepresentation().c_str(), i.d_netmask.empty() ? "" : i.d_netmask.toString().c_str());
}
catch(...) {
- fprintf(fp, "; error printing '%s'\n", i->d_qname.empty() ? "EMPTY" : i->d_qname.toString().c_str());
+ fprintf(fp, "; error printing '%s'\n", i.d_qname.empty() ? "EMPTY" : i.d_qname.toString().c_str());
}
}
}
d_cachecachevalid=false;
unsigned int maxCached=::arg().asNum("max-cache-entries") / g_numThreads;
- pruneCollection(d_cache, maxCached);
+ pruneCollection(*this, d_cache, maxCached);
}
#undef L
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
+#include <boost/multi_index/hashed_index.hpp>
#include <boost/tuple/tuple_comparison.hpp>
#include <boost/multi_index/key_extractors.hpp>
#include <boost/multi_index/sequenced_index.hpp>
int doWipeCache(const DNSName& name, bool sub, uint16_t qtype=0xffff);
bool doAgeCache(time_t now, const DNSName& name, uint16_t qtype, uint32_t newTTL);
- bool updateValidationStatus(const DNSName &qname, const QType& qt, const ComboAddress& who, bool requireAuth, vState newState);
+ bool updateValidationStatus(time_t now, const DNSName &qname, const QType& qt, const ComboAddress& who, bool requireAuth, vState newState);
uint64_t cacheHits, cacheMisses;
struct CacheEntry
{
CacheEntry(const boost::tuple<DNSName, uint16_t, Netmask>& key, const vector<shared_ptr<DNSRecordContent>>& records, bool auth) :
- d_qname(key.get<0>()), d_qtype(key.get<1>()), d_auth(auth), d_ttd(0), d_records(records), d_netmask(key.get<2>())
+ d_records(records), d_qname(key.get<0>()), d_netmask(key.get<2>()), d_ttd(0), d_qtype(key.get<1>()), d_auth(auth)
{}
typedef vector<std::shared_ptr<DNSRecordContent>> records_t;
- vector<std::shared_ptr<RRSIGRecordContent>> d_signatures;
time_t getTTD() const
{
return d_ttd;
}
- DNSName d_qname;
- uint16_t d_qtype;
- bool d_auth;
- time_t d_ttd;
records_t d_records;
+ vector<std::shared_ptr<RRSIGRecordContent>> d_signatures;
std::vector<std::shared_ptr<DNSRecord>> d_authorityRecs;
+ DNSName d_qname;
Netmask d_netmask;
mutable vState d_state;
+ time_t d_ttd;
+ uint16_t d_qtype;
+ bool d_auth;
+ };
+
+ class IndexEntry
+ {
+ public:
+ IndexEntry(const DNSName& qname, uint16_t qtype): d_qname(qname), d_qtype(qtype)
+ {
+ }
+
+ Netmask lookupBestMatch(const ComboAddress& addr) const
+ {
+ Netmask result = Netmask();
+
+ const auto best = d_nmt.lookup(addr);
+ if (best != nullptr) {
+ result = best->first;
+ }
+
+ return result;
+ }
+
+ void addMask(const Netmask& nm) const
+ {
+ d_nmt.insert(nm).second = true;
+ }
+
+ void removeNetmask(const Netmask& nm) const
+ {
+ d_nmt.erase(nm);
+ }
+
+ bool isEmpty() const
+ {
+ return d_nmt.empty();
+ }
+
+ mutable NetmaskTree<bool> d_nmt;
+ DNSName d_qname;
+ uint16_t d_qtype;
};
typedef multi_index_container<
sequenced<>
>
> cache_t;
+ typedef multi_index_container<
+ IndexEntry,
+ indexed_by <
+ ordered_unique <
+ composite_key<
+ IndexEntry,
+ member<IndexEntry,DNSName,&IndexEntry::d_qname>,
+ member<IndexEntry,uint16_t,&IndexEntry::d_qtype>
+ >
+ >
+ >
+ > index_t;
cache_t d_cache;
+ index_t d_index;
pair<cache_t::iterator, cache_t::iterator> d_cachecache;
DNSName d_cachedqname;
bool d_cachecachevalid;
bool attemptToRefreshNSTTL(const QType& qt, const vector<DNSRecord>& content, const CacheEntry& stored);
- bool entryMatches(cache_t::const_iterator& entry, const QType& qt, bool requireAuth, const ComboAddress& who);
+ bool entryMatches(cache_t::const_iterator& entry, uint16_t qt, bool requireAuth, const ComboAddress& who);
std::pair<cache_t::const_iterator, cache_t::const_iterator> getEntries(const DNSName &qname, const QType& qt);
+ cache_t::const_iterator getEntryUsingIndex(time_t now, const DNSName &qname, uint16_t qtype, bool requireAuth, const ComboAddress& who);
+ int32_t handleHit(cache_t::iterator entry, const DNSName& qname, const ComboAddress& who, vector<DNSRecord>* res, vector<std::shared_ptr<RRSIGRecordContent>>* signatures, std::vector<std::shared_ptr<DNSRecord>>* authorityRecs, bool* variable, vState* state, bool* wasAuth);
+public:
+ void preRemoval(const CacheEntry& entry)
+ {
+ if (entry.d_netmask.empty()) {
+ return;
+ }
+
+ auto key = tie(entry.d_qname, entry.d_qtype);
+ auto indexEntry = d_index.find(key);
+ if (indexEntry != d_index.end()) {
+ indexEntry->removeNetmask(entry.d_netmask);
+ if (indexEntry->isEmpty()) {
+ d_index.erase(indexEntry);
+ }
+ }
+ }
};
#endif