]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Scaffolding for config via Lua
authorOtto <otto.moerbeek@open-xchange.com>
Wed, 29 Sep 2021 13:51:51 +0000 (15:51 +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 ceb32001a45a2db17063ee8844dc7ad0394a0862..1eef4713dc0b468439d4198a9edcf092d7e2f666 100644 (file)
@@ -136,7 +136,6 @@ static thread_local uint64_t t_frameStreamServersGeneration;
 thread_local std::unique_ptr<MT_t> MT; // the big MTasker
 std::unique_ptr<MemRecursorCache> g_recCache;
 std::unique_ptr<NegCache> g_negCache;
-std::map<std::string, std::string> g_zones_to_cache;
 
 thread_local std::unique_ptr<RecursorPacketCache> t_packetCache;
 thread_local FDMultiplexer* t_fdm{nullptr};
@@ -3776,10 +3775,10 @@ 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% up.
-        if (!g_zones_to_cache.empty() && now.tv_sec - last_zone_to_cache > zones_to_cache_interval * 9 / 10) {
+        // 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(g_zones_to_cache);
+          zones_to_cache_interval = RecZoneToCache::ZonesToCache(luaconfsLocal->zonesToCacheConfig);
         }
       }
 
@@ -5027,19 +5026,6 @@ static int serviceMain(int argc, char*argv[])
       exit(1);
     }
   }
-  {
-    std::vector<std::string> parts;
-    stringtok(parts, ::arg()["zones-to-cache"], " ,\t\n\r");
-    for (const auto& p : parts) {
-      if (p.find('=') == string::npos) {
-        throw PDNSException("Error parsing '" + p + "', missing =");
-      }
-      auto [zone, url] = splitField(p, '=');
-      boost::trim(zone);
-      boost::trim(url);
-      g_zones_to_cache[zone] = url;
-    }
-  }
 
   g_networkTimeoutMsec = ::arg().asNum("network-timeout");
 
@@ -5963,7 +5949,6 @@ int main(int argc, char **argv)
 
     ::arg().setSwitch("dot-to-port-853", "Force DoT connection to target port 853 if DoT compiled in")="yes";
     ::arg().set("dot-to-auth-names", "Use DoT to authoritative servers with these names or suffixes")="";
-    ::arg().set("zones-to-cache", "Zones to prefill the cache with, comma separated domain=url pairs")="";
 
     ::arg().set("tcp-out-max-idle-ms", "Time TCP/DoT connections are left idle in milliseconds or 0 if no limit") = "10000";
     ::arg().set("tcp-out-max-idle-per-auth", "Maximum number of idle TCP/DoT connections to a specific IP per thread, 0 means do not keep idle connections open") = "10";
index 3f86fcd0be24d5ba06f3c13d3109708bd2127344..6bcd0b897b2b596b4c1b787fc3adeb0d1044cbdb 100644 (file)
@@ -398,6 +398,49 @@ void loadRecursorLuaConfig(const std::string& fname, luaConfigDelayedThreads& de
     rpzPrimary(lci, delayedThreads, primaries_, zoneName, options);
       });
 
+  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) {
+    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));
+      }
+      else {
+        for (const auto& primary : boost::get<std::vector<std::pair<int, std::string>>>(primaries)) {
+          conf.d_sources.push_back(primary.second);
+        }
+      }
+      if (options) {
+        auto& have = *options;
+        if (have.count("timeout")) {
+          conf.d_timeout = boost::get<uint32_t>(have["timeout"]);
+        }
+        if (have.count("tsigname")) {
+          conf.d_tt.name = DNSName(toLower(boost::get<string>(have["tsigname"])));
+          conf.d_tt.algo = DNSName(toLower(boost::get<string>(have[ "tsigalgo"])));
+          if (B64Decode(boost::get<string>(have[ "tsigsecret"]), conf.d_tt.secret)) {
+            throw std::runtime_error("TSIG secret is not valid Base-64 encoded");
+          }
+        }
+        if (have.count("maxReceivedMBytes")) {
+          conf.d_maxReceivedBytes = static_cast<size_t>(boost::get<uint32_t>(have["maxReceivedMBytes"]));
+          conf.d_maxReceivedBytes *= 1024 * 1024;
+        }
+        if (have.count("localAddress")) {
+          conf.d_local = ComboAddress(boost::get<string>(have["localAddress"]));
+        }
+      }
+
+      lci.zonesToCacheConfig.push_back(conf);
+    }
+    catch (const std::exception&e ) {
+      g_log<<Logger::Error<<"Problem configuring zoneToCache: "<<e.what()<<endl;
+    }
+  });
   typedef vector<pair<int,boost::variant<string, vector<pair<int, string> > > > > argvec_t;
   Lua.writeFunction("addSortList", 
                    [&lci](const std::string& formask_, 
index cc03bf99a26f9da14915c5487ccb3c610a8168bf..c9317f108856561cdd0b20ef1b94e1c5e8be9d45 100644 (file)
@@ -26,6 +26,7 @@
 #include "sortlist.hh"
 #include "filterpo.hh"
 #include "validate.hh"
+#include "rec-zonetocache.hh"
 
 struct ProtobufExportConfig
 {
@@ -72,7 +73,7 @@ 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 */
index 1835e89cbf7abbbf270d69d6151a39d42245c743..9e4a4899beaef79c9a75d98abeccb14ec5503a60 100644 (file)
 #include "rec-zonetocache.hh"
 
 #include "syncres.hh"
-#include "minicurl.hh"
 #include "zoneparser-tng.hh"
 #include "query-local-address.hh"
 #include "axfr-retriever.hh"
 #include "validate-recursor.hh"
 
-time_t RecZoneToCache::ZonesToCache(const std::map<std::string, std::string>& map)
+#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;
@@ -41,10 +44,10 @@ time_t RecZoneToCache::ZonesToCache(const std::map<std::string, std::string>& ma
   sr.setDoDNSSEC(dnssec);
   sr.setDNSSECValidationRequested(g_dnssecmode != DNSSECMode::Off && g_dnssecmode != DNSSECMode::ProcessNoValidate);
 
-  for (const auto& [zone, url] : map) {
-    const string msg = "zones-to-cache error while loading " + zone + " from " + url + ": ";
+  for (const auto& config : cfgs) {
+    const string msg = "zones-to-cache error while loading " + config.d_zone + ": ";
     try {
-      refresh = min(refresh, ZoneToCache(zone, url, dnssec));
+      refresh = min(refresh, ZoneToCache(config, dnssec));
     }
     catch (const PDNSException& e) {
       g_log << Logger::Error << msg << e.reason << endl;
@@ -57,7 +60,7 @@ time_t RecZoneToCache::ZonesToCache(const std::map<std::string, std::string>& ma
     }
 
     // We do not want to refresh more than once per hour
-    refresh = max(refresh, 3600LL);
+    refresh = std::max(refresh, 3600LL);
   }
 
   return refresh;
@@ -74,7 +77,7 @@ struct ZoneData
 
   bool isRRSetAuth(const DNSName& qname, QType qtype);
   void parseDRForCache(DNSRecord& dr);
-  void getByAXFR(const std::string& url);
+  void getByAXFR(const RecZoneToCache::Config&);
 };
 
 bool ZoneData::isRRSetAuth(const DNSName& qname, QType qtype)
@@ -141,13 +144,13 @@ void ZoneData::parseDRForCache(DNSRecord& dr)
   }
 }
 
-void ZoneData::getByAXFR(const std::string& url)
+void ZoneData::getByAXFR(const RecZoneToCache::Config& config)
 {
-  ComboAddress primary = ComboAddress(url.substr(7), 53);
-  uint16_t axfrTimeout = 20;
-  size_t maxReceivedBytes = 0;
-  const TSIGTriplet tt;
-  ComboAddress local; //(localAddress);
+  ComboAddress primary = ComboAddress(config.d_sources.at(0), 53);
+  uint16_t axfrTimeout = config.d_timeout;
+  size_t maxReceivedBytes = config.d_maxReceivedBytes;
+  const TSIGTriplet tt = config.d_tt;
+  ComboAddress local = config.d_local;
   if (local == ComboAddress()) {
     local = pdns::getQueryLocalAddress(primary.sin4.sin_family, 0);
   }
@@ -168,10 +171,14 @@ void ZoneData::getByAXFR(const std::string& url)
   }
 }
 
-static std::vector<std::string> getLinesFromFile(const std::string& url)
+static std::vector<std::string> getLinesFromFile(const RecZoneToCache::Config& config)
 {
+
   std::vector<std::string> lines;
-  std::ifstream stream(url);
+  std::ifstream stream(config.d_sources.at(0));
+  if (!stream) {
+    throw PDNSException("Cannot read file: " + config.d_sources.at(0));
+  }
   std::string line;
   while (std::getline(stream, line)) {
     lines.push_back(line);
@@ -179,12 +186,13 @@ static std::vector<std::string> getLinesFromFile(const std::string& url)
   return lines;
 }
 
-static std::vector<std::string> getURL(const std::string& url)
+static std::vector<std::string> getURL(const RecZoneToCache::Config& config)
 {
   std::vector<std::string> lines;
 #ifdef HAVE_LIBCURL
   MiniCurl mc;
-  std::string reply = mc.getURL(url, nullptr, nullptr, 10, false, true);
+  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);
   std::istringstream stream(reply);
   string line;
   while (std::getline(stream, line)) {
@@ -194,11 +202,14 @@ static std::vector<std::string> getURL(const std::string& url)
   return lines;
 }
 
-time_t RecZoneToCache::ZoneToCache(const string& name, const string& url, bool dnssec)
+time_t RecZoneToCache::ZoneToCache(const Config& config, bool dnssec)
 {
+  if (config.d_sources.size() > 1) {
+    // XXX Warning
+  }
   ZoneData data;
   data.d_refresh = std::numeric_limits<time_t>::max();
-  data.d_zone = DNSName(name);
+  data.d_zone = DNSName(config.d_zone);
   data.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
@@ -206,16 +217,16 @@ time_t RecZoneToCache::ZoneToCache(const string& name, const string& url, bool d
   // A this moment, we ignore NSEC and NSEC3 records. It is not clear to me yet under which conditions
   // they could be entered in into the (neg)cache.
 
-  if (url.substr(0, 7) == "axfr://") {
-    data.getByAXFR(url);
+  if (config.d_method == "axfr") {
+    data.getByAXFR(config);
   }
   else {
     vector<string> lines;
-    if (url.substr(0, 8) != "https://" && url.substr(0, 7) != "http://") {
-      lines = getLinesFromFile(url);
+    if (config.d_method == "url") {
+      lines = getURL(config);
     }
-    else {
-      lines = getURL(url);
+    else if (config.d_method == "file") {
+      lines = getLinesFromFile(config);
     }
     DNSResourceRecord drr;
     ZoneParserTNG zpt(lines, data.d_zone);
index 7f777dcf9fe1ab7bb877927f052de7313a5a0777..d95891e9557fe0bff08e38f233250ff5a486ca80 100644 (file)
 #pragma once
 
 #include "namespaces.hh"
+#include "dns.hh"
+#include "iputils.hh"
 
 class RecZoneToCache
 {
 public:
-  static time_t ZonesToCache(const std::map<std::string, std::string>& map);
+  struct Config
+  {
+    std::string d_zone; // Zone name
+    std::string d_method; // axfr, http, https, file
+    vector<std::string> d_sources; // IPs or URLs
+    uint32_t d_timeout{20}; // timeout in seconds
+    ComboAddress d_local; // local address
+    size_t d_maxReceivedBytes{0}; // Maximum size
+    TSIGTriplet d_tt; // Authentication data
+  };
+  static time_t ZonesToCache(const std::vector<Config>&);
 
 private:
-  static time_t ZoneToCache(const string& name, const string& url, bool dnssec);
+  static time_t ZoneToCache(const Config& config, bool dnssec);
 };