#include "query-local-address.hh"
#include "rec-snmp.hh"
+#include "rec-taskqueue.hh"
#ifdef HAVE_SYSTEMD
#include <systemd/sd-daemon.h>
g_log<<Logger::Error<<"Any other exception in a resolver context "<< makeLoginfo(dc) <<endl;
}
+ runTaskOnce(g_logCommonErrors);
+
g_stats.maxMThreadStackUsage = max(MT->getMaxStackUsage(), g_stats.maxMThreadStackUsage);
}
auto rc_stats = g_recCache->stats();
double r = rc_stats.second == 0 ? 0.0 : (100.0 * rc_stats.first / rc_stats.second);
uint64_t negCacheSize = g_negCache->size();
+ auto taskPushes = getTaskPushes();
+ auto taskExpired = getTaskExpired();
+ auto taskSize = getTaskSize();
if(g_stats.qcounter && (cacheHits + cacheMisses) && SyncRes::s_queries && SyncRes::s_outqueries) {
g_log<<Logger::Notice<<"stats: "<<g_stats.qcounter<<" questions, "<<
}
}
+ g_log<<Logger::Notice<<"stats: tasks pushed/expired/queuesize: " << taskPushes << '/' << taskExpired << '/' << taskSize << endl;
time_t now = time(0);
if(lastOutputTime && lastQueryCount && now != lastOutputTime) {
g_log<<Logger::Notice<<"stats: "<< (SyncRes::s_queries - lastQueryCount) / (now - lastOutputTime) <<" qps (average over "<< (now - lastOutputTime) << " seconds)"<<endl;
}
s_running=true;
+ runTaskOnce(g_logCommonErrors);
+
struct timeval now, past;
Utility::gettimeofday(&now, nullptr);
past = now;
return a;
}
-
/*
This function should only be called by the handler to gather metrics, wipe the cache,
reload the Lua script (not the Lua config) or change the current trace regex,
SyncRes::s_maxtotusec=1000*::arg().asNum("max-total-msec");
SyncRes::s_maxdepth=::arg().asNum("max-recursion-depth");
SyncRes::s_rootNXTrust = ::arg().mustDo( "root-nx-trust");
+ SyncRes::s_refresh_ttlperc = ::arg().asNum("refresh-on-ttl-perc");
+ RecursorPacketCache::s_refresh_ttlperc = SyncRes::s_refresh_ttlperc;
+
if(SyncRes::s_serverID.empty()) {
SyncRes::s_serverID = myHostname;
}
::arg().setSwitch("nothing-below-nxdomain", "When an NXDOMAIN exists in cache for a name with fewer labels than the qname, send NXDOMAIN without doing a lookup (see RFC 8020)")="dnssec";
::arg().set("max-generate-steps", "Maximum number of $GENERATE steps when loading a zone from a file")="0";
::arg().set("record-cache-shards", "Number of shards in the record cache")="1024";
+ ::arg().set("refresh-on-ttl-perc", "If a record is requested from the cache and only this % of original TTL remains, refetch") = "0";
#ifdef NOD_ENABLED
::arg().set("new-domain-tracking", "Track newly observed domains (i.e. never seen before).")="no";
return ret;
}
+
#include "secpoll-recursor.hh"
#include "pubsuffix.hh"
#include "namespaces.hh"
+#include "rec-taskqueue.hh"
+
std::mutex g_carbon_config_lock;
static map<string, const uint32_t*> d_get32bitpointers;
addGetStat("nod-lookups-dropped-oversize", &g_stats.nodLookupsDroppedOversize);
+ addGetStat("taskqueue-pushed", []() { return getTaskPushes(); });
+ addGetStat("taskqueue-expired", []() { return getTaskExpired(); });
+ addGetStat("taskqueue-size", []() { return getTaskSize(); });
+
/* make sure that the ECS stats are properly initialized */
SyncRes::clearECSStats();
for (size_t idx = 0; idx < SyncRes::s_ecsResponsesBySubnetSize4.size(); idx++) {
return ret + "\n";
}
-
string RecursorControlParser::getAnswer(const string& question, RecursorControlParser::func_t** command)
{
*command=nop;
#include "cachecleaner.hh"
#include "dns.hh"
#include "namespaces.hh"
+#include "rec-taskqueue.hh"
RecursorPacketCache::RecursorPacketCache()
{
d_hits = d_misses = 0;
}
+unsigned int RecursorPacketCache::s_refresh_ttlperc{0};
+
int RecursorPacketCache::doWipePacketCache(const DNSName& name, uint16_t qtype, bool subtree)
{
int count=0;
bool RecursorPacketCache::qrMatch(const packetCache_t::index<HashTag>::type::iterator& iter, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass)
{
- // this ignores checking on the EDNS subnet flags!
+ // this ignores checking on the EDNS subnet flags!
if (qname != iter->d_name || iter->d_type != qtype || iter->d_class != qclass) {
return false;
}
if (now < iter->d_ttd) { // it is right, it is fresh!
*age = static_cast<uint32_t>(now - iter->d_creation);
+ // we know ttl is > 0
+ uint32_t ttl = static_cast<uint32_t>(iter->d_ttd - now);
+ if (s_refresh_ttlperc > 0 && !iter->d_submitted) {
+ const uint32_t deadline = iter->getOrigTTL() * s_refresh_ttlperc / 100;
+ const bool almostExpired = ttl <= deadline;
+ if (almostExpired) {
+ iter->d_submitted = true;
+ pushTask(qname, qtype, iter->d_ttd);
+ }
+ }
*responsePacket = iter->d_packet;
responsePacket->replace(0, 2, queryPacket.c_str(), 2);
*valState = iter->d_vstate;
-
+
const size_t wirelength = qname.wirelength();
if (responsePacket->size() > (sizeof(dnsheader) + wirelength)) {
responsePacket->replace(sizeof(dnsheader), wirelength, queryPacket, sizeof(dnsheader), wirelength);
return true;
}
else {
- moveCacheItemToFront<SequencedTag>(d_packetCache, iter);
+ moveCacheItemToFront<SequencedTag>(d_packetCache, iter);
d_misses++;
break;
}
iter->d_ttd = now + ttl;
iter->d_creation = now;
iter->d_vstate = valState;
-
+ iter->d_submitted = false;
if (pbdata) {
iter->d_pbdata = std::move(*pbdata);
}
break;
}
-
+
if(iter == range.second) { // nothing to refresh
struct Entry e(qname, std::move(responsePacket), std::move(query));
e.d_qhash = qhash;
e.d_creation = now;
e.d_tag = tag;
e.d_vstate = valState;
-
+ e.d_submitted = false;
if (pbdata) {
e.d_pbdata = std::move(*pbdata);
}
class RecursorPacketCache: public PacketCache
{
public:
+ static unsigned int s_refresh_ttlperc;
+
struct PBData {
std::string d_message;
std::string d_response;
uint16_t d_type;
uint16_t d_class;
mutable vState d_vstate;
+ mutable bool d_submitted; // whether this entry has been queued for refetch
inline bool operator<(const struct Entry& rhs) const;
time_t getTTD() const
{
return d_ttd;
}
+
+ uint32_t getOrigTTL() const {
+ return d_ttd - d_creation;
+ }
};
struct SequencedTag{};
#include "recursor_cache.hh"
#include "namespaces.hh"
#include "cachecleaner.hh"
+#include "rec-taskqueue.hh"
MemRecursorCache::MemRecursorCache(size_t mapsCount) : d_maps(mapsCount)
{
}
}
-int32_t MemRecursorCache::handleHit(MapCombo& map, MemRecursorCache::OrderedTagIterator_t& entry, const DNSName& qname, vector<DNSRecord>* res, vector<std::shared_ptr<RRSIGRecordContent>>* signatures, std::vector<std::shared_ptr<DNSRecord>>* authorityRecs, bool* variable, boost::optional<vState>& state, bool* wasAuth, DNSName* fromAuthZone)
+int32_t MemRecursorCache::handleHit(MapCombo& map, MemRecursorCache::OrderedTagIterator_t& entry, const DNSName& qname, uint32_t& origTTL, vector<DNSRecord>* res, vector<std::shared_ptr<RRSIGRecordContent>>* signatures, std::vector<std::shared_ptr<DNSRecord>>* authorityRecs, bool* variable, boost::optional<vState>& state, bool* wasAuth, DNSName* fromAuthZone)
{
// MUTEX SHOULD BE ACQUIRED
int32_t ttd = entry->d_ttd;
+ origTTL = entry->d_orig_ttl;
if (variable && (!entry->d_netmask.empty() || entry->d_rtag)) {
*variable = true;
}
- // cerr<<"Looking at "<<entry->d_records.size()<<" records for this name"<<endl;
if (res) {
res->reserve(res->size() + entry->d_records.size());
bool match = (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));
- //cerr << match << "N " << qt << ':' << entry->d_qtype << ' ' << entry->d_netmask.toString() << ':' << who.toString() << endl;
return match;
}
+// Fake a cache miss if more than refreshTTLPerc of the original TTL has passed
+int32_t MemRecursorCache::fakeTTD(MemRecursorCache::OrderedTagIterator_t& entry, const DNSName& qname, uint16_t qtype, int32_t ret, time_t now, uint32_t origTTL, bool refresh)
+{
+ int32_t ttl = static_cast<int32_t>(ret - now);
+ if (ttl > 0 && SyncRes::s_refresh_ttlperc > 0) {
+ const uint32_t deadline = origTTL * SyncRes::s_refresh_ttlperc / 100;
+ const bool almostExpired = static_cast<uint32_t>(ttl) <= deadline;
+ if (almostExpired) {
+ if (refresh) {
+ return -1;
+ } else {
+ if (!entry->d_submitted) {
+ pushTask(qname, qtype, entry->d_ttd);
+ entry->d_submitted = true;
+ }
+ }
+ }
+ }
+ return ttl;
+}
// returns -1 for no hits
-int32_t MemRecursorCache::get(time_t now, const DNSName &qname, const QType& qt, bool requireAuth, vector<DNSRecord>* res, const ComboAddress& who, const OptTag& routingTag, vector<std::shared_ptr<RRSIGRecordContent>>* signatures, std::vector<std::shared_ptr<DNSRecord>>* authorityRecs, bool* variable, vState* state, bool* wasAuth, DNSName* fromAuthZone)
+int32_t MemRecursorCache::get(time_t now, const DNSName &qname, const QType& qt, bool requireAuth, vector<DNSRecord>* res, const ComboAddress& who, bool refresh, const OptTag& routingTag, vector<std::shared_ptr<RRSIGRecordContent>>* signatures, std::vector<std::shared_ptr<DNSRecord>>* authorityRecs, bool* variable, vState* state, bool* wasAuth, DNSName* fromAuthZone)
{
boost::optional<vState> cachedState{boost::none};
time_t ttd=0;
- // cerr<<"looking up "<< qname<<"|"+qt.getName()<<"\n";
+ uint32_t origTTL;
+
if(res) {
res->clear();
}
auto entryA = getEntryUsingECSIndex(map, now, qname, QType::A, requireAuth, who);
if (entryA != map.d_map.end()) {
- ret = handleHit(map, entryA, qname, res, signatures, authorityRecs, variable, cachedState, wasAuth, fromAuthZone);
+ ret = handleHit(map, entryA, qname, origTTL, res, signatures, authorityRecs, variable, cachedState, wasAuth, fromAuthZone);
}
auto entryAAAA = getEntryUsingECSIndex(map, now, qname, QType::AAAA, requireAuth, who);
if (entryAAAA != map.d_map.end()) {
- int32_t ttdAAAA = handleHit(map, entryAAAA, qname, res, signatures, authorityRecs, variable, cachedState, wasAuth, fromAuthZone);
+ int32_t ttdAAAA = handleHit(map, entryAAAA, qname, origTTL, res, signatures, authorityRecs, variable, cachedState, wasAuth, fromAuthZone);
if (ret > 0) {
ret = std::min(ret, ttdAAAA);
} else {
else {
auto entry = getEntryUsingECSIndex(map, now, qname, qtype, requireAuth, who);
if (entry != map.d_map.end()) {
- int32_t ret = handleHit(map, entry, qname, res, signatures, authorityRecs, variable, cachedState, wasAuth, fromAuthZone);
+ int32_t ret = handleHit(map, entry, qname, origTTL, res, signatures, authorityRecs, variable, cachedState, wasAuth, fromAuthZone);
if (state && cachedState) {
*state = *cachedState;
}
- return static_cast<int32_t>(ret-now);
+ return fakeTTD(entry, qname, qtype, ret, now, origTTL, refresh);
}
return -1;
}
auto entries = getEntries(map, qname, qt, routingTag);
if (entries.first != entries.second) {
+ OrderedTagIterator_t firstIndexIterator;
for (auto i=entries.first; i != entries.second; ++i) {
- auto firstIndexIterator = map.d_map.project<OrderedTag>(i);
+ firstIndexIterator = map.d_map.project<OrderedTag>(i);
if (i->d_ttd <= now) {
moveCacheItemToFront<SequencedTag>(map.d_map, firstIndexIterator);
continue;
continue;
}
- ttd = handleHit(map, firstIndexIterator, qname, res, signatures, authorityRecs, variable, cachedState, wasAuth, fromAuthZone);
+ ttd = handleHit(map, firstIndexIterator, qname, origTTL, res, signatures, authorityRecs, variable, cachedState, wasAuth, fromAuthZone);
if (qt.getCode() != QType::ANY && qt.getCode() != QType::ADDR) { // normally if we have a hit, we are done
break;
if (state && cachedState) {
*state = *cachedState;
}
- return static_cast<int32_t>(ttd-now);
+ return fakeTTD(firstIndexIterator, qname, qtype, ttd, now, origTTL, refresh);
}
}
// Try (again) without tag
auto entries = getEntries(map, qname, qt, boost::none);
if (entries.first != entries.second) {
+ OrderedTagIterator_t firstIndexIterator;
for (auto i=entries.first; i != entries.second; ++i) {
- auto firstIndexIterator = map.d_map.project<OrderedTag>(i);
+ firstIndexIterator = map.d_map.project<OrderedTag>(i);
if (i->d_ttd <= now) {
moveCacheItemToFront<SequencedTag>(map.d_map, firstIndexIterator);
continue;
continue;
}
- ttd = handleHit(map, firstIndexIterator, qname, res, signatures, authorityRecs, variable, cachedState, wasAuth, fromAuthZone);
+ ttd = handleHit(map, firstIndexIterator, qname, origTTL, res, signatures, authorityRecs, variable, cachedState, wasAuth, fromAuthZone);
if (qt.getCode() != QType::ANY && qt.getCode() != QType::ADDR) { // normally if we have a hit, we are done
break;
if (state && cachedState) {
*state = *cachedState;
}
- return static_cast<int32_t>(ttd-now);
+ return fakeTTD(firstIndexIterator, qname, qtype, ttd, now, origTTL, refresh);
}
return -1;
}
const lock l(map);
map.d_cachecachevalid = false;
- // cerr<<"Replacing "<<qname<<" for "<< (ednsmask ? ednsmask->toString() : "everyone") << endl;
if (ednsmask) {
ednsmask = ednsmask->getNormalized();
}
CacheEntry ce=*stored; // this is a COPY
ce.d_qtype=qt.getCode();
- // cerr<<"asked to store "<< (qname.empty() ? "EMPTY" : qname.toString()) <<"|"+qt.getName()<<" -> '";
- // cerr<<(content.empty() ? string("EMPTY CONTENT") : content.begin()->d_content->getZoneRepresentation())<<"', auth="<<auth<<", ce.auth="<<ce.d_auth;
- // cerr<<", ednsmask: " << (ednsmask ? ednsmask->toString() : "none") <<endl;
-
if(!auth && ce.d_auth) { // unauth data came in, we have some auth data, but is it fresh?
if(ce.d_ttd > now) { // we still have valid data, ignore unauth data
- // cerr<<"\tStill hold valid auth data, and the new data is unauth, return\n";
return;
}
else {
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_ttd = min(maxTTD, static_cast<time_t>(i.d_ttl)); // XXX this does weird things if TTLs differ in the set
+ ce.d_orig_ttl = ce.d_ttd - now;
ce.d_records.push_back(i.d_content);
}
if (!isNew) {
moveCacheItemToBack<SequencedTag>(map.d_map, stored);
}
+ ce.d_submitted = false;
map.d_map.replace(stored, ce);
}
for (const auto& j : i.d_records) {
count++;
try {
- fprintf(fp.get(), "%s %" PRId64 " IN %s %s ; (%s) auth=%i zone=%s from=%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(), vStateToString(i.d_state).c_str(), i.d_auth, i.d_authZone.toLogString().c_str(), i.d_from.toString().c_str(), i.d_netmask.empty() ? "" : i.d_netmask.toString().c_str(), !i.d_rtag ? "" : i.d_rtag.get().c_str());
+ fprintf(fp.get(), "%s %" PRIu32 " %" PRId64 " IN %s %s ; (%s) auth=%i zone=%s from=%s %s %s\n", i.d_qname.toString().c_str(), i.d_orig_ttl, static_cast<int64_t>(i.d_ttd - now), DNSRecordContent::NumberToType(i.d_qtype).c_str(), j->getZoneRepresentation().c_str(), vStateToString(i.d_state).c_str(), i.d_auth, i.d_authZone.toLogString().c_str(), i.d_from.toString().c_str(), i.d_netmask.empty() ? "" : i.d_netmask.toString().c_str(), !i.d_rtag ? "" : i.d_rtag.get().c_str());
}
catch(...) {
fprintf(fp.get(), "; error printing '%s'\n", i.d_qname.empty() ? "EMPTY" : i.d_qname.toString().c_str());
for (const auto &sig : i.d_signatures) {
count++;
try {
- fprintf(fp.get(), "%s %" PRId64 " IN RRSIG %s ; %s\n", i.d_qname.toString().c_str(), static_cast<int64_t>(i.d_ttd - now), sig->getZoneRepresentation().c_str(), i.d_netmask.empty() ? "" : i.d_netmask.toString().c_str());
+ fprintf(fp.get(), "%s %" PRIu32 " %" PRId64 " IN RRSIG %s ; %s\n", i.d_qname.toString().c_str(), i.d_orig_ttl, static_cast<int64_t>(i.d_ttd - now), sig->getZoneRepresentation().c_str(), i.d_netmask.empty() ? "" : i.d_netmask.toString().c_str());
}
catch(...) {
fprintf(fp.get(), "; error printing '%s'\n", i.d_qname.empty() ? "EMPTY" : i.d_qname.toString().c_str());
typedef boost::optional<std::string> OptTag;
- int32_t get(time_t, const DNSName &qname, const QType& qt, bool requireAuth, vector<DNSRecord>* res, const ComboAddress& who, const OptTag& routingTag = boost::none, vector<std::shared_ptr<RRSIGRecordContent>>* signatures=nullptr, std::vector<std::shared_ptr<DNSRecord>>* authorityRecs=nullptr, bool* variable=nullptr, vState* state=nullptr, bool* wasAuth=nullptr, DNSName* fromAuthZone=nullptr);
+int32_t get(time_t, const DNSName &qname, const QType& qt, bool requireAuth, vector<DNSRecord>* res, const ComboAddress& who, bool refresh = false, const OptTag& routingTag = boost::none, vector<std::shared_ptr<RRSIGRecordContent>>* signatures=nullptr, std::vector<std::shared_ptr<DNSRecord>>* authorityRecs=nullptr, bool* variable=nullptr, vState* state=nullptr, bool* wasAuth=nullptr, DNSName* fromAuthZone=nullptr);
void replace(time_t, 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, const DNSName& authZone, boost::optional<Netmask> ednsmask=boost::none, const OptTag& routingTag = boost::none, vState state=vState::Indeterminate, boost::optional<ComboAddress> from=boost::none);
OptTag d_rtag;
mutable vState d_state;
mutable time_t d_ttd;
+ uint32_t d_orig_ttl;
uint16_t d_qtype;
bool d_auth;
+ mutable bool d_submitted; // whether this entry has been queued for refetch
};
/* The ECS Index (d_ecsIndex) keeps track of whether there is any ECS-specific
return d_maps[qname.hash() % d_maps.size()];
}
+ static int32_t fakeTTD(OrderedTagIterator_t& entry, const DNSName& qname, uint16_t qtype, int32_t ret, time_t now, uint32_t origTTL, bool refresh);
+
bool entryMatches(OrderedTagIterator_t& entry, uint16_t qt, bool requireAuth, const ComboAddress& who);
Entries getEntries(MapCombo& map, const DNSName &qname, const QType& qt, const OptTag& rtag);
cache_t::const_iterator getEntryUsingECSIndex(MapCombo& map, time_t now, const DNSName &qname, uint16_t qtype, bool requireAuth, const ComboAddress& who);
- int32_t handleHit(MapCombo& map, OrderedTagIterator_t& entry, const DNSName& qname, vector<DNSRecord>* res, vector<std::shared_ptr<RRSIGRecordContent>>* signatures, std::vector<std::shared_ptr<DNSRecord>>* authorityRecs, bool* variable, boost::optional<vState>& state, bool* wasAuth, DNSName* authZone);
+
+ int32_t handleHit(MapCombo& map, OrderedTagIterator_t& entry, const DNSName& qname, uint32_t& origTTL, vector<DNSRecord>* res, vector<std::shared_ptr<RRSIGRecordContent>>* signatures, std::vector<std::shared_ptr<DNSRecord>>* authorityRecs, bool* variable, boost::optional<vState>& state, bool* wasAuth, DNSName* authZone);
public:
struct lock {
rec-lua-conf.hh rec-lua-conf.cc \
rec-protozero.cc rec-protozero.hh \
rec-snmp.hh rec-snmp.cc \
+ rec-taskqueue.cc rec-taskqueue.hh \
rec_channel.cc rec_channel.hh rec_metrics.hh \
rec_channel_rec.cc \
recpacketcache.cc recpacketcache.hh \
stable-bloom.hh \
svc-records.cc svc-records.hh \
syncres.cc syncres.hh \
+ taskqueue.cc taskqueue.hh \
threadname.hh threadname.cc \
tsigverifier.cc tsigverifier.hh \
ueberbackend.hh \
``record-cache-contented/record-cache-acquired``, you can try to
enlarge this value or run with fewer threads.
+.. _setting-refresh-on-ttl-perc:
+
+``refresh-on-ttl-perc``
+-----------------------
+.. versionadded: 4.5.0
+
+- Integer
+- Default: 0
+
+Sets the "refresh almost expired" percentage of the record cache. Whenever a record is fetched from the packet or record cache
+and only ``refresh-on-ttl-perc`` percent or less of its original TTL is left, a task is queued to refetch the name/type combination to
+update the record cache. In most cases this causes future queries to always see a non-expired record cache entry.
+A typical value is 10. If the value is zero, this functionality is disabled.
+
.. _setting-reuseport:
``reuseport``
--- /dev/null
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include "rec-taskqueue.hh"
+#include "taskqueue.hh"
+#include "syncres.hh"
+
+static thread_local pdns::TaskQueue t_taskQueue;
+
+void runTaskOnce(bool logErrors)
+{
+ t_taskQueue.runOnce(logErrors);
+}
+
+void pushTask(const DNSName& qname, uint16_t qtype, time_t deadline)
+{
+ t_taskQueue.push({qname, qtype, deadline, true});
+}
+
+uint64_t getTaskPushes()
+{
+ return broadcastAccFunction<uint64_t>([] { return t_taskQueue.getPushes(); });
+}
+
+uint64_t getTaskExpired()
+{
+ return broadcastAccFunction<uint64_t>([] { return t_taskQueue.getExpired(); });
+}
+
+uint64_t getTaskSize()
+{
+ return broadcastAccFunction<uint64_t>([] { return t_taskQueue.getSize(); });
+}
--- /dev/null
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#pragma once
+
+#include "dnsname.hh"
+
+void runTaskOnce(bool logErrors);
+void pushTask(const DNSName& qname, uint16_t qtype, time_t deadline);
+uint64_t getTaskPushes();
+uint64_t getTaskExpired();
+uint64_t getTaskSize();
--- /dev/null
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "taskqueue.hh"
+
+#include "logger.hh"
+#include "syncres.hh"
+
+namespace pdns
+{
+
+bool TaskQueue::empty() const
+{
+ return d_queue.empty();
+}
+
+size_t TaskQueue::size() const
+{
+ return d_queue.size();
+}
+
+void TaskQueue::push(const ResolveTask&& task)
+{
+ // Insertion fails if it's already there, no problem since we're already scheduled
+ // and the deadline would remain the same anyway.
+ auto result = d_queue.insert(std::move(task));
+ if (result.second) {
+ d_pushes++;
+ }
+}
+
+ResolveTask TaskQueue::pop()
+{
+ ResolveTask ret = d_queue.get<SequencedTag>().front();
+ d_queue.get<SequencedTag>().pop_front();
+ return ret;
+}
+
+bool TaskQueue::runOnce(bool logErrors)
+{
+ if (d_queue.empty()) {
+ return false;
+ }
+ ResolveTask task = pop();
+ struct timeval now;
+ gettimeofday(&now, 0);
+ if (task.d_deadline >= now.tv_sec) {
+ SyncRes sr(now);
+ vector<DNSRecord> ret;
+ sr.setRefreshAlmostExpired(task.d_refreshMode);
+ try {
+ g_log << Logger::Debug << "TaskQueue: resolving " << task.d_qname.toString() << '|' << QType(task.d_qtype).getName() << endl;
+ sr.beginResolve(task.d_qname, QType(task.d_qtype), QClass::IN, ret);
+ }
+ catch (const std::exception& e) {
+ g_log << Logger::Error << "Exception while running the background task queue: " << e.what() << endl;
+ }
+ catch (const PDNSException& e) {
+ g_log << Logger::Notice << "Exception while running the background task queue: " << e.reason << endl;
+ }
+ catch (const ImmediateServFailException& e) {
+ if (logErrors) {
+ g_log << Logger::Notice << "Exception while running the background task queue: " << e.reason << endl;
+ }
+ }
+ catch (const PolicyHitException& e) {
+ if (logErrors) {
+ g_log << Logger::Notice << "Policy hit while running the background task queue" << endl;
+ }
+ }
+ catch (...) {
+ g_log << Logger::Error << "Exception while running the background task queue" << endl;
+ }
+ }
+ else {
+ // Deadline passed
+ g_log << Logger::Debug << "TaskQueue: deadline for " << task.d_qname.toString() << '|' << QType(task.d_qtype).getName() << " passed" << endl;
+ d_expired++;
+ }
+ return true;
+}
+
+void TaskQueue::runAll(bool logErrors)
+{
+ while (runOnce(logErrors)) {
+ /* empty */
+ }
+}
+
+uint64_t* TaskQueue::getPushes() const
+{
+ return new uint64_t(d_pushes);
+}
+
+uint64_t* TaskQueue::getExpired() const
+{
+ return new uint64_t(d_expired);
+}
+
+uint64_t* TaskQueue::getSize() const
+{
+ return new uint64_t(size());
+}
+
+} /* namespace pdns */
--- /dev/null
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#pragma once
+
+#include <thread>
+
+#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/hashed_index.hpp>
+#include <boost/multi_index/key_extractors.hpp>
+#include <boost/multi_index/member.hpp>
+#include <boost/multi_index/sequenced_index.hpp>
+#include <boost/multi_index/tag.hpp>
+#include <boost/tuple/tuple_comparison.hpp>
+
+#include "dnsname.hh"
+#include "qtype.hh"
+
+using namespace ::boost::multi_index;
+
+namespace pdns
+{
+
+struct ResolveTask
+{
+ DNSName d_qname;
+ uint16_t d_qtype;
+ time_t d_deadline;
+ bool d_refreshMode; // Whether to run this task in regular mode (false) or in the mode that refreshes almost expired tasks
+};
+
+struct HashTag
+{
+};
+struct SequencedTag
+{
+};
+
+typedef multi_index_container<
+ ResolveTask,
+ indexed_by<
+ hashed_unique<tag<HashTag>,
+ composite_key<ResolveTask,
+ member<ResolveTask, DNSName, &ResolveTask::d_qname>,
+ member<ResolveTask, uint16_t, &ResolveTask::d_qtype>,
+ member<ResolveTask, bool, &ResolveTask::d_refreshMode>>>,
+ sequenced<tag<SequencedTag>>>>
+ queue_t;
+
+class TaskQueue
+{
+public:
+ bool empty() const;
+ size_t size() const;
+ void push(const ResolveTask&& task);
+ ResolveTask pop();
+ bool runOnce(bool logErrors); // Run one task if the queue is not empty
+ void runAll(bool logErrors);
+ uint64_t* getPushes() const;
+ uint64_t* getExpired() const;
+ uint64_t* getSize() const;
+
+private:
+ queue_t d_queue;
+ uint64_t d_pushes{0};
+ uint64_t d_expired{0};
+};
+
+}
int64_t expected = counter - delcounter;
for (; delcounter < counter; ++delcounter) {
- if (MRC.get(now, DNSName("hello ") + DNSName(std::to_string(delcounter)), QType(QType::A), false, &retrieved, who) > 0) {
+ if (MRC.get(now, DNSName("hello ") + DNSName(std::to_string(delcounter)), QType(QType::A), false, &retrieved, who, false) > 0) {
matches++;
BOOST_REQUIRE_EQUAL(retrieved.size(), records.size());
BOOST_CHECK_EQUAL(getRR<AAAARecordContent>(retrieved.at(0))->getCA().toString(), dr0Content.toString());
BOOST_CHECK_EQUAL(MRC.size(), 1U);
vState retrievedState = vState::Indeterminate;
bool wasAuth = false;
- BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1"), boost::none, nullptr, nullptr, nullptr, &retrievedState, &wasAuth), (ttd - now));
+ BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1"), false, boost::none, nullptr, nullptr, nullptr, &retrievedState, &wasAuth), (ttd - now));
BOOST_CHECK_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(vStateToString(retrievedState), vStateToString(vState::Secure));
BOOST_CHECK_EQUAL(wasAuth, true);
BOOST_CHECK_EQUAL(MRC.size(), 1U);
retrievedState = vState::Indeterminate;
wasAuth = false;
- BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1"), boost::none, nullptr, nullptr, nullptr, &retrievedState, &wasAuth), (ttd - now));
+ BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1"), false, boost::none, nullptr, nullptr, nullptr, &retrievedState, &wasAuth), (ttd - now));
BOOST_CHECK_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(vStateToString(retrievedState), vStateToString(vState::Secure));
BOOST_CHECK_EQUAL(wasAuth, true);
/* the remaining entry should be power2, but to get it
we need to go back in the past a bit */
- BOOST_CHECK_EQUAL(MRC.get(ttd - 1, power2, QType(dr2.d_type), false, &retrieved, who, boost::none, nullptr), 1);
+ BOOST_CHECK_EQUAL(MRC.get(ttd - 1, power2, QType(dr2.d_type), false, &retrieved, who, 0, boost::none, nullptr), 1);
BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(getRR<AAAARecordContent>(retrieved.at(0))->getCA().toString(), dr2Content.toString());
/* check that power1 is gone */
- BOOST_CHECK_EQUAL(MRC.get(ttd - 1, power1, QType(dr1.d_type), false, &retrieved, who, boost::none, nullptr), -1);
+ BOOST_CHECK_EQUAL(MRC.get(ttd - 1, power1, QType(dr1.d_type), false, &retrieved, who, 0, boost::none, nullptr), -1);
/* clear everything up */
MRC.doWipeCache(DNSName("."), true);
BOOST_CHECK_EQUAL(MRC.size(), 2U);
/* trigger a miss (expired) for power2 */
- BOOST_CHECK_EQUAL(MRC.get(now, power2, QType(dr2.d_type), false, &retrieved, who, boost::none, nullptr), -now);
+ BOOST_CHECK_EQUAL(MRC.get(now, power2, QType(dr2.d_type), false, &retrieved, who, 0, boost::none, nullptr), -now);
/* power2 should have been moved to the front of the expunge
queue, and should this time be removed first */
/* the remaining entry should be power1, but to get it
we need to go back in the past a bit */
- BOOST_CHECK_EQUAL(MRC.get(ttd - 1, power1, QType(dr1.d_type), false, &retrieved, who, boost::none, nullptr), 1);
+ BOOST_CHECK_EQUAL(MRC.get(ttd - 1, power1, QType(dr1.d_type), false, &retrieved, who, 0, boost::none, nullptr), 1);
BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(getRR<AAAARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
/* check that power2 is gone */
- BOOST_CHECK_EQUAL(MRC.get(ttd - 1, power2, QType(dr2.d_type), false, &retrieved, who, boost::none, nullptr), -1);
+ BOOST_CHECK_EQUAL(MRC.get(ttd - 1, power2, QType(dr2.d_type), false, &retrieved, who, 0, boost::none, nullptr), -1);
}
BOOST_AUTO_TEST_CASE(test_RecursorCache_ExpungingValidEntries)
BOOST_CHECK_EQUAL(MRC.size(), 1U);
/* the remaining entry should be power2 */
- BOOST_CHECK_EQUAL(MRC.get(now, power2, QType(dr2.d_type), false, &retrieved, who, boost::none, nullptr), ttd - now);
+ BOOST_CHECK_EQUAL(MRC.get(now, power2, QType(dr2.d_type), false, &retrieved, who, 0, boost::none, nullptr), ttd - now);
BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(getRR<AAAARecordContent>(retrieved.at(0))->getCA().toString(), dr2Content.toString());
/* check that power1 is gone */
- BOOST_CHECK_EQUAL(MRC.get(now, power1, QType(dr1.d_type), false, &retrieved, who, boost::none, nullptr), -1);
+ BOOST_CHECK_EQUAL(MRC.get(now, power1, QType(dr1.d_type), false, &retrieved, who, 0, boost::none, nullptr), -1);
/* clear everything up */
MRC.doWipeCache(DNSName("."), true);
BOOST_CHECK_EQUAL(MRC.size(), 1U);
/* the remaining entry should be power1 */
- BOOST_CHECK_EQUAL(MRC.get(now, power1, QType(dr1.d_type), false, &retrieved, who, boost::none, nullptr), ttd - now);
+ BOOST_CHECK_EQUAL(MRC.get(now, power1, QType(dr1.d_type), false, &retrieved, who, 0, boost::none, nullptr), ttd - now);
BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(getRR<AAAARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
/* check that power2 is gone */
- BOOST_CHECK_EQUAL(MRC.get(now, power2, QType(dr2.d_type), false, &retrieved, who, boost::none, nullptr), -1);
+ BOOST_CHECK_EQUAL(MRC.get(now, power2, QType(dr2.d_type), false, &retrieved, who, 0, boost::none, nullptr), -1);
/* clear everything up */
MRC.doWipeCache(DNSName("."), true);
BOOST_CHECK_EQUAL(MRC.size(), 2U);
/* get a hit for power1 */
- BOOST_CHECK_EQUAL(MRC.get(now, power1, QType(dr1.d_type), false, &retrieved, who, boost::none, nullptr), ttd - now);
+ BOOST_CHECK_EQUAL(MRC.get(now, power1, QType(dr1.d_type), false, &retrieved, who, 0, boost::none, nullptr), ttd - now);
BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(getRR<AAAARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
BOOST_CHECK_EQUAL(MRC.size(), 1U);
/* the remaining entry should be power1 */
- BOOST_CHECK_EQUAL(MRC.get(now, power1, QType(dr1.d_type), false, &retrieved, who, boost::none, nullptr), ttd - now);
+ BOOST_CHECK_EQUAL(MRC.get(now, power1, QType(dr1.d_type), false, &retrieved, who, 0, boost::none, nullptr), ttd - now);
BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(getRR<AAAARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
/* check that power2 is gone */
- BOOST_CHECK_EQUAL(MRC.get(now, power2, QType(dr2.d_type), false, &retrieved, who, boost::none, nullptr), -1);
+ BOOST_CHECK_EQUAL(MRC.get(now, power2, QType(dr2.d_type), false, &retrieved, who, 0, boost::none, nullptr), -1);
MRC.doPrune(0);
BOOST_CHECK_EQUAL(MRC.size(), 0U);
for (size_t i = 0; i <= 255; i++) {
ComboAddress whoLoop("192.0.2." + std::to_string(i));
- auto ret = MRC.get(now, power1, QType(QType::A), false, &retrieved, whoLoop);
+ auto ret = MRC.get(now, power1, QType(QType::A), false, &retrieved, whoLoop, 0);
if (ret > 0) {
BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), whoLoop.toString());
int64_t expected = counter;
for (counter = 0; counter < 110; counter++) {
- if (MRC.get(now, DNSName("hello ") + DNSName(std::to_string(counter)), QType(QType::A), false, &retrieved, nobody, boost::none) > 0) {
+ if (MRC.get(now, DNSName("hello ") + DNSName(std::to_string(counter)), QType(QType::A), false, &retrieved, nobody, 0, boost::none) > 0) {
matches++;
BOOST_CHECK_EQUAL(retrieved.size(), rset0.size());
BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr0Content.toString());
matches = 0;
for (counter = 0; counter < 110; ++counter) {
- if (MRC.get(now, DNSName("hello ") + DNSName(std::to_string(counter)), QType(QType::A), false, &retrieved, who, string("mytagB")) > 0) {
+ if (MRC.get(now, DNSName("hello ") + DNSName(std::to_string(counter)), QType(QType::A), false, &retrieved, who, 0, string("mytagB")) > 0) {
matches++;
BOOST_CHECK_EQUAL(retrieved.size(), rset0.size());
BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr0Content.toString());
matches = 0;
for (counter = 0; counter < 110; counter++) {
- if (MRC.get(now, DNSName("hello ") + DNSName(std::to_string(counter)), QType(QType::A), false, &retrieved, who, string("mytagX")) > 0) {
+ if (MRC.get(now, DNSName("hello ") + DNSName(std::to_string(counter)), QType(QType::A), false, &retrieved, who, 0, string("mytagX")) > 0) {
matches++;
BOOST_CHECK_EQUAL(retrieved.size(), rset0.size());
BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr0Content.toString());
matches = 0;
for (counter = 0; counter < 110; counter++) {
- if (MRC.get(now, DNSName("hello ") + DNSName(std::to_string(counter)), QType(QType::A), false, &retrieved, nobody, boost::none) > 0) {
+ if (MRC.get(now, DNSName("hello ") + DNSName(std::to_string(counter)), QType(QType::A), false, &retrieved, nobody, 0, boost::none) > 0) {
matches++;
BOOST_CHECK_EQUAL(retrieved.size(), rset0.size());
BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr0Content.toString());
matches = 0;
for (counter = 0; counter < 110; ++counter) {
- if (MRC.get(now, DNSName("hello ") + DNSName(std::to_string(counter)), QType(QType::A), false, &retrieved, nobody, string("mytagA")) > 0) {
+ if (MRC.get(now, DNSName("hello ") + DNSName(std::to_string(counter)), QType(QType::A), false, &retrieved, nobody, 0, string("mytagA")) > 0) {
matches++;
if (counter < 50) {
BOOST_CHECK_EQUAL(retrieved.size(), rset0tagged.size());
matches = 0;
for (counter = 0; counter < 110; counter++) {
- if (MRC.get(now, DNSName("hello ") + DNSName(std::to_string(counter)), QType(QType::A), false, &retrieved, nobody, string("mytagX")) > 0) {
+ if (MRC.get(now, DNSName("hello ") + DNSName(std::to_string(counter)), QType(QType::A), false, &retrieved, nobody, 0, string("mytagX")) > 0) {
matches++;
BOOST_CHECK_EQUAL(retrieved.size(), rset0.size());
BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr0Content.toString());
BOOST_CHECK_EQUAL(MRC.size(), 1U);
// tagged specific should be returned for a matching tag
- BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.2"), string("mytag")), (ttd - now));
+ BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.2"), 0, string("mytag")), (ttd - now));
BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
// tag specific should not be returned for a different tag
- BOOST_CHECK_LT(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.2"), string("othertag")), 0);
+ BOOST_CHECK_LT(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.2"), 0, string("othertag")), 0);
BOOST_CHECK_EQUAL(retrieved.size(), 0U);
// insert a new entry without tag
BOOST_CHECK_EQUAL(MRC.size(), 2U);
// tagged specific should be returned for a matching tag
- BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.2"), string("mytag")), (ttd - now));
+ BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.2"), 0, string("mytag")), (ttd - now));
BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
// if no tag given nothing should be retrieved if address doesn't match
- BOOST_CHECK_LT(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1"), boost::none), 0);
+ BOOST_CHECK_LT(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1"), 0, boost::none), 0);
BOOST_REQUIRE_EQUAL(retrieved.size(), 0U);
// if no tag given and no-non-tagged entries matches nothing should be returned
- BOOST_CHECK_LT(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.2"), boost::none), 0);
+ BOOST_CHECK_LT(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.2"), 0, boost::none), 0);
BOOST_REQUIRE_EQUAL(retrieved.size(), 0U);
// Insert untagged entry with no netmask
BOOST_CHECK_EQUAL(MRC.size(), 3U);
// Retrieval with no address and no tag should get that one
- BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress(), boost::none), (ttd - now));
+ BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress(), 0, boost::none), (ttd - now));
BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr3Content.toString());
// If no tag given match non-tagged entry
- BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.2"), boost::none), (ttd - now));
+ BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.2"), 0, boost::none), (ttd - now));
BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr3Content.toString());
// If no tag given we should be able to retrieve the netmask specific record
- BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.3.1"), boost::none), (ttd - now));
+ BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.3.1"), 0, boost::none), (ttd - now));
BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr2Content.toString());
// tagged specific should still be returned for a matching tag, address is not used
- BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.2"), string("mytag")), (ttd - now));
+ BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.2"), 0, string("mytag")), (ttd - now));
BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
#include "base32.hh"
#include "lua-recursor4.hh"
#include "root-dnssec.hh"
+#include "rec-taskqueue.hh"
#include "test-syncres_cc.hh"
RecursorStats g_stats;
}
return LWResult::Result::Timeout;
}
+
+void pushTask(const DNSName& qname, uint16_t qtype, time_t deadline)
+{
+}
const ComboAddress who;
vector<DNSRecord> cached;
vector<std::shared_ptr<RRSIGRecordContent>> signatures;
- BOOST_REQUIRE_EQUAL(g_recCache->get(now, target, QType(QType::A), false, &cached, who, boost::none, &signatures), -1);
+ BOOST_REQUIRE_EQUAL(g_recCache->get(now, target, QType(QType::A), false, &cached, who, 0, boost::none, &signatures), -1);
}
BOOST_AUTO_TEST_CASE(test_special_types)
BOOST_CHECK_EQUAL(queriesCount, 11U);
/* now we remove the denial of powerdns.com DS from the cache and ask www2 */
- BOOST_REQUIRE_EQUAL(g_negCache->wipe(target, false), 1);
+ BOOST_REQUIRE_EQUAL(g_negCache->wipe(target, false), 1U);
ret.clear();
res = sr->beginResolve(DNSName("www2.powerdns.com."), QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(queriesCount, 10U);
/* now we remove the denial of powerdns.com DS from the cache and ask www2 */
- BOOST_REQUIRE_EQUAL(g_negCache->wipe(target, false), 1);
+ BOOST_REQUIRE_EQUAL(g_negCache->wipe(target, false), 1U);
ret.clear();
res = sr->beginResolve(DNSName("www2.powerdns.com."), QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
const ComboAddress who;
vector<DNSRecord> cached;
vector<std::shared_ptr<RRSIGRecordContent>> signatures;
- BOOST_REQUIRE_EQUAL(g_recCache->get(tnow, target, QType(QType::A), true, &cached, who, boost::none, &signatures), 1);
+ BOOST_REQUIRE_EQUAL(g_recCache->get(tnow, target, QType(QType::A), true, &cached, who, 0, boost::none, &signatures), 1);
BOOST_REQUIRE_EQUAL(cached.size(), 1U);
BOOST_REQUIRE_EQUAL(signatures.size(), 1U);
BOOST_CHECK_EQUAL((cached[0].d_ttl - tnow), 1);
vector<DNSRecord> cached;
bool wasAuth = false;
- auto ttl = g_recCache->get(now, DNSName("powerdns.com."), QType(QType::NS), false, &cached, who, boost::none, nullptr, nullptr, nullptr, nullptr, &wasAuth);
+ auto ttl = g_recCache->get(now, DNSName("powerdns.com."), QType(QType::NS), false, &cached, who, 0, boost::none, nullptr, nullptr, nullptr, nullptr, &wasAuth);
BOOST_REQUIRE_GE(ttl, 1);
BOOST_REQUIRE_LE(ttl, 42);
BOOST_CHECK_EQUAL(cached.size(), 1U);
cached.clear();
/* Also check that the the part in additional is still not auth */
- BOOST_REQUIRE_GE(g_recCache->get(now, DNSName("a.gtld-servers.net."), QType(QType::A), false, &cached, who, boost::none, nullptr, nullptr, nullptr, nullptr, &wasAuth), -1);
+ BOOST_REQUIRE_GE(g_recCache->get(now, DNSName("a.gtld-servers.net."), QType(QType::A), false, &cached, who, 0, boost::none, nullptr, nullptr, nullptr, nullptr, &wasAuth), -1);
BOOST_CHECK_EQUAL(cached.size(), 1U);
BOOST_CHECK_EQUAL(wasAuth, false);
}
vector<DNSRecord> cached;
bool wasAuth = false;
vState retrievedState = vState::Insecure;
- BOOST_CHECK_GT(g_recCache->get(now, DNSName("powerdns.com."), QType(QType::SOA), true, &cached, who, boost::none, nullptr, nullptr, nullptr, &retrievedState, &wasAuth), 0);
+ BOOST_CHECK_GT(g_recCache->get(now, DNSName("powerdns.com."), QType(QType::SOA), true, &cached, who, false, boost::none, nullptr, nullptr, nullptr, &retrievedState, &wasAuth), 0);
BOOST_CHECK_EQUAL(vStateToString(retrievedState), vStateToString(vState::Secure));
BOOST_CHECK_EQUAL(wasAuth, true);
BOOST_REQUIRE_EQUAL(ret.size(), 2U);
cached.clear();
- BOOST_CHECK_GT(g_recCache->get(now, DNSName("powerdns.com."), QType(QType::SOA), true, &cached, who, boost::none, nullptr, nullptr, nullptr, &retrievedState, &wasAuth), 0);
+ BOOST_CHECK_GT(g_recCache->get(now, DNSName("powerdns.com."), QType(QType::SOA), true, &cached, who, false, boost::none, nullptr, nullptr, nullptr, &retrievedState, &wasAuth), 0);
BOOST_CHECK_EQUAL(vStateToString(retrievedState), vStateToString(vState::Secure));
BOOST_CHECK_EQUAL(wasAuth, true);
}
bool SyncRes::s_noEDNS;
bool SyncRes::s_qnameminimization;
SyncRes::HardenNXD SyncRes::s_hardenNXD;
+unsigned int SyncRes::s_refresh_ttlperc;
#define LOG(x) if(d_lm == Log) { g_log <<Logger::Warning << x; } else if(d_lm == Store) { d_trace << x; }
// We have some IPv4 records, don't bother with going out to get IPv6, but do consult the cache
// Once IPv6 adoption matters, this needs to be revisited
res_t cset;
- if (g_recCache->get(d_now.tv_sec, qname, QType(QType::AAAA), false, &cset, d_cacheRemote, d_routingTag) > 0) {
+ if (g_recCache->get(d_now.tv_sec, qname, QType(QType::AAAA), false, &cset, d_cacheRemote, d_refresh, d_routingTag) > 0) {
for (const auto &i : cset) {
if (i.d_ttl > (unsigned int)d_now.tv_sec ) {
if (auto rec = getRR<AAAARecordContent>(i)) {
vector<DNSRecord> ns;
*flawedNSSet = false;
- if(g_recCache->get(d_now.tv_sec, subdomain, QType(QType::NS), false, &ns, d_cacheRemote, d_routingTag) > 0) {
+ if(g_recCache->get(d_now.tv_sec, subdomain, QType(QType::NS), false, &ns, d_cacheRemote, d_refresh, d_routingTag) > 0) {
bestns.reserve(ns.size());
for(auto k=ns.cbegin();k!=ns.cend(); ++k) {
const DNSRecord& dr=*k;
auto nrr = getRR<NSRecordContent>(dr);
if(nrr && (!nrr->getNS().isPartOf(subdomain) || g_recCache->get(d_now.tv_sec, nrr->getNS(), nsqt,
- false, doLog() ? &aset : 0, d_cacheRemote, d_routingTag) > 5)) {
+ false, doLog() ? &aset : 0, d_cacheRemote, d_refresh, d_routingTag) > 5)) {
bestns.push_back(dr);
LOG(prefix<<qname<<": NS (with ip, or non-glue) in cache for '"<<subdomain<<"' -> '"<<nrr->getNS()<<"'"<<endl);
LOG(prefix<<qname<<": within bailiwick: "<< nrr->getNS().isPartOf(subdomain));
LOG(prefix<<qname<<": Looking for CNAME cache hit of '"<<qname<<"|CNAME"<<"'"<<endl);
/* we don't require auth data for forward-recurse lookups */
- if (g_recCache->get(d_now.tv_sec, qname, QType(QType::CNAME), !wasForwardRecurse && d_requireAuthData, &cset, d_cacheRemote, d_routingTag, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &state, &wasAuth, &authZone) > 0) {
+ if (g_recCache->get(d_now.tv_sec, qname, QType(QType::CNAME), !wasForwardRecurse && d_requireAuthData, &cset, d_cacheRemote, d_refresh, d_routingTag, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &state, &wasAuth, &authZone) > 0) {
foundName = qname;
foundQT = QType(QType::CNAME);
}
if (dnameName == qname && qtype != QType::DNAME) { // The client does not want a DNAME, but we've reached the QNAME already. So there is no match
break;
}
- if (g_recCache->get(d_now.tv_sec, dnameName, QType(QType::DNAME), !wasForwardRecurse && d_requireAuthData, &cset, d_cacheRemote, d_routingTag, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &state, &wasAuth, &authZone) > 0) {
+ if (g_recCache->get(d_now.tv_sec, dnameName, QType(QType::DNAME), !wasForwardRecurse && d_requireAuthData, &cset, d_cacheRemote, d_refresh, d_routingTag, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &state, &wasAuth, &authZone) > 0) {
foundName = dnameName;
foundQT = QType(QType::DNAME);
break;
uint32_t capTTL = std::numeric_limits<uint32_t>::max();
bool wasCachedAuth;
- if(g_recCache->get(d_now.tv_sec, sqname, sqt, !wasForwardRecurse && d_requireAuthData, &cset, d_cacheRemote, d_routingTag, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &cachedState, &wasCachedAuth) > 0) {
+ if(g_recCache->get(d_now.tv_sec, sqname, sqt, !wasForwardRecurse && d_requireAuthData, &cset, d_cacheRemote, d_refresh, d_routingTag, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &cachedState, &wasCachedAuth) > 0) {
LOG(prefix<<sqname<<": Found cache hit for "<<sqt.getName()<<": ");
return old;
}
+ bool setRefreshAlmostExpired(bool doit)
+ {
+ auto old = d_refresh;
+ d_refresh = doit;
+ return old;
+ }
+
void setQNameMinimization(bool state=true)
{
d_qNameMinimization=state;
static bool s_nopacketcache;
static bool s_qnameminimization;
static HardenNXD s_hardenNXD;
+ static unsigned int s_refresh_ttlperc;
std::unordered_map<std::string,bool> d_discardedPolicies;
DNSFilterEngine::Policy d_appliedPolicy;
bool d_qNameMinimization{false};
bool d_queryReceivedOverTCP{false};
bool d_followCNAME{true};
+ bool d_refresh{false};
LogMode d_lm;
};
<measurement><name>system CPU seconds</name><value>%S</value></measurement>
<measurement><name>wallclock seconds</name><value>%e</value></measurement>
<measurement><name>%% CPU used</name><value>%P</value></measurement>
-' ${RECURSOR} --daemon=no --local-port=$port --socket-dir=./ --trace=$TRACE --config-dir=. --max-mthreads=$mthreads --query-local-address="0.0.0.0${QLA6}" --threads=$threads --record-cache-shards=$shards --disable-packetcache > recursor.log 2>&1 &
+' ${RECURSOR} --daemon=no --local-port=$port --socket-dir=./ --trace=$TRACE --config-dir=. --max-mthreads=$mthreads --query-local-address="0.0.0.0${QLA6}" --threads=$threads --record-cache-shards=$shards --disable-packetcache --refresh-on-ttl-perc=10 > recursor.log 2>&1 &
sleep 3
# warm up the cache