From: Otto Date: Wed, 17 Feb 2021 09:25:49 +0000 (+0100) Subject: Remember if an ns name did not resolve for a while and skip those. X-Git-Tag: dnsdist-1.6.0-alpha2~22^2~3 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=e329b582f6d44f555d0beeb92d7435ff2eeaa6e0;p=thirdparty%2Fpdns.git Remember if an ns name did not resolve for a while and skip those. --- diff --git a/pdns/pdns_recursor.cc b/pdns/pdns_recursor.cc index 595892a680..7fd6d877ae 100644 --- a/pdns/pdns_recursor.cc +++ b/pdns/pdns_recursor.cc @@ -3503,6 +3503,7 @@ static void houseKeeping(void *) limit = now.tv_sec - 2*3600; SyncRes::pruneEDNSStatuses(limit); SyncRes::pruneThrottledServers(); + SyncRes::pruneNonResolving(now.tv_sec - SyncRes::s_nonresolvingnsthrottletime); Utility::gettimeofday(&last_prune, nullptr); } @@ -4600,6 +4601,8 @@ static int serviceMain(int argc, char*argv[]) 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"); @@ -5365,6 +5368,9 @@ int main(int argc, char **argv) ::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"; diff --git a/pdns/rec_channel_rec.cc b/pdns/rec_channel_rec.cc index 7b16c21eab..fd358fd7da 100644 --- a/pdns/rec_channel_rec.cc +++ b/pdns/rec_channel_rec.cc @@ -325,6 +325,11 @@ static uint64_t* pleaseDumpFailedServers(int fd) 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) { @@ -1692,6 +1697,7 @@ RecursorControlChannel::Answer RecursorControlParser::getAnswer(int s, const str "clear-ta [DOMAIN]... Clear the Trust Anchor for DOMAINs\n" "dump-cache dump cache contents to the named file\n" "dump-edns [status] dump EDNS status to the named file\n" +"dump-nonresolving dump non-resolving nameservers to the named file\n" "dump-nsspeeds dump nsspeeds statistics to the named file\n" "dump-rpz dump the content of a RPZ zone to the named file\n" "dump-throttlemap dump the contents of the throttle map to the named file\n" @@ -1774,6 +1780,9 @@ RecursorControlChannel::Answer RecursorControlParser::getAnswer(int s, const str 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)}; } diff --git a/pdns/rec_control.cc b/pdns/rec_control.cc index 942058abf1..b3ef73e9a4 100644 --- a/pdns/rec_control.cc +++ b/pdns/rec_control.cc @@ -90,7 +90,8 @@ int main(int argc, char** argv) "dump-nsspeeds", "dump-failedservers", "dump-rpz", - "dump-throttlemap" + "dump-throttlemap", + "dump-nonresolving" }; try { initArguments(argc, argv); diff --git a/pdns/syncres.cc b/pdns/syncres.cc index b9b12c218e..7ea387932a 100644 --- a/pdns/syncres.cc +++ b/pdns/syncres.cc @@ -62,6 +62,8 @@ unsigned int SyncRes::s_packetcachettl; 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 SyncRes::s_authzonequeries; std::atomic SyncRes::s_queries; @@ -516,8 +518,33 @@ uint64_t SyncRes::doDumpFailedServers(int fd) count++; char tmp[26]; ctime_r(&i.last, tmp); - fprintf(fp.get(), "%s\t%lld\t%s", i.address.toString().c_str(), - static_cast(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(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; @@ -1133,27 +1160,27 @@ vector SyncRes::getAddrs(const DNSName &qname, unsigned int depth, 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 "< SyncRes::retrieveAddressesForNS(const std::string& prefix, { vector result; - if(!tns->first.empty()) { + + if (!tns->first.empty()) { + if (s_nonresolvingnsmaxfails > 0 && t_sstorage.nonresolving.value(tns->first) >= s_nonresolvingnsmaxfails) { + LOG(prefix<first<< " in nonresolving map, skipping"<first<< "' ("<<1+tns-rnameservers.begin()<<"/"<<(unsigned int)rnameservers.size()<<")"<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 { diff --git a/pdns/syncres.hh b/pdns/syncres.hh index e8da3dbba6..5616bc101c 100644 --- a/pdns/syncres.hh +++ b/pdns/syncres.hh @@ -186,27 +186,28 @@ private: float d_val{0}; }; +template 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, member>, + ordered_unique, member>, ordered_non_unique, member> >> 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); @@ -216,20 +217,20 @@ public: 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::max()) { i->value++; } - auto &ind = d_cont.get(); + auto &ind = d_cont.template get(); 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); } @@ -245,7 +246,7 @@ public: } void prune(time_t cutoff) { - auto &ind = d_cont.get(); + auto &ind = d_cont.template get(); ind.erase(ind.begin(), ind.upper_bound(cutoff)); } @@ -403,7 +404,8 @@ public: nsspeeds_t nsSpeeds; throttle_t throttle; ednsstatus_t ednsstatus; - fails_t fails; + fails_t fails; + fails_t nonresolving; std::shared_ptr domainmap; }; @@ -415,6 +417,7 @@ public: 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() { @@ -555,6 +558,10 @@ public: { return t_sstorage.fails.value(server); } + static void pruneNonResolving(time_t cutoff) + { + t_sstorage.nonresolving.prune(cutoff); + } static void setDomainMap(std::shared_ptr newMap) { t_sstorage.domainmap = newMap; @@ -754,6 +761,9 @@ public: 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;