]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Use structured logging, move loading to separate thread (a la RPZ)
authorOtto Moerbeek <otto.moerbeek@open-xchange.com>
Fri, 1 Oct 2021 11:42:14 +0000 (13:42 +0200)
committerOtto <otto.moerbeek@open-xchange.com>
Mon, 4 Oct 2021 10:46:07 +0000 (12:46 +0200)
pdns/pdns_recursor.cc
pdns/rec-lua-conf.cc
pdns/rec-lua-conf.hh
pdns/recursordist/rec-zonetocache.cc
pdns/recursordist/rec-zonetocache.hh

index 1eef4713dc0b468439d4198a9edcf092d7e2f666..0011b55180e69ffffbb97dae6107ba90d905b2b0 100644 (file)
 #include "rec-protozero.hh"
 
 #include "xpf.hh"
-#include "rec-zonetocache.hh"
 
 typedef map<ComboAddress, uint32_t, ComboAddress::addressOnlyLessThan> tcpClientCounts_t;
 
@@ -3702,8 +3701,6 @@ static void houseKeeping(void *)
   static thread_local int cleanCounter=0;
   static thread_local bool s_running;  // houseKeeping can get suspended in secpoll, and be restarted, which makes us do duplicate work
   static time_t last_RC_prune = 0;
-  static time_t last_zone_to_cache = 0;
-  static time_t zones_to_cache_interval = 0;
   auto luaconfsLocal = g_luaconfs.getLocal();
 
   if (last_trustAnchorUpdate == 0 && !luaconfsLocal->trustAnchorFileInfo.fname.empty() && luaconfsLocal->trustAnchorFileInfo.interval != 0) {
@@ -3775,11 +3772,6 @@ static void houseKeeping(void *)
             g_log<<Logger::Error<<"Exception while priming the root NS zones"<<endl;
           }
         }
-        // Redo zones-to-cache if the min TTL of those zones time is 90% passed.
-        if (!luaconfsLocal->zonesToCacheConfig.empty() && now.tv_sec - last_zone_to_cache > zones_to_cache_interval * 9 / 10) {
-          last_zone_to_cache = now.tv_sec;
-          zones_to_cache_interval = RecZoneToCache::ZonesToCache(luaconfsLocal->zonesToCacheConfig);
-        }
       }
 
       if(now.tv_sec - last_secpoll >= 3600) {
index 6bcd0b897b2b596b4c1b787fc3adeb0d1044cbdb..4d3f10dd94203adc45fe73070aab95dcd7e80181 100644 (file)
@@ -400,17 +400,17 @@ void loadRecursorLuaConfig(const std::string& fname, luaConfigDelayedThreads& de
 
   typedef std::unordered_map<std::string, boost::variant<uint32_t, std::string>> zoneToCacheOptions_t;
 
-  Lua.writeFunction("zoneToCache", [&lci](const string& zoneName, const string& method, const boost::variant<string, std::vector<std::pair<int, string>>>& primaries, boost::optional<zoneToCacheOptions_t> options) {
+  Lua.writeFunction("zoneToCache", [&delayedThreads](const string& zoneName, const string& method, const boost::variant<string, std::vector<std::pair<int, string>>>& srcs, boost::optional<zoneToCacheOptions_t> options) {
     try {
       RecZoneToCache::Config conf;
       conf.d_zone = zoneName;
       conf.d_method = method;
-      if (primaries.type() == typeid(string)) {
-        conf.d_sources.push_back(boost::get<std::string>(primaries));
+      if (srcs.type() == typeid(std::string)) {
+        conf.d_sources.push_back(boost::get<std::string>(srcs));
       }
       else {
-        for (const auto& primary : boost::get<std::vector<std::pair<int, std::string>>>(primaries)) {
-          conf.d_sources.push_back(primary.second);
+        for (const auto& src : boost::get<std::vector<std::pair<int, std::string>>>(srcs)) {
+          conf.d_sources.push_back(src.second);
         }
       }
       if (options) {
@@ -432,9 +432,15 @@ void loadRecursorLuaConfig(const std::string& fname, luaConfigDelayedThreads& de
         if (have.count("localAddress")) {
           conf.d_local = ComboAddress(boost::get<string>(have["localAddress"]));
         }
+        if (have.count("refreshPeriod")) {
+          conf.d_refreshPeriod = boost::get<uint32_t>(have["refreshPeriod"]);
+        }
+        if (have.count("retryOnErrorPeriod")) {
+          conf.d_retryOnError = boost::get<uint32_t>(have["retryOnErrorPeriod"]);
+        }
       }
 
-      lci.zonesToCacheConfig.push_back(conf);
+      delayedThreads.ztcConfigs.push_back(conf);
     }
     catch (const std::exception&e ) {
       g_log<<Logger::Error<<"Problem configuring zoneToCache: "<<e.what()<<endl;
@@ -676,11 +682,26 @@ void startLuaConfigDelayedThreads(const luaConfigDelayedThreads& delayedThreads,
     }
     catch(const std::exception& e) {
       g_log<<Logger::Error<<"Problem starting RPZIXFRTracker thread: "<<e.what()<<endl;
-      exit(1);  // FIXME proper exit code?
+      exit(1);
     }
     catch(const PDNSException& e) {
       g_log<<Logger::Error<<"Problem starting RPZIXFRTracker thread: "<<e.reason<<endl;
-      exit(1);  // FIXME proper exit code?
+      exit(1);
+    }
+  }
+
+  for (const auto& ztcConfig: delayedThreads.ztcConfigs) {
+    try {
+      std::thread t(RecZoneToCache::ZoneToCache, ztcConfig, generation);
+      t.detach();
+    }
+    catch(const std::exception& e) {
+      g_log<<Logger::Error<<"Problem starting zoneToCache thread: "<<e.what()<<endl;
+      exit(1);
+    }
+    catch(const PDNSException& e) {
+      g_log<<Logger::Error<<"Problem starting zoneToCache thread: "<<e.reason<<endl;
+      exit(1);
     }
   }
 }
index c9317f108856561cdd0b20ef1b94e1c5e8be9d45..2a0de6a5ec15bce7bbd73f01028a48bdd9845953 100644 (file)
@@ -73,7 +73,6 @@ public:
   ProtobufExportConfig protobufExportConfig;
   ProtobufExportConfig outgoingProtobufExportConfig;
   FrameStreamExportConfig frameStreamExportConfig;
-  vector<RecZoneToCache::Config> zonesToCacheConfig;
   /* we need to increment this every time the configuration
      is reloaded, so we know if we need to reload the protobuf
      remote loggers */
@@ -88,6 +87,7 @@ struct luaConfigDelayedThreads
 {
   // Please make sure that the tuple below only contains value types since they are used as parameters in a thread ct
   std::vector<std::tuple<std::vector<ComboAddress>, boost::optional<DNSFilterEngine::Policy>, bool, uint32_t, size_t, TSIGTriplet, size_t, ComboAddress, uint16_t, uint32_t, std::shared_ptr<SOARecordContent>, std::string> > rpzPrimaryThreads;
+  std::vector<RecZoneToCache::Config> ztcConfigs;
 };
 
 void loadRecursorLuaConfig(const std::string& fname, luaConfigDelayedThreads& delayedThreads);
index 053ddbcb567c103d81f839660e1fda2c9ad87f89..bfff560d216b49efca4d75651110edb9b7b4d922 100644 (file)
@@ -20,7 +20,6 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-#include <algorithm>
 #include "rec-zonetocache.hh"
 
 #include "syncres.hh"
 #include "query-local-address.hh"
 #include "axfr-retriever.hh"
 #include "validate-recursor.hh"
+#include "logging.hh"
+#include "threadname.hh"
+#include "rec-lua-conf.hh"
 
 #ifdef HAVE_LIBCURL
 #include "minicurl.hh"
 #endif
 
-time_t RecZoneToCache::ZonesToCache(const std::vector<Config>& cfgs)
-{
-  // By default and max once per 24 hours
-  time_t refresh = 24 * 3600;
-
-  for (const auto& config : cfgs) {
-    const string msg = "zones-to-cache error while loading " + config.d_zone + ": ";
-    try {
-      refresh = std::min(refresh, ZoneToCache(config));
-    }
-    catch (const PDNSException& e) {
-      g_log << Logger::Error << msg << e.reason << endl;
-    }
-    catch (const std::runtime_error& e) {
-      g_log << Logger::Error << msg << e.what() << endl;
-    }
-    catch (...) {
-      g_log << Logger::Error << msg << "unexpected exception" << endl;
-    }
-
-    // We do not want to refresh more than once per hour
-    refresh = std::max(refresh, static_cast<time_t>(3600));
-  }
-
-  return refresh;
-}
+#include <fstream>
 
 struct ZoneData
 {
+  ZoneData(shared_ptr<Logr::Logger>& log) : d_log(log) {}
+
   std::map<pair<DNSName, QType>, vector<DNSRecord>> d_all;
   std::map<pair<DNSName, QType>, vector<shared_ptr<RRSIGRecordContent>>> d_sigs;
   std::set<DNSName> d_delegations;
-  time_t d_refresh;
   time_t d_now;
   DNSName d_zone;
+  shared_ptr<Logr::Logger>& d_log;
 
   bool isRRSetAuth(const DNSName& qname, QType qtype);
   void parseDRForCache(DNSRecord& dr);
   void getByAXFR(const RecZoneToCache::Config&);
+  time_t ZoneToCache(const RecZoneToCache::Config& config);
 };
 
 bool ZoneData::isRRSetAuth(const DNSName& qname, QType qtype)
@@ -97,7 +77,6 @@ void ZoneData::parseDRForCache(DNSRecord& dr)
 {
   const auto key = make_pair(dr.d_name, dr.d_type);
 
-  d_refresh = std::min(d_refresh, static_cast<time_t>(dr.d_ttl));
   dr.d_ttl += d_now;
 
   switch (dr.d_type) {
@@ -148,12 +127,13 @@ void ZoneData::getByAXFR(const RecZoneToCache::Config& config)
   if (local == ComboAddress()) {
     local = pdns::getQueryLocalAddress(primary.sin4.sin_family, 0);
   }
+
   AXFRRetriever axfr(primary, d_zone, tt, &local, maxReceivedBytes, axfrTimeout);
   Resolver::res_t nop;
   vector<DNSRecord> chunk;
   time_t axfrStart = time(nullptr);
   time_t axfrNow = time(nullptr);
-  shared_ptr<SOARecordContent> sr;
+
   while (axfr.getChunk(nop, &chunk, (axfrStart + axfrTimeout - axfrNow))) {
     for (auto& dr : chunk) {
       parseDRForCache(dr);
@@ -187,6 +167,10 @@ static std::vector<std::string> getURL(const RecZoneToCache::Config& config)
   MiniCurl mc;
   ComboAddress local = config.d_local;
   std::string reply = mc.getURL(config.d_sources.at(0), nullptr, local == ComboAddress() ? nullptr : &local, config.d_timeout, false, true);
+  if (config.d_maxReceivedBytes > 0 && reply.size() > config.d_maxReceivedBytes) {
+    // We should actually detect this *during* the GET
+    throw PDNSException("Retrieved data exceeds maxReceivedBytes");
+  }
   std::istringstream stream(reply);
   string line;
   while (std::getline(stream, line)) {
@@ -196,15 +180,13 @@ static std::vector<std::string> getURL(const RecZoneToCache::Config& config)
   return lines;
 }
 
-time_t RecZoneToCache::ZoneToCache(const Config& config)
+time_t ZoneData::ZoneToCache(const RecZoneToCache::Config& config)
 {
   if (config.d_sources.size() > 1) {
-    // XXX Warning
+    d_log->info("Multiple sources not yet supported, using first"); 
   }
-  ZoneData data;
-  data.d_refresh = std::numeric_limits<time_t>::max();
-  data.d_zone = DNSName(config.d_zone);
-  data.d_now = time(nullptr);
+  d_zone = DNSName(config.d_zone);
+  d_now = time(nullptr);
 
   // We do not do validation, it will happen on-demand if an Indeterminate record is encountered when the caches are queried
   // First scan all records collecting info about delegations ans sigs
@@ -212,29 +194,33 @@ time_t RecZoneToCache::ZoneToCache(const Config& config)
   // they could be entered in into the (neg)cache.
 
   if (config.d_method == "axfr") {
-    data.getByAXFR(config);
+    d_log->info("Getting zone by AXFR");
+    getByAXFR(config);
   }
   else {
     vector<string> lines;
     if (config.d_method == "url") {
+      d_log->info("Getting zone by URL");
       lines = getURL(config);
     }
     else if (config.d_method == "file") {
+      d_log->info("Getting zone from file");
       lines = getLinesFromFile(config);
     }
     DNSResourceRecord drr;
-    ZoneParserTNG zpt(lines, data.d_zone);
+    ZoneParserTNG zpt(lines, d_zone);
     zpt.setMaxGenerateSteps(0);
 
     while (zpt.get(drr)) {
       DNSRecord dr(drr);
-      data.parseDRForCache(dr);
+      parseDRForCache(dr);
     }
   }
 
   // Rerun, now inserting the rrsets into the cache with associated sigs
-  data.d_now = time(nullptr);
-  for (const auto& [key, v] : data.d_all) {
+  d_now = time(nullptr);
+  time_t refreshFromSoa = 0;
+  for (const auto& [key, v] : d_all) {
     const auto& [qname, qtype] = key;
     switch (qtype) {
     case QType::NSEC:
@@ -243,23 +229,68 @@ time_t RecZoneToCache::ZoneToCache(const Config& config)
     case QType::RRSIG:
       break;
     default: {
+      if (qtype == QType::SOA) {
+        const auto& rr = getRR<SOARecordContent>(v[0]);
+        refreshFromSoa = rr->d_st.refresh;
+      }
+
       vector<shared_ptr<RRSIGRecordContent>> sigsrr;
-      auto it = data.d_sigs.find(key);
-      if (it != data.d_sigs.end()) {
+      auto it = d_sigs.find(key);
+      if (it != d_sigs.end()) {
         for (const auto& sig : it->second) {
           sigsrr.push_back(sig);
         }
       }
-      bool auth = data.isRRSetAuth(qname, qtype);
+      bool auth = isRRSetAuth(qname, qtype);
       // Same decision as updateCacheFromRecords() (we do not test for NSEC since we skip those completely)
       if (auth || (qtype == QType::NS || qtype == QType::A || qtype == QType::AAAA || qtype == QType::DS)) {
-        g_recCache->replace(data.d_now, qname, qtype, v, sigsrr,
-                            std::vector<std::shared_ptr<DNSRecord>>(), auth, data.d_zone);
+        g_recCache->replace(d_now, qname, qtype, v, sigsrr,
+                            std::vector<std::shared_ptr<DNSRecord>>(), auth, d_zone);
       }
       break;
     }
     }
   }
 
-  return data.d_refresh;
+  return refreshFromSoa;
+}
+
+// Config must be a copy, so call by value!
+void RecZoneToCache::ZoneToCache(RecZoneToCache::Config config, uint64_t configGeneration)
+{
+  setThreadName("pdns-r/ztc/" + config.d_zone);
+  auto luaconfsLocal = g_luaconfs.getLocal();
+
+  auto log = g_slog->withName("ztc")->withValues("zone", Logging::Loggable(config.d_zone));
+
+  while (true) {
+    if (luaconfsLocal->generation != configGeneration) {
+      /* the configuration has been reloaded, meaning that a new thread
+         has been started to handle that zone and we are now obsolete.
+      */
+      log->info("A more recent configuration has been found, stopping the old update thread");
+      return;
+    }
+
+    time_t refresh = config.d_retryOnError;
+    try {
+      ZoneData data(log);
+      time_t refreshFromSoa = data.ZoneToCache(config);
+      refresh = config.d_refreshPeriod;
+      if (refresh == 0) {
+        refresh = refreshFromSoa;
+      }
+      log->info("Loaded zone into cache", "refresh", Logging::Loggable(refresh));
+    }
+    catch (const PDNSException &e) {
+      log->info("Unable to load zone into cache, will retry", "exception", Logging::Loggable(e.reason), "refresh", Logging::Loggable(refresh));
+    }
+    catch (const std::runtime_error& e) {
+      log->info("Unable to load zone into cache, will retry", "exception", Logging::Loggable(e.what()), "refresh", Logging::Loggable(refresh));
+    }
+    catch (...) {
+      log->info("Unable to load zone into cache, will retry", "exception", Logging::Loggable("unknown"), "refresh", Logging::Loggable(refresh));
+    }
+    sleep(refresh);
+  }
 }
index 8d80913e565c08f0c1ef24752ae26ecc21633118..81e86cf2c22ed2d857965284183ec66ea783045e 100644 (file)
@@ -37,10 +37,9 @@ public:
     ComboAddress d_local; // local address
     TSIGTriplet d_tt; // Authentication data
     size_t d_maxReceivedBytes{0}; // Maximum size
-    uint32_t d_timeout{20}; // timeout in seconds
+    time_t d_retryOnError{60}; // Retry on error
+    time_t d_refreshPeriod{0}; // Take from SOA by default
+    uint32_t d_timeout{30}; // timeout in seconds
   };
-  static time_t ZonesToCache(const std::vector<Config>&);
-
-private:
-  static time_t ZoneToCache(const Config& config);
+  static void ZoneToCache(Config config, uint64_t gen);
 };