]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Remember if an ns name did not resolve for a while and skip those.
authorOtto <otto.moerbeek@open-xchange.com>
Wed, 17 Feb 2021 09:25:49 +0000 (10:25 +0100)
committerOtto <otto.moerbeek@open-xchange.com>
Wed, 17 Feb 2021 09:25:49 +0000 (10:25 +0100)
pdns/pdns_recursor.cc
pdns/rec_channel_rec.cc
pdns/rec_control.cc
pdns/syncres.cc
pdns/syncres.hh

index 595892a68047111b5fd746e3aec2c494d6bed425..7fd6d877aefddd98271db4f96854b158ef7553fe 100644 (file)
@@ -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";
index 7b16c21eab905693a51e9f9e56f4759bf7b6d43a..fd358fd7da12622a87782fab1826aa311009cb12 100644 (file)
@@ -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 <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"
@@ -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)};
   }
index 942058abf15b662cebf2872d6623a0c39f855d79..b3ef73e9a47d34ed0daae581b35684f09a478c1b 100644 (file)
@@ -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);
index b9b12c218ec2343babbad59937b65107b2f76636..7ea387932a4676c95297e4656c36b14ff1b3391a 100644 (file)
@@ -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<uint64_t> SyncRes::s_authzonequeries;
 std::atomic<uint64_t> 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<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;
@@ -1133,27 +1160,27 @@ vector<ComboAddress> 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 "<<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;
@@ -2269,9 +2296,33 @@ vector<ComboAddress> SyncRes::retrieveAddressesForNS(const std::string& prefix,
 {
   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 {
index e8da3dbba6fce1b108d94c7b6d142c7b535098a0..5616bc101caddcf452593fb4aced2fae9bc41684 100644 (file)
@@ -186,27 +186,28 @@ private:
   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);
 
@@ -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<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);
   }
@@ -245,7 +246,7 @@ public:
   }
 
   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));
   }
 
@@ -403,7 +404,8 @@ public:
     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;
   };
 
@@ -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<domainmap_t> 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;