limit = now.tv_sec - 2*3600;
SyncRes::pruneEDNSStatuses(limit);
SyncRes::pruneThrottledServers();
+ SyncRes::pruneNonResolving(now.tv_sec - SyncRes::s_nonresolvingnsthrottletime);
Utility::gettimeofday(&last_prune, nullptr);
}
SyncRes::s_packetcacheservfailttl=(packetCacheServFailTTL > SyncRes::s_packetcachettl) ? SyncRes::s_packetcachettl : packetCacheServFailTTL;
SyncRes::s_serverdownmaxfails=::arg().asNum("server-down-max-fails");
SyncRes::s_serverdownthrottletime=::arg().asNum("server-down-throttle-time");
+ SyncRes::s_nonresolvingnsmaxfails=::arg().asNum("non-resolving-ns-max-fails");
+ SyncRes::s_nonresolvingnsthrottletime=::arg().asNum("non-resolving-ns-throttle-time");
SyncRes::s_serverID=::arg()["server-id"];
SyncRes::s_maxqperq=::arg().asNum("max-qperq");
SyncRes::s_maxnsaddressqperq=::arg().asNum("max-ns-address-qperq");
::arg().set("server-down-throttle-time","Number of seconds to throttle all queries to a server after being marked as down")="60";
::arg().set("dont-throttle-names", "Do not throttle nameservers with this name or suffix")="";
::arg().set("dont-throttle-netmasks", "Do not throttle nameservers with this IP netmask")="";
+ ::arg().set("non-resolving-ns-max-fails", "Number of failed address resolves of a nameserver to start throttling it, 0 is disabled")="1";
+ ::arg().set("non-resolving-ns-throttle-time", "Number of seconds the throttle a namserver failing to resolve")="60";
+
::arg().set("hint-file", "If set, load root hints from this file")="";
::arg().set("max-cache-entries", "If set, maximum number of entries in the main cache")="1000000";
::arg().set("max-negative-ttl", "maximum number of seconds to keep a negative cached entry in memory")="3600";
return new uint64_t(SyncRes::doDumpFailedServers(fd));
}
+static uint64_t* pleaseDumpNonResolvingNS(int fd)
+{
+ return new uint64_t(SyncRes::doDumpNonResolvingNS(fd));
+}
+
// Generic dump to file command
static RecursorControlChannel::Answer doDumpToFile(int s, uint64_t* (*function)(int s), const string& name)
{
"clear-ta [DOMAIN]... Clear the Trust Anchor for DOMAINs\n"
"dump-cache <filename> dump cache contents to the named file\n"
"dump-edns [status] <filename> dump EDNS status to the named file\n"
+"dump-nonresolving <filename> dump non-resolving nameservers to the named file\n"
"dump-nsspeeds <filename> dump nsspeeds statistics to the named file\n"
"dump-rpz <zone name> <filename> dump the content of a RPZ zone to the named file\n"
"dump-throttlemap <filename> dump the contents of the throttle map to the named file\n"
if (cmd == "dump-throttlemap") {
return doDumpToFile(s, pleaseDumpThrottleMap, cmd);
}
+ if (cmd == "dump-nonresolving") {
+ return doDumpToFile(s, pleaseDumpNonResolvingNS, cmd);
+ }
if (cmd == "wipe-cache" || cmd == "flushname") {
return {0, doWipeCache(begin, end, 0xffff)};
}
"dump-nsspeeds",
"dump-failedservers",
"dump-rpz",
- "dump-throttlemap"
+ "dump-throttlemap",
+ "dump-nonresolving"
};
try {
initArguments(argc, argv);
unsigned int SyncRes::s_packetcacheservfailttl;
unsigned int SyncRes::s_serverdownmaxfails;
unsigned int SyncRes::s_serverdownthrottletime;
+unsigned int SyncRes::s_nonresolvingnsmaxfails;
+unsigned int SyncRes::s_nonresolvingnsthrottletime;
unsigned int SyncRes::s_ecscachelimitttl;
std::atomic<uint64_t> SyncRes::s_authzonequeries;
std::atomic<uint64_t> SyncRes::s_queries;
count++;
char tmp[26];
ctime_r(&i.last, tmp);
- fprintf(fp.get(), "%s\t%lld\t%s", i.address.toString().c_str(),
- static_cast<long long>(i.value), tmp);
+ fprintf(fp.get(), "%s\t%llu\t%s", i.key.toString().c_str(), i.value, tmp);
+ }
+
+ return count;
+}
+
+uint64_t SyncRes::doDumpNonResolvingNS(int fd)
+{
+ int newfd = dup(fd);
+ if (newfd == -1) {
+ return 0;
+ }
+ auto fp = std::unique_ptr<FILE, int(*)(FILE*)>(fdopen(newfd, "w"), fclose);
+ if (!fp) {
+ close(newfd);
+ return 0;
+ }
+ fprintf(fp.get(), "; non resolving nameserver dump follows\n");
+ fprintf(fp.get(), "; name\tcount\ttimestamp\n");
+ uint64_t count=0;
+
+ for(const auto& i : t_sstorage.nonresolving.getMap())
+ {
+ count++;
+ char tmp[26];
+ ctime_r(&i.last, tmp);
+ fprintf(fp.get(), "%s\t%llu\t%s", i.key.toString().c_str(), i.value, tmp);
}
return count;
t_sstorage.nsSpeeds[qname].purge(speeds);
- if(ret.size() > 1) {
+ if (ret.size() > 1) {
shuffle(ret.begin(), ret.end(), pdns::dns_random_engine());
speedOrderCA so(speeds);
stable_sort(ret.begin(), ret.end(), so);
+ }
- if(doLog()) {
- string prefix=d_prefix;
- prefix.append(depth, ' ');
- LOG(prefix<<"Nameserver "<<qname<<" IPs: ");
- bool first = true;
- for(const auto& addr : ret) {
- if (first) {
- first = false;
- }
- else {
- LOG(", ");
- }
- LOG((addr.toString())<<"(" << (boost::format("%0.2f") % (speeds[addr]/1000.0)).str() <<"ms)");
+ if(doLog()) {
+ string prefix=d_prefix;
+ prefix.append(depth, ' ');
+ LOG(prefix<<"Nameserver "<<qname<<" IPs: ");
+ bool first = true;
+ for(const auto& addr : ret) {
+ if (first) {
+ first = false;
+ }
+ else {
+ LOG(", ");
}
- LOG(endl);
+ LOG((addr.toString())<<"(" << (boost::format("%0.2f") % (speeds[addr]/1000.0)).str() <<"ms)");
}
+ LOG(endl);
}
return ret;
{
vector<ComboAddress> result;
- if(!tns->first.empty()) {
+
+ if (!tns->first.empty()) {
+ if (s_nonresolvingnsmaxfails > 0 && t_sstorage.nonresolving.value(tns->first) >= s_nonresolvingnsmaxfails) {
+ LOG(prefix<<qname<<": NS "<<tns->first<< " in nonresolving map, skipping"<<endl);
+ return result;
+ }
+
LOG(prefix<<qname<<": Trying to resolve NS '"<<tns->first<< "' ("<<1+tns-rnameservers.begin()<<"/"<<(unsigned int)rnameservers.size()<<")"<<endl);
- result = getAddrs(tns->first, depth, beenthere, cacheOnly, retrieveAddressesForNS);
+ try {
+ result = getAddrs(tns->first, depth, beenthere, cacheOnly, retrieveAddressesForNS);
+ }
+ // Other exceptions should likely not throttle...
+ catch (const ImmediateServFailException& ex) {
+ if (s_nonresolvingnsmaxfails > 0) {
+ auto dontThrottleNames = g_dontThrottleNames.getLocal();
+ if (!dontThrottleNames->check(tns->first)) {
+ t_sstorage.nonresolving.incr(tns->first, d_now);
+ }
+ }
+ throw ex;
+ }
+ if (s_nonresolvingnsmaxfails > 0 && result.empty()) {
+ auto dontThrottleNames = g_dontThrottleNames.getLocal();
+ if (!dontThrottleNames->check(tns->first)) {
+ t_sstorage.nonresolving.incr(tns->first, d_now);
+ }
+ }
pierceDontQuery=false;
}
else {
float d_val{0};
};
+template<class T>
class fails_t : public boost::noncopyable
{
public:
- typedef unsigned long counter_t;
+ typedef unsigned long long counter_t;
struct value_t {
- value_t(const ComboAddress &a) : address(a) {}
- ComboAddress address;
+ value_t(const T &a) : key(a) {}
+ T key;
mutable counter_t value{0};
time_t last{0};
};
typedef multi_index_container<value_t,
indexed_by<
- ordered_unique<tag<ComboAddress>, member<value_t, ComboAddress, &value_t::address>>,
+ ordered_unique<tag<T>, member<value_t, T, &value_t::key>>,
ordered_non_unique<tag<time_t>, member<value_t, time_t, &value_t::last>>
>> cont_t;
cont_t getMap() const {
return d_cont;
}
- counter_t value(const ComboAddress& t) const
+ counter_t value(const T& t) const
{
auto i = d_cont.find(t);
return i->value;
}
- counter_t incr(const ComboAddress& address, const struct timeval& now)
+ counter_t incr(const T& key, const struct timeval& now)
{
- auto i = d_cont.insert(address).first;
+ auto i = d_cont.insert(key).first;
if (i->value < std::numeric_limits<counter_t>::max()) {
i->value++;
}
- auto &ind = d_cont.get<ComboAddress>();
+ auto &ind = d_cont.template get<T>();
time_t tm = now.tv_sec;
ind.modify(i, [tm](value_t &val) { val.last = tm; });
return i->value;
}
- void clear(const ComboAddress& a)
+ void clear(const T& a)
{
d_cont.erase(a);
}
}
void prune(time_t cutoff) {
- auto &ind = d_cont.get<time_t>();
+ auto &ind = d_cont.template get<time_t>();
ind.erase(ind.begin(), ind.upper_bound(cutoff));
}
nsspeeds_t nsSpeeds;
throttle_t throttle;
ednsstatus_t ednsstatus;
- fails_t fails;
+ fails_t<ComboAddress> fails;
+ fails_t<DNSName> nonresolving;
std::shared_ptr<domainmap_t> domainmap;
};
static uint64_t doDumpNSSpeeds(int fd);
static uint64_t doDumpThrottleMap(int fd);
static uint64_t doDumpFailedServers(int fd);
+ static uint64_t doDumpNonResolvingNS(int fd);
static int getRootNS(struct timeval now, asyncresolve_t asyncCallback, unsigned int depth);
static void clearDelegationOnly()
{
{
return t_sstorage.fails.value(server);
}
+ static void pruneNonResolving(time_t cutoff)
+ {
+ t_sstorage.nonresolving.prune(cutoff);
+ }
static void setDomainMap(std::shared_ptr<domainmap_t> newMap)
{
t_sstorage.domainmap = newMap;
static unsigned int s_packetcacheservfailttl;
static unsigned int s_serverdownmaxfails;
static unsigned int s_serverdownthrottletime;
+ static unsigned int s_nonresolvingnsmaxfails;
+ static unsigned int s_nonresolvingnsthrottletime;
+
static unsigned int s_ecscachelimitttl;
static uint8_t s_ecsipv4limit;
static uint8_t s_ecsipv6limit;