]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Merge pull request #11603 from rgacogne/ddist-upgrade-guide-formatting
authorRemi Gacogne <remi.gacogne@powerdns.com>
Thu, 5 May 2022 11:33:01 +0000 (13:33 +0200)
committerGitHub <noreply@github.com>
Thu, 5 May 2022 11:33:01 +0000 (13:33 +0200)
dnsdist: Fix formatting issues in the upgrade guide

22 files changed:
pdns/anadns.hh
pdns/decafsigners.cc
pdns/dnsdist-console.cc
pdns/dnsdist-lua.cc
pdns/dnsdistdist/dnsdist-backend.cc
pdns/dnsdistdist/docs/reference/config.rst
pdns/dnsdistdist/docs/reference/ebpf.rst
pdns/dnsreplay.cc
pdns/dnssecinfra.cc
pdns/ixplore.cc
pdns/opensslsigners.cc
pdns/rec_channel_rec.cc
pdns/recursordist/docs/upgrade.rst
pdns/recursordist/rec-main.cc
pdns/signingpipe.cc
pdns/sodiumsigners.cc
pdns/syncres.cc
pdns/syncres.hh
pdns/test-signers.cc
pdns/zonemd.cc
regression-tests.auth-py/test_XFRIncomplete.py
regression-tests.recursor-dnssec/test_Protobuf.py

index 8c2e168890bb2e48758c907c4a5887086f73d0c7..8b68db8488cb010a42ba5c432b0c05529ed9fdc2 100644 (file)
@@ -69,8 +69,8 @@ struct QuestionIdentifier
   ComboAddress d_source, d_dest;
 
   DNSName d_qname;
-  uint16_t d_qtype;
-  uint16_t d_id;
+  uint16_t d_qtype{0};
+  uint16_t d_id{0};
 };
 
 inline ostream& operator<<(ostream &s, const QuestionIdentifier& qi) 
index 9f3bd1bda32b8a6c44ce08c53bd0eeaf2da4a5a1..5eae78292f57d97d7738e4fb1ca126b8a8ac049c 100644 (file)
@@ -6,6 +6,7 @@
 #include <decaf.hxx>
 #include <decaf/eddsa.hxx>
 #include <decaf/spongerng.hxx>
+#include "dnsseckeeper.hh"
 
 #include "dnssecinfra.hh"
 
@@ -421,12 +422,12 @@ bool DecafED448DNSCryptoKeyEngine::verify(const std::string& msg, const std::str
 
 namespace
 {
-struct LoaderDecafStruct
+const struct LoaderDecafStruct
 {
   LoaderDecafStruct()
   {
-    DNSCryptoKeyEngine::report(15, &DecafED25519DNSCryptoKeyEngine::maker, true);
-    DNSCryptoKeyEngine::report(16, &DecafED448DNSCryptoKeyEngine::maker);
+    DNSCryptoKeyEngine::report(DNSSECKeeper::ED25519, &DecafED25519DNSCryptoKeyEngine::maker, true);
+    DNSCryptoKeyEngine::report(DNSSECKeeper::ED448, &DecafED448DNSCryptoKeyEngine::maker);
   }
 } loaderdecaf;
 }
index 2058e804d2456396fb518423935e0ae68f4e6caf..acca971812cece99962ffc17f1b9b0f18e5a95dc 100644 (file)
@@ -252,10 +252,6 @@ void doClient(ComboAddress server, const std::string& command)
   else if (commandResult == ConsoleCommandResult::TooLarge) {
     return;
   }
-  else if (commandResult == ConsoleCommandResult::TooLarge) {
-    close(fd);
-    return;
-  }
 
   if (!command.empty()) {
     sendMessageToServer(fd.getHandle(), command, readingNonce, writingNonce, false);
@@ -709,6 +705,7 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
   { "setUDPMultipleMessagesVectorSize", true, "n", "set the size of the vector passed to recvmmsg() to receive UDP messages. Default to 1 which means that the feature is disabled and recvmsg() is used instead" },
   { "setUDPSocketBufferSizes", true, "recv, send", "Set the size of the receive (SO_RCVBUF) and send (SO_SNDBUF) buffers for incoming UDP sockets" },
   { "setUDPTimeout", true, "n", "set the maximum time dnsdist will wait for a response from a backend over UDP, in seconds" },
+  { "setVerbose", true, "bool", "set whether log messages at the verbose level will be logged" },
   { "setVerboseHealthChecks", true, "bool", "set whether health check errors will be logged" },
   { "setWebserverConfig", true, "[{password=string, apiKey=string, customHeaders, statsRequireAuthentication}]", "Updates webserver configuration" },
   { "setWeightedBalancingFactor", true, "factor", "Set the balancing factor for bounded-load weighted policies (whashed, wrandom)" },
index dfed80d7f8f1558ab0f4194dce0030a693a993a3..da85a9bc9cde5da2615f6768decb98f39b480b86 100644 (file)
@@ -1717,7 +1717,9 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck)
     return pool;
   });
 
+  luaCtx.writeFunction("setVerbose", [](bool verbose) { g_verbose = verbose; });
   luaCtx.writeFunction("setVerboseHealthChecks", [](bool verbose) { g_verboseHealthChecks = verbose; });
+
   luaCtx.writeFunction("setStaleCacheEntriesTTL", [](uint64_t ttl) {
     checkParameterBound("setStaleCacheEntriesTTL", ttl, std::numeric_limits<uint32_t>::max());
     g_staleCacheEntriesTTL = ttl;
index 25dd110345f0a2384cd595e59e603f1c8a5d890b..ac655111fab141e2b6c815d52197b1879ba13f16 100644 (file)
@@ -357,16 +357,18 @@ void DownstreamState::handleTimeouts()
     }
   }
   else {
-    for (IDState& ids : idStates) {
-      int64_t usageIndicator = ids.usageIndicator;
-      if (IDState::isInUse(usageIndicator) && isIDSExpired(ids)) {
-        if (!ids.tryMarkUnused(usageIndicator)) {
-          /* this state has been altered in the meantime,
-             don't go anywhere near it */
-          continue;
-        }
+    if (outstanding.load() > 0) {
+      for (IDState& ids : idStates) {
+        int64_t usageIndicator = ids.usageIndicator;
+        if (IDState::isInUse(usageIndicator) && isIDSExpired(ids)) {
+          if (!ids.tryMarkUnused(usageIndicator)) {
+            /* this state has been altered in the meantime,
+               don't go anywhere near it */
+            continue;
+          }
 
-        handleTimeout(ids);
+          handleTimeout(ids);
+        }
       }
     }
   }
index b79c4831a82e8c97bd1c409fc4667da98ef65218..293b06055919c152b146e35eabeb6edf745a5147 100644 (file)
@@ -1059,6 +1059,14 @@ Status, Statistics and More
   :param {str} selectors: A lua table of selectors. Only queries matching all selectors are shown
   :param int num: Show a maximum of ``num`` recent queries+responses, default is 10.
 
+.. function:: setVerbose(verbose)
+
+  .. versionadded:: 1.8.0
+
+  Set whether log messages issued at the verbose level should be logged. This is turned off by default.
+
+  :param bool verbose: Set to true if you want to enable verbose logging
+
 .. function:: setVerboseHealthChecks(verbose)
 
   Set whether health check errors should be logged. This is turned off by default.
index a7fb1ba8b891ea279d55839eb7446cbfa811f83d..c9d838213de4981cc7d231dfb0aff709c358554c 100644 (file)
@@ -113,10 +113,10 @@ These are all the functions, objects and methods related to the :doc:`../advance
 
     Exclude this range, or list of ranges, meaning that no dynamic block will ever be inserted for clients in that range. Default to empty, meaning rules are applied to all ranges. When used in combination with :meth:`DynBPFFilter:includeRange`, the more specific entry wins.
 
-    :param int netmasks: A netmask, or list of netmasks, as strings, like for example "192.0.2.1/24"
+    :param str or list of str netmasks: A netmask, or list of netmasks, as strings, like for example "192.0.2.1/24"
 
   .. method:: DynBPFFilter:includeRange(netmasks)
 
     Include this range, or list of ranges, meaning that rules will be applied to this range. When used in combination with :meth:`DynBPFFilter:excludeRange`, the more specific entry wins.
 
-    :param int netmasks: A netmask, or list of netmasks, as strings, like for example "192.0.2.1/24"
+    :param str or list of str netmasks: A netmask, or list of netmasks, as strings, like for example "192.0.2.1/24"
index 0d4b8d10014f7bb6bb05ac8c33002057d289e5e4..8b9c55b547dd6dc10c857523850de9a9a5374f0a 100644 (file)
@@ -296,6 +296,10 @@ static uint64_t countLessThan(unsigned int msec)
 static void emitFlightTimes()
 {
   uint64_t totals = countLessThan(flightTimes.size());
+  if (totals == 0) {
+    // Avoid division by zero below
+    totals = 1;
+  }
   unsigned int limits[]={1, 2, 3, 4, 5, 10, 20, 30, 40, 50, 100, 200, 500, 1000, (unsigned int) flightTimes.size()};
   uint64_t sofar=0;
   cout.setf(std::ios::fixed);
index e49176d87f6bb6e17e3403f49ccbadf919eb54de..43d623f93e18e91ce3d64b4fa2086fe247240eb5 100644 (file)
@@ -225,10 +225,10 @@ vector<pair<uint8_t, string>> DNSCryptoKeyEngine::listAllAlgosWithBackend()
 void DNSCryptoKeyEngine::report(unsigned int algo, maker_t* maker, bool fallback)
 {
   getAllMakers()[algo].push_back(maker);
-  if(getMakers().count(algo) && fallback) {
+  if (getMakers().count(algo) != 0 && fallback) {
     return;
   }
-  getMakers()[algo]=maker;
+  getMakers()[algo] = maker;
 }
 
 bool DNSCryptoKeyEngine::testAll()
index e72ce55df2b48ff8e3fb1232949028318384c4e9..0890aa3d2507b7072b7f9a8236f1be098736f067 100644 (file)
@@ -179,8 +179,9 @@ int main(int argc, char** argv) {
       shared_ptr<SOARecordContent> sr;
       uint32_t serial = getSerialFromMaster(master, zone, sr, tt);
       if(ourSerial == serial) {
-        cout<<"still up to date, their serial is "<<serial<<", sleeping "<<sr->d_st.refresh<<" seconds"<<endl;
-        sleep(sr->d_st.refresh);
+        time_t sleepTime = sr ? sr->d_st.refresh : 60;
+        cout<<"still up to date, their serial is "<<serial<<", sleeping "<<sleepTime<<" seconds"<<endl;
+        sleep(sleepTime);
         continue;
       }
 
index c521e1558ffff9d380c0e12be142f89d55e2ed73..ad95a1d69665c9f7ebef74618a2f11a911bef565 100644 (file)
@@ -189,6 +189,36 @@ public:
   int getBits() const override { return RSA_size(d_key.get()) << 3; }
 
   void create(unsigned int bits) override;
+
+  /**
+   * \brief Creates an RSA key engine from a PEM file.
+   *
+   * Receives an open file handle with PEM contents and creates an RSA key
+   * engine.
+   *
+   * \param[in] drc Key record contents to be populated.
+   *
+   * \param[in] filename Only used for providing filename information in error
+   * messages.
+   *
+   * \param[in] fp An open file handle to a file containing RSA PEM contents.
+   *
+   * \return An RSA key engine populated with the contents of the PEM file.
+   */
+  void createFromPEMFile(DNSKEYRecordContent& drc, const std::string& filename, std::FILE& fp) override;
+
+  /**
+   * \brief Writes this key's contents to a file.
+   *
+   * Receives an open file handle and writes this key's contents to the
+   * file.
+   *
+   * \param[in] fp An open file handle for writing.
+   *
+   * \exception std::runtime_error In case of OpenSSL errors.
+   */
+  void convertToPEM(std::FILE& fp) const override;
+
   storvector_t convertToISCVector() const override;
   std::string hash(const std::string& hash) const override;
   std::string sign(const std::string& hash) const override;
@@ -253,6 +283,20 @@ void OpenSSLRSADNSCryptoKeyEngine::create(unsigned int bits)
   d_key = std::move(key);
 }
 
+void OpenSSLRSADNSCryptoKeyEngine::createFromPEMFile(DNSKEYRecordContent& drc, const std::string& filename, std::FILE& fp) {
+  drc.d_algorithm = d_algorithm;
+  d_key = std::unique_ptr<RSA, decltype(&RSA_free)>(PEM_read_RSAPrivateKey(&fp, nullptr, nullptr, nullptr), &RSA_free);
+  if (d_key == nullptr) {
+    throw runtime_error(getName() + ": Failed to read private key from PEM file `" + filename + "`");
+  }
+}
+
+void OpenSSLRSADNSCryptoKeyEngine::convertToPEM(std::FILE& fp) const {
+  auto ret = PEM_write_RSAPrivateKey(&fp, d_key.get(), nullptr, nullptr, 0, nullptr, nullptr);
+  if (ret == 0) {
+    throw runtime_error(getName() + ": Could not convert private key to PEM");
+  }
+}
 
 DNSCryptoKeyEngine::storvector_t OpenSSLRSADNSCryptoKeyEngine::convertToISCVector() const
 {
@@ -1174,23 +1218,23 @@ void OpenSSLEDDSADNSCryptoKeyEngine::fromPublicKeyString(const std::string& cont
 #endif // HAVE_LIBCRYPTO_EDDSA
 
 namespace {
-  struct LoaderStruct
+  const struct LoaderStruct
   {
     LoaderStruct()
     {
-      DNSCryptoKeyEngine::report(5, &OpenSSLRSADNSCryptoKeyEngine::maker);
-      DNSCryptoKeyEngine::report(7, &OpenSSLRSADNSCryptoKeyEngine::maker);
-      DNSCryptoKeyEngine::report(8, &OpenSSLRSADNSCryptoKeyEngine::maker);
-      DNSCryptoKeyEngine::report(10, &OpenSSLRSADNSCryptoKeyEngine::maker);
+      DNSCryptoKeyEngine::report(DNSSECKeeper::RSASHA1, &OpenSSLRSADNSCryptoKeyEngine::maker);
+      DNSCryptoKeyEngine::report(DNSSECKeeper::RSASHA1NSEC3SHA1, &OpenSSLRSADNSCryptoKeyEngine::maker);
+      DNSCryptoKeyEngine::report(DNSSECKeeper::RSASHA256, &OpenSSLRSADNSCryptoKeyEngine::maker);
+      DNSCryptoKeyEngine::report(DNSSECKeeper::RSASHA512, &OpenSSLRSADNSCryptoKeyEngine::maker);
 #ifdef HAVE_LIBCRYPTO_ECDSA
-      DNSCryptoKeyEngine::report(13, &OpenSSLECDSADNSCryptoKeyEngine::maker);
-      DNSCryptoKeyEngine::report(14, &OpenSSLECDSADNSCryptoKeyEngine::maker);
+      DNSCryptoKeyEngine::report(DNSSECKeeper::ECDSA256, &OpenSSLECDSADNSCryptoKeyEngine::maker);
+      DNSCryptoKeyEngine::report(DNSSECKeeper::ECDSA384, &OpenSSLECDSADNSCryptoKeyEngine::maker);
 #endif
 #ifdef HAVE_LIBCRYPTO_ED25519
-      DNSCryptoKeyEngine::report(15, &OpenSSLEDDSADNSCryptoKeyEngine::maker);
+      DNSCryptoKeyEngine::report(DNSSECKeeper::ED25519, &OpenSSLEDDSADNSCryptoKeyEngine::maker);
 #endif
 #ifdef HAVE_LIBCRYPTO_ED448
-      DNSCryptoKeyEngine::report(16, &OpenSSLEDDSADNSCryptoKeyEngine::maker);
+      DNSCryptoKeyEngine::report(DNSSECKeeper::ED448, &OpenSSLEDDSADNSCryptoKeyEngine::maker);
 #endif
     }
   } loaderOpenSSL;
index 845d78a46396c257f5920dd6de8de53eb16b3a2e..1f76636a02391f0f32acfcd213b4efd692a59bc1 100644 (file)
@@ -966,16 +966,6 @@ static string doCurrentQueries()
   return broadcastAccFunction<string>(pleaseGetCurrentQueries);
 }
 
-uint64_t* pleaseGetThrottleSize()
-{
-  return new uint64_t(SyncRes::getThrottledServersSize());
-}
-
-static uint64_t getThrottleSize()
-{
-  return broadcastAccFunction<uint64_t>(pleaseGetThrottleSize);
-}
-
 static uint64_t getNegCacheSize()
 {
   return g_negCache->size();
@@ -1240,7 +1230,7 @@ static void registerAllStats1()
   addGetStat("max-mthread-stack", &g_stats.maxMThreadStackUsage);
 
   addGetStat("negcache-entries", getNegCacheSize);
-  addGetStat("throttle-entries", getThrottleSize);
+  addGetStat("throttle-entries", SyncRes::getThrottledServersSize);
 
   addGetStat("nsspeeds-entries", SyncRes::getNSSpeedsSize);
   addGetStat("failed-host-entries", SyncRes::getFailedServersSize);
@@ -2002,7 +1992,7 @@ RecursorControlChannel::Answer RecursorControlParser::getAnswer(int s, const str
     return doDumpRPZ(s, begin, end);
   }
   if (cmd == "dump-throttlemap") {
-    return doDumpToFile(s, pleaseDumpThrottleMap, cmd);
+    return doDumpToFile(s, pleaseDumpThrottleMap, cmd, false);
   }
   if (cmd == "dump-non-resolving") {
     return doDumpToFile(s, pleaseDumpNonResolvingNS, cmd, false);
index f43d3f9465a4f3a1a090657c2f8274275c6376d1..1aa07bc60735d1a7eb4b0a97daa4965ac7a6175a 100644 (file)
@@ -4,7 +4,14 @@ Upgrade Guide
 Before upgrading, it is advised to read the :doc:`changelog/index`.
 When upgrading several versions, please read **all** notes applying to the upgrade.
 
-4.6.2 to master
+4.7.0 to master
+---------------
+
+:program:`rec_control` changes
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+The ``dump-throttle`` subcommand no longer produces a table per thread, as the corresponding table now is shared by all threads.
+
+4.6.2 to 4.7.0
 ---------------
 
 Zone to Cache Changes
@@ -33,6 +40,10 @@ New settings
 - The :ref:`setting-max-busy-dot-probes` settings has been introduced, enabling the :program:`Recursor` probe for ``DoT`` support of authoritative servers.
   This is an experimental function, use with care.
 
+:program:`rec_control` changes
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+The ``dump-nsspeeds``, ``dump-failedservers`` and ``dump-non-resolving`` subcommands no longer produce a table per thread, as the corresponding tables are now shared by all threads.
+
 
 4.6.1 to 4.6.2
 --------------
index 4e8fd3a9c333696f6d1b1a5ae60eb922695df727..3b7805774c2a0e0a138bea52425137a9b5bb93a9 100644 (file)
@@ -890,7 +890,7 @@ static void doStats(void)
     g_log << Logger::Notice << "stats: cache contended/acquired " << rc_stats.first << '/' << rc_stats.second << " = " << r << '%' << endl;
 
     g_log << Logger::Notice << "stats: throttle map: "
-          << broadcastAccFunction<uint64_t>(pleaseGetThrottleSize) << ", ns speeds: "
+          << SyncRes::getThrottledServersSize() << ", ns speeds: "
           << SyncRes::getNSSpeedsSize() << ", failed ns: "
           << SyncRes::getFailedServersSize() << ", ednsmap: "
           << broadcastAccFunction<uint64_t>(pleaseGetEDNSStatusesSize) << ", non-resolving: "
@@ -1888,11 +1888,6 @@ static void houseKeeping(void*)
       SyncRes::pruneEDNSStatuses(now.tv_sec - 2 * 3600);
     });
 
-    static thread_local PeriodicTask pruneThrottledTask{"pruneThrottledTask", 5};
-    pruneThrottledTask.runIfDue(now, []() {
-      SyncRes::pruneThrottledServers();
-    });
-
     static thread_local PeriodicTask pruneTCPTask{"pruneTCPTask", 5};
     pruneTCPTask.runIfDue(now, [now]() {
       t_tcp_manager.cleanup(now);
@@ -1946,6 +1941,11 @@ static void houseKeeping(void*)
         });
       }
 
+      static PeriodicTask pruneThrottledTask{"pruneThrottledTask", 5};
+      pruneThrottledTask.runIfDue(now, [now]() {
+        SyncRes::pruneThrottledServers(now.tv_sec);
+      });
+
       static PeriodicTask pruneFailedServersTask{"pruneFailedServerTask", 5};
       pruneFailedServersTask.runIfDue(now, [now]() {
         SyncRes::pruneFailedServers(now.tv_sec - SyncRes::s_serverdownthrottletime * 10);
index 0f1bfd17b230af0227286957149f6cbea2ed1218..0198686d5c24651e8e2e6840db54f88172ed9750 100644 (file)
@@ -199,8 +199,10 @@ void ChunkedSigningPipe::sendRRSetToWorker() // it sounds so socialist!
   
   if(wantWrite && !rwVect.second.empty()) {
     shuffle(rwVect.second.begin(), rwVect.second.end(), pdns::dns_random_engine()); // pick random available worker
-    auto ptr = d_rrsetToSign.release();
+    auto ptr = d_rrsetToSign.get();
     writen2(*rwVect.second.begin(), &ptr, sizeof(ptr));
+    // coverity[leaked_storage]
+    static_cast<void>(d_rrsetToSign.release());
     d_rrsetToSign = make_unique<rrset_t>();
     d_outstandings[*rwVect.second.begin()]++;
     d_outstanding++;
@@ -248,8 +250,10 @@ void ChunkedSigningPipe::sendRRSetToWorker() // it sounds so socialist!
   if(wantWrite) {  // our optimization above failed, we now wait synchronously
     rwVect = waitForRW(false, wantWrite, -1); // wait for something to happen
     shuffle(rwVect.second.begin(), rwVect.second.end(), pdns::dns_random_engine()); // pick random available worker
-    auto ptr = d_rrsetToSign.release();
+    auto ptr = d_rrsetToSign.get();
     writen2(*rwVect.second.begin(), &ptr, sizeof(ptr));
+    // coverity[leaked_storage]
+    static_cast<void>(d_rrsetToSign.release());
     d_rrsetToSign = make_unique<rrset_t>();
     d_outstandings[*rwVect.second.begin()]++;
     d_outstanding++;
index dbec4af75ee3db3c7b5d9babc7c68d98ad44a5ae..380dbb09b3c98f7160a5e9eec4f9fada40fd9491 100644 (file)
@@ -7,6 +7,7 @@ extern "C" {
 #include <sodium.h>
 }
 #include "dnssecinfra.hh"
+#include "dnsseckeeper.hh"
 
 class SodiumED25519DNSCryptoKeyEngine : public DNSCryptoKeyEngine
 {
@@ -200,11 +201,11 @@ bool SodiumED25519DNSCryptoKeyEngine::verify(const std::string& msg, const std::
 }
 
 namespace {
-struct LoaderSodiumStruct
+const struct LoaderSodiumStruct
 {
   LoaderSodiumStruct()
   {
-    DNSCryptoKeyEngine::report(15, &SodiumED25519DNSCryptoKeyEngine::maker);
+    DNSCryptoKeyEngine::report(DNSSECKeeper::ED25519, &SodiumED25519DNSCryptoKeyEngine::maker);
   }
 } loadersodium;
 }
index deb87f6c22c75056fbe916d8b3f9859148e15465..f0d0140b93e194733acdecb00d20b771a0752ebf 100644 (file)
@@ -231,6 +231,80 @@ public:
 
 static LockGuarded <nsspeeds_t> s_nsSpeeds;
 
+template<class Thing> class Throttle : public boost::noncopyable
+{
+public:
+
+  struct entry_t
+  {
+    entry_t(const Thing& thing_, time_t ttd_, unsigned int count_) : thing(thing_), ttd(ttd_), count(count_)
+    {
+    }
+    Thing thing;
+    time_t ttd;
+    mutable unsigned int count;
+  };
+  typedef multi_index_container<entry_t,
+                                indexed_by<
+                                  ordered_unique<tag<Thing>, member<entry_t, Thing, &entry_t::thing>>,
+                                  ordered_non_unique<tag<time_t>, member<entry_t, time_t, &entry_t::ttd>>
+                                  >> cont_t;
+
+  bool shouldThrottle(time_t now, const Thing &t)
+  {
+    auto i = d_cont.find(t);
+    if (i == d_cont.end()) {
+      return false;
+    }
+    if (now > i->ttd || i->count == 0) {
+      d_cont.erase(i);
+      return false;
+    }
+    i->count--;
+
+    return true; // still listed, still blocked
+  }
+
+  void throttle(time_t now, const Thing &t, time_t ttl, unsigned int count)
+  {
+    auto i = d_cont.find(t);
+    time_t ttd = now + ttl;
+    if (i == d_cont.end()) {
+      d_cont.emplace(t, ttd, count);
+    } else if (ttd > i->ttd || count > i->count) {
+      ttd = std::max(i->ttd, ttd);
+      count = std::max(i->count, count);
+      auto &ind = d_cont.template get<Thing>();
+      ind.modify(i, [ttd,count](entry_t &e) { e.ttd = ttd; e.count = count; });
+    }
+  }
+
+  size_t size() const
+  {
+    return d_cont.size();
+  }
+
+  cont_t getThrottleMap() const
+  {
+    return d_cont;
+  }
+
+  void clear()
+  {
+    d_cont.clear();
+  }
+
+  void prune(time_t now) {
+    auto &ind = d_cont.template get<time_t>();
+    ind.erase(ind.begin(), ind.upper_bound(now));
+  }
+
+private:
+  cont_t d_cont;
+};
+
+static LockGuarded<Throttle<std::tuple<ComboAddress,DNSName,QType>>> s_throttle;
+
 struct SavedParentEntry
 {
   SavedParentEntry(const DNSName& name, map<DNSName, vector<ComboAddress>>&& nsAddresses, time_t ttd)
@@ -1036,6 +1110,41 @@ uint64_t SyncRes::doDumpNSSpeeds(int fd)
   return count;
 }
 
+uint64_t SyncRes::getThrottledServersSize()
+{
+  return s_throttle.lock()->size();
+}
+
+void SyncRes::pruneThrottledServers(time_t now)
+{
+  s_throttle.lock()->prune(now);
+}
+
+void SyncRes::clearThrottle()
+{
+  s_throttle.lock()->clear();
+}
+
+bool SyncRes::isThrottled(time_t now, const ComboAddress& server, const DNSName& target, QType qtype)
+{
+  return s_throttle.lock()->shouldThrottle(now, std::make_tuple(server, target, qtype));
+}
+
+bool SyncRes::isThrottled(time_t now, const ComboAddress& server)
+{
+  return s_throttle.lock()->shouldThrottle(now, std::make_tuple(server, g_rootdnsname, 0));
+}
+
+void SyncRes::doThrottle(time_t now, const ComboAddress& server, time_t duration, unsigned int tries)
+{
+  s_throttle.lock()->throttle(now, std::make_tuple(server, g_rootdnsname, 0), duration, tries);
+}
+
+void SyncRes::doThrottle(time_t now, const ComboAddress& server, const DNSName&name, QType qtype, time_t duration, unsigned int tries)
+{
+  s_throttle.lock()->throttle(now, std::make_tuple(server, name, qtype), duration, tries);
+}
+
 uint64_t SyncRes::doDumpThrottleMap(int fd)
 {
   int newfd = dup(fd);
@@ -1051,13 +1160,14 @@ uint64_t SyncRes::doDumpThrottleMap(int fd)
   fprintf(fp.get(), "; remote IP\tqname\tqtype\tcount\tttd\n");
   uint64_t count=0;
 
-  const auto& throttleMap = t_sstorage.throttle.getThrottleMap();
+  // Get a copy to avoid holding the lock while doing I/O
+  const auto throttleMap = s_throttle.lock()->getThrottleMap();
   for(const auto& i : throttleMap)
   {
     count++;
     char tmp[26];
     // remote IP, dns name, qtype, count, ttd
-    fprintf(fp.get(), "%s\t%s\t%d\t%u\t%s\n", std::get<0>(i.thing).toString().c_str(), std::get<1>(i.thing).toLogString().c_str(), std::get<2>(i.thing), i.count, timestamp(i.ttd, tmp, sizeof(tmp)));
+    fprintf(fp.get(), "%s\t%s\t%s\t%u\t%s\n", std::get<0>(i.thing).toString().c_str(), std::get<1>(i.thing).toLogString().c_str(), std::get<2>(i.thing).toString().c_str(), i.count, timestamp(i.ttd, tmp, sizeof(tmp)));
   }
 
   return count;
@@ -3126,12 +3236,12 @@ vector<ComboAddress> SyncRes::retrieveAddressesForNS(const std::string& prefix,
 
 bool SyncRes::throttledOrBlocked(const std::string& prefix, const ComboAddress& remoteIP, const DNSName& qname, const QType qtype, bool pierceDontQuery)
 {
-  if(t_sstorage.throttle.shouldThrottle(d_now.tv_sec, std::make_tuple(remoteIP, g_rootdnsname, 0))) {
+  if (isThrottled(d_now.tv_sec, remoteIP)) {
     LOG(prefix<<qname<<": server throttled "<<endl);
     s_throttledqueries++; d_throttledqueries++;
     return true;
   }
-  else if(t_sstorage.throttle.shouldThrottle(d_now.tv_sec, std::make_tuple(remoteIP, qname, qtype.getCode()))) {
+  else if (isThrottled(d_now.tv_sec, remoteIP, qname, qtype)) {
     LOG(prefix<<qname<<": query throttled "<<remoteIP.toString()<<", "<<qname<<"; "<<qtype<<endl);
     s_throttledqueries++; d_throttledqueries++;
     return true;
@@ -4950,15 +5060,15 @@ bool SyncRes::doResolveAtThisIP(const std::string& prefix, const DNSName& qname,
       if (s_serverdownmaxfails > 0 && (auth != g_rootdnsname) && s_fails.lock()->incr(remoteIP, d_now) >= s_serverdownmaxfails) {
         LOG(prefix<<qname<<": Max fails reached resolving on "<< remoteIP.toString() <<". Going full throttle for "<< s_serverdownthrottletime <<" seconds" <<endl);
         // mark server as down
-        t_sstorage.throttle.throttle(d_now.tv_sec, std::make_tuple(remoteIP, g_rootdnsname, 0), s_serverdownthrottletime, 10000);
+        doThrottle(d_now.tv_sec, remoteIP, s_serverdownthrottletime, 10000);
       }
       else if (resolveret == LWResult::Result::PermanentError) {
         // unreachable, 1 minute or 100 queries
-        t_sstorage.throttle.throttle(d_now.tv_sec, std::make_tuple(remoteIP, qname, qtype.getCode()), 60, 100);
+        doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 60, 100);
       }
       else {
         // timeout, 10 seconds or 5 queries
-        t_sstorage.throttle.throttle(d_now.tv_sec, std::make_tuple(remoteIP, qname, qtype.getCode()), 10, 5);
+       doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 10, 5);
       }
     }
 
@@ -4974,10 +5084,10 @@ bool SyncRes::doResolveAtThisIP(const std::string& prefix, const DNSName& qname,
 
       if (doTCP) {
         // we can be more heavy-handed over TCP
-        t_sstorage.throttle.throttle(d_now.tv_sec, std::make_tuple(remoteIP, qname, qtype.getCode()), 60, 10);
+        doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 60, 10);
       }
       else {
-        t_sstorage.throttle.throttle(d_now.tv_sec, std::make_tuple(remoteIP, qname, qtype.getCode()), 10, 2);
+        doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 10, 2);
       }
     }
     return false;
@@ -4994,7 +5104,7 @@ bool SyncRes::doResolveAtThisIP(const std::string& prefix, const DNSName& qname,
           s_nsSpeeds.lock()->find_or_enter(nsName.empty()? DNSName(remoteIP.toStringWithPort()) : nsName, d_now).submit(remoteIP, 1000000, d_now); // 1 sec
         }
         else {
-          t_sstorage.throttle.throttle(d_now.tv_sec, std::make_tuple(remoteIP, qname, qtype.getCode()), 60, 3);
+          doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 60, 3);
         }
       }
       return false;
@@ -5013,7 +5123,7 @@ bool SyncRes::doResolveAtThisIP(const std::string& prefix, const DNSName& qname,
       LOG(prefix<<qname<<": truncated bit set, over TCP?"<<endl);
       if (!dontThrottle) {
         /* let's treat that as a ServFail answer from this server */
-        t_sstorage.throttle.throttle(d_now.tv_sec, std::make_tuple(remoteIP, qname, qtype.getCode()), 60, 3);
+        doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 60, 3);
       }
       return false;
     }
@@ -5425,7 +5535,7 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con
             break;
           }
           /* was lame */
-          t_sstorage.throttle.throttle(d_now.tv_sec, std::make_tuple(*remoteIP, qname, qtype.getCode()), 60, 100);
+          doThrottle(d_now.tv_sec, *remoteIP, qname, qtype, 60, 100);
         }
 
         if (gotNewServers) {
index 11afeb1ef8900eb577e20603d396b6ba42fac26e..dd54b46ecbdc5496da312e89e3e010dc44ecb62c 100644 (file)
@@ -79,76 +79,6 @@ typedef std::unordered_map<
   >
 > NsSet;
 
-template<class Thing> class Throttle : public boost::noncopyable
-{
-public:
-
-  struct entry_t
-  {
-    Thing thing;
-    time_t ttd;
-    mutable unsigned int count;
-  };
-  typedef multi_index_container<entry_t,
-                                indexed_by<
-                                  ordered_unique<tag<Thing>, member<entry_t, Thing, &entry_t::thing>>,
-                                  ordered_non_unique<tag<time_t>, member<entry_t, time_t, &entry_t::ttd>>
-                                  >> cont_t;
-
-  bool shouldThrottle(time_t now, const Thing &t)
-  {
-    auto i = d_cont.find(t);
-    if (i == d_cont.end()) {
-      return false;
-    }
-    if (now > i->ttd || i->count == 0) {
-      d_cont.erase(i);
-      return false;
-    }
-    i->count--;
-
-    return true; // still listed, still blocked
-  }
-
-  void throttle(time_t now, const Thing &t, time_t ttl, unsigned int count)
-  {
-    auto i = d_cont.find(t);
-    time_t ttd = now + ttl;
-    if (i == d_cont.end()) {
-      entry_t e = { t, ttd, count };
-      d_cont.insert(e);
-    } else if (ttd > i->ttd || count > i->count) {
-      ttd = std::max(i->ttd, ttd);
-      count = std::max(i->count, count);
-      auto &ind = d_cont.template get<Thing>();
-      ind.modify(i, [ttd,count](entry_t &e) { e.ttd = ttd; e.count = count; });
-    }
-  }
-
-  size_t size() const
-  {
-    return d_cont.size();
-  }
-
-  const cont_t &getThrottleMap() const
-  {
-    return d_cont;
-  }
-
-  void clear()
-  {
-    d_cont.clear();
-  }
-
-  void prune() {
-    time_t now = time(nullptr);
-    auto &ind = d_cont.template get<time_t>();
-    ind.erase(ind.begin(), ind.upper_bound(now));
-  }
-
-private:
-  cont_t d_cont;
-};
 
 extern std::unique_ptr<NegCache> g_negCache;
 
@@ -206,7 +136,6 @@ public:
   };
 
   typedef std::unordered_map<DNSName, AuthDomain> domainmap_t;
-  typedef Throttle<std::tuple<ComboAddress,DNSName,uint16_t> > throttle_t;
 
   struct EDNSStatus {
     EDNSStatus(const ComboAddress &arg) : address(arg) {}
@@ -238,7 +167,6 @@ public:
   };
 
   struct ThreadLocalStorage {
-    throttle_t throttle;
     ednsstatus_t ednsstatus;
     std::shared_ptr<domainmap_t> domainmap;
   };
@@ -327,30 +255,14 @@ public:
   {
     t_sstorage.ednsstatus.prune(cutoff);
   }
-  static uint64_t getThrottledServersSize()
-  {
-    return t_sstorage.throttle.size();
-  }
-  static void pruneThrottledServers()
-  {
-    t_sstorage.throttle.prune();
-  }
-  static void clearThrottle()
-  {
-    t_sstorage.throttle.clear();
-  }
-  static bool isThrottled(time_t now, const ComboAddress& server, const DNSName& target, uint16_t qtype)
-  {
-    return t_sstorage.throttle.shouldThrottle(now, std::make_tuple(server, target, qtype));
-  }
-  static bool isThrottled(time_t now, const ComboAddress& server)
-  {
-    return t_sstorage.throttle.shouldThrottle(now, std::make_tuple(server, g_rootdnsname, 0));
-  }
-  static void doThrottle(time_t now, const ComboAddress& server, time_t duration, unsigned int tries)
-  {
-    t_sstorage.throttle.throttle(now, std::make_tuple(server, g_rootdnsname, 0), duration, tries);
-  }
+
+  static uint64_t getThrottledServersSize();
+  static void pruneThrottledServers(time_t now);
+  static void clearThrottle();
+  static bool isThrottled(time_t now, const ComboAddress& server, const DNSName& target, QType qtype);
+  static bool isThrottled(time_t now, const ComboAddress& server);
+  static void doThrottle(time_t now, const ComboAddress& server, time_t duration, unsigned int tries);
+  static void doThrottle(time_t now, const ComboAddress& server, const DNSName& name, QType qtype, time_t duration, unsigned int tries);
 
   static uint64_t getFailedServersSize();
   static void clearFailedServers();
index d087da0c14e4c3c2d758dced69b86275d0b7722e..f491ee8fe3a3a7e5c8ae28782994171626c8a962 100644 (file)
 #include "misc.hh"
 
 #include <cstdio>
+#include <unordered_map>
 
 BOOST_AUTO_TEST_SUITE(test_signers)
 
-static const std::string message = "Very good, young padawan.";
-
 struct SignerParams
 {
   std::string iscMap;
@@ -34,130 +33,270 @@ struct SignerParams
   uint16_t rfcFlags;
   uint8_t algorithm;
   bool isDeterministic;
-  std::optional<std::string> pem;
+  std::string pem;
 };
 
-static const std::array<struct SignerParams, 3> signers
+static const SignerParams rsaSha256SignerParams = SignerParams{
+  .iscMap = "Algorithm: 8\n"
+            "Modulus: qtunSiHnYq4XRLBehKAw1Glxb+48oIpAC7w3Jhpj570bb2uHt6orWGqnuyRtK8oqUi2ABoV0PFm8+IPgDMEdCQ==\n"
+            "PublicExponent: AQAB\n"
+            "PrivateExponent: MiItniUAngXzMeaGdWgDq/AcpvlCtOCcFlVt4TJRKkfp8DNRSxIxG53NNlOFkp1W00iLHqYC2GrH1qkKgT9l+Q==\n"
+            "Prime1: 3sZmM+5FKFy5xaRt0n2ZQOZ2C+CoKzVil6/al9LmYVs=\n"
+            "Prime2: xFcNWSIW6v8dDL2JQ1kxFDm/8RVeUSs1BNXXnvCjBGs=\n"
+            "Exponent1: WuUwhjfN1+4djlrMxHmisixWNfpwI1Eg7Ss/UXsnrMk=\n"
+            "Exponent2: vfMqas1cNsXRqP3Fym6D2Pl2BRuTQBv5E1B/ZrmQPTk=\n"
+            "Coefficient: Q10z43cA3hkwOkKsj5T0W5jrX97LBwZoY5lIjDCa4+M=\n",
+
+  .dsSHA1 = "1506 8 1 "
+            "172a500b374158d1a64ba3073cdbbc319b2fdf2c",
+
+  .dsSHA256 = "1506 8 2 "
+              "253b099ff47b02c6ffa52695a30a94c6681c56befe0e71a5077d6f79514972f9",
+
+  .dsSHA384 = "1506 8 4 "
+              "22ea940600dc2d9a98b1126c26ac0dc5c91b31eb50fe784b"
+              "36ad675e9eecfe6573c1f85c53b6bc94580f3ac443d13c4c",
+
+  // clang-format off
+  /* from https://github.com/CZ-NIC/knot/blob/master/src/dnssec/tests/sign.c */
+  .signature = {
+    0x93, 0x93, 0x5f, 0xd8, 0xa1, 0x2b, 0x4c, 0x0b, 0xf3, 0x67, 0x42, 0x13, 0x52,
+    0x00, 0x35, 0xdc, 0x09, 0xe0, 0xdf, 0xe0, 0x3e, 0xc2, 0xcf, 0x64, 0xab, 0x9f,
+    0x9f, 0x51, 0x5f, 0x5c, 0x27, 0xbe, 0x13, 0xd6, 0x17, 0x07, 0xa6, 0xe4, 0x3b,
+    0x63, 0x44, 0x85, 0x06, 0x13, 0xaa, 0x01, 0x3c, 0x58, 0x52, 0xa3, 0x98, 0x20,
+    0x65, 0x03, 0xd0, 0x40, 0xc8, 0xa0, 0xe9, 0xd2, 0xc0, 0x03, 0x5a, 0xab
+  },
+  // clang-format on
+
+  .zoneRepresentation = "256 3 8 "
+                        "AwEAAarbp0oh52KuF0SwXoSgMNRpcW/uPKCKQAu8NyYaY+"
+                        "e9G29rh7eqK1hqp7skbSvKKlItgAaFdDxZvPiD4AzBHQk=",
+
+  .name = "rsa.",
+
+  .rfcMsgDump = "",
+  .rfcB64Signature = "",
+
+  .bits = 512,
+  .flags = 256,
+  .rfcFlags = 0,
+
+  .algorithm = DNSSECKeeper::RSASHA256,
+  .isDeterministic = true,
+
+  .pem = "-----BEGIN RSA PRIVATE KEY-----\n"
+         "MIIBOgIBAAJBAKrbp0oh52KuF0SwXoSgMNRpcW/uPKCKQAu8NyYaY+e9G29rh7eq\n"
+         "K1hqp7skbSvKKlItgAaFdDxZvPiD4AzBHQkCAwEAAQJAMiItniUAngXzMeaGdWgD\n"
+         "q/AcpvlCtOCcFlVt4TJRKkfp8DNRSxIxG53NNlOFkp1W00iLHqYC2GrH1qkKgT9l\n"
+         "+QIhAN7GZjPuRShcucWkbdJ9mUDmdgvgqCs1Ypev2pfS5mFbAiEAxFcNWSIW6v8d\n"
+         "DL2JQ1kxFDm/8RVeUSs1BNXXnvCjBGsCIFrlMIY3zdfuHY5azMR5orIsVjX6cCNR\n"
+         "IO0rP1F7J6zJAiEAvfMqas1cNsXRqP3Fym6D2Pl2BRuTQBv5E1B/ZrmQPTkCIENd\n"
+         "M+N3AN4ZMDpCrI+U9FuY61/eywcGaGOZSIwwmuPj\n"
+         "-----END RSA PRIVATE KEY-----\n"};
+
+/* ECDSA-P256-SHA256 from
+ * https://github.com/CZ-NIC/knot/blob/master/src/dnssec/tests/sample_keys.h
+ */
+static const SignerParams ecdsaSha256 = SignerParams{
+  .iscMap = "Algorithm: 13\n"
+            "PrivateKey: iyLIPdk3DOIxVmmSYlmTstbtUPiVlEyDX46psyCwNVQ=\n",
+
+  .dsSHA1 = "5345 13 1 "
+            "954103ac7c43810ce9f414e80f30ab1cbe49b236",
+
+  .dsSHA256 = "5345 13 2 "
+              "bac2107036e735b50f85006ce409a19a3438cab272e70769ebda032239a3d0ca",
+
+  .dsSHA384 = "5345 13 4 "
+              "a0ac6790483872be72a258314200a88ab75cdd70f66a18a0"
+              "9f0f414c074df0989fdb1df0e67d82d4312cda67b93a76c1",
+
+  // clang-format off
+  /* from https://github.com/CZ-NIC/knot/blob/master/src/dnssec/tests/sign.c */
+  .signature = {
+    0xa2, 0x95, 0x76, 0xb5, 0xf5, 0x7e, 0xbd, 0xdd, 0xf5, 0x62, 0xa2, 0xc3, 0xa4,
+    0x8d, 0xd4, 0x53, 0x5c, 0xba, 0x29, 0x71, 0x8c, 0xcc, 0x28, 0x7b, 0x58, 0xf3,
+    0x1e, 0x4e, 0x58, 0xe2, 0x36, 0x7e, 0xa0, 0x1a, 0xb6, 0xe6, 0x29, 0x71, 0x1b,
+    0xd3, 0x8c, 0x88, 0xc3, 0xee, 0x12, 0x0e, 0x69, 0x70, 0x55, 0x99, 0xec, 0xd5,
+    0xf6, 0x4f, 0x4b, 0xe2, 0x41, 0xd9, 0x10, 0x7e, 0x67, 0xe5, 0xad, 0x2f
+  },
+  // clang-format on
+
+  .zoneRepresentation = "256 3 13 "
+                        "8uD7C4THTM/w7uhryRSToeE/jKT78/p853RX0L5EwrZ"
+                        "rSLBubLPiBw7gbvUP6SsIga5ZQ4CSAxNmYA/gZsuXzA==",
+
+  .name = "ecdsa.",
+
+  .rfcMsgDump = "",
+  .rfcB64Signature = "",
+
+  .bits = 256,
+  .flags = 256,
+  .rfcFlags = 0,
+
+  .algorithm = DNSSECKeeper::ECDSA256,
+  .isDeterministic = false,
+
+  .pem = "-----BEGIN EC PRIVATE KEY-----\n"
+         "MHcCAQEEIIsiyD3ZNwziMVZpkmJZk7LW7VD4lZRMg1+OqbMgsDVUoAoGCCqGSM49\n"
+         "AwEHoUQDQgAE8uD7C4THTM/w7uhryRSToeE/jKT78/p853RX0L5EwrZrSLBubLPi\n"
+         "Bw7gbvUP6SsIga5ZQ4CSAxNmYA/gZsuXzA==\n"
+         "-----END EC PRIVATE KEY-----\n"};
+
+/* Ed25519 from https://github.com/CZ-NIC/knot/blob/master/src/dnssec/tests/sample_keys.h,
+ * also from rfc8080 section 6.1
+ */
+static const SignerParams ed25519 = SignerParams{
+  .iscMap = "Algorithm: 15\n"
+            "PrivateKey: ODIyNjAzODQ2MjgwODAxMjI2NDUxOTAyMDQxNDIyNjI=\n",
+
+  .dsSHA1 = "3612 15 1 "
+            "501249721e1f09a79d30d5c6c4dca1dc1da4ed5d",
+
+  .dsSHA256 = "3612 15 2 "
+              "1b1c8766b2a96566ff196f77c0c4194af86aaa109c5346ff60231a27d2b07ac0",
+
+  .dsSHA384 = "3612 15 4 "
+              "d11831153af4985efbd0ae792c967eb4aff3c35488db95f7"
+              "e2f85dcec74ae8f59f9a72641798c91c67c675db1d710c18",
+
+  // clang-format off
+  /* from https://github.com/CZ-NIC/knot/blob/master/src/dnssec/tests/sign.c */
+  .signature = {
+    0x0a, 0x9e, 0x51, 0x5f, 0x16, 0x89, 0x49, 0x27, 0x0e, 0x98, 0x34, 0xd3, 0x48,
+    0xef, 0x5a, 0x6e, 0x85, 0x2f, 0x7c, 0xd6, 0xd7, 0xc8, 0xd0, 0xf4, 0x2c, 0x68,
+    0x8c, 0x1f, 0xf7, 0xdf, 0xeb, 0x7c, 0x25, 0xd6, 0x1a, 0x76, 0x3e, 0xaf, 0x28,
+    0x1f, 0x1d, 0x08, 0x10, 0x20, 0x1c, 0x01, 0x77, 0x1b, 0x5a, 0x48, 0xd6, 0xe5,
+    0x1c, 0xf9, 0xe3, 0xe0, 0x70, 0x34, 0x5e, 0x02, 0x49, 0xfb, 0x9e, 0x05
+  },
+  // clang-format on
+
+  .zoneRepresentation = "256 3 15 l02Woi0iS8Aa25FQkUd9RMzZHJpBoRQwAQEX1SxZJA4=",
+
+  .name = "ed25519.",
+
+  // vector extracted from https://gitlab.labs.nic.cz/labs/ietf/blob/master/dnskey.py (rev
+  // 476d6ded) by printing signature_data
+  .rfcMsgDump = "00 0f 0f 02 00 00 0e 10 55 d4 fc 60 55 b9 4c e0 0e 1d 07 65 78 "
+                "61 6d 70 6c 65 03 63 6f 6d 00 07 65 78 61 6d 70 6c 65 03 63 6f "
+                "6d 00 00 0f 00 01 00 00 0e 10 00 14 00 0a 04 6d 61 69 6c 07 65 "
+                "78 61 6d 70 6c 65 03 63 6f 6d 00 ",
+
+  // vector verified from dnskey.py as above, and confirmed with
+  // https://www.rfc-editor.org/errata_search.php?rfc=8080&eid=4935
+  .rfcB64Signature = "oL9krJun7xfBOIWcGHi7mag5/hdZrKWw15jPGrHpjQeR"
+                     "AvTdszaPD+QLs3fx8A4M3e23mRZ9VrbpMngwcrqNAg==",
+
+  .bits = 256,
+  .flags = 256,
+  .rfcFlags = 257,
+
+  .algorithm = DNSSECKeeper::ED25519,
+  .isDeterministic = true,
+
+  .pem = "-----BEGIN PRIVATE KEY-----\n"
+         "MC4CAQAwBQYDK2VwBCIEIDgyMjYwMzg0NjI4MDgwMTIyNjQ1MTkwMjA0MTQyMjYy\n"
+         "-----END PRIVATE KEY-----\n"};
+
+/* Ed448.
+ */
+static const SignerParams ed448 = SignerParams{
+  .iscMap = "Private-key-format: v1.2\n"
+            "Algorithm: 16 (ED448)\n"
+            "PrivateKey: xZ+5Cgm463xugtkY5B0Jx6erFTXp13rYegst0qRtNsOYnaVpMx0Z/c5EiA9x8wWbDDct/U3FhYWA\n",
+
+  .dsSHA1 = "9712 16 1 "
+            "2873e800eb2d784cdd1802f884b3c540b573eaa0",
+
+  .dsSHA256 = "9712 16 2 "
+              "9aa27306f8a04a0a6fae8affd65d6f35875dcb134c05bd7c7b61bd0dc44009cd",
+
+  .dsSHA384 = "9712 16 4 "
+              "3876e5d892d3f31725f9964a332f9b9afd791171833480f2"
+              "e71af78efb985cde9900ba95315287123a5908ca8f334369",
+
+  // clang-format off
+  .signature = {
+    0xb5, 0xcc, 0x21, 0x5a, 0x52, 0x21, 0x60, 0xa3, 0xb8, 0xd9, 0x3a, 0xd7, 0x05,
+    0xdd, 0x4a, 0x32, 0x96, 0xce, 0x08, 0xde, 0x74, 0x5f, 0xdb, 0xde, 0x54, 0x95,
+    0x97, 0x93, 0x6f, 0x3a, 0x4a, 0x34, 0x41, 0x14, 0xba, 0x99, 0x86, 0x0d, 0xe2,
+    0x99, 0xf1, 0x14, 0x6a, 0x1b, 0x7a, 0xfa, 0xef, 0xab, 0x62, 0xd2, 0x71, 0x85,
+    0xae, 0xd1, 0x84, 0x80, 0x00, 0x50, 0x03, 0x9e, 0x73, 0x53, 0xe8, 0x9e, 0x19,
+    0xb8, 0xc0, 0xdb, 0xd4, 0xf0, 0x1e, 0x44, 0x4c, 0xb7, 0x32, 0x07, 0xda, 0x0b,
+    0x64, 0x22, 0xa8, 0x63, 0xaa, 0x7a, 0x12, 0x73, 0xc9, 0x29, 0xfd, 0x50, 0x85,
+    0x0f, 0x43, 0x72, 0x77, 0x86, 0xec, 0x88, 0x1a, 0x96, 0x95, 0x4a, 0x01, 0xfe,
+    0xf2, 0xe6, 0x77, 0x4a, 0x2e, 0x43, 0xdd, 0x60, 0x29, 0x00,
+  },
+  // clang-format on
+
+  .zoneRepresentation = "256 3 16 "
+                        "3kgROaDjrh0H2iuixWBrc8g2EpBBLCdGzHmn+"
+                        "G2MpTPhpj/OiBVHHSfPodx1FYYUcJKm1MDpJtIA",
+
+  .name = "ed448.",
+
+  // vector extracted from https://gitlab.labs.nic.cz/labs/ietf/blob/master/dnskey.py (rev
+  // 476d6ded) by printing signature_data
+  .rfcMsgDump = "00 0f 10 02 00 00 0e 10 55 d4 fc 60 55 b9 4c e0 25 f1 07 65 78 "
+                "61 6d 70 6c 65 03 63 6f 6d 00 07 65 78 61 6d 70 6c 65 03 63 6f "
+                "6d 00 00 0f 00 01 00 00 0e 10 00 14 00 0a 04 6d 61 69 6c 07 65 "
+                "78 61 6d 70 6c 65 03 63 6f 6d 00 ",
+
+  // vector verified from dnskey.py as above, and confirmed with
+  // https://www.rfc-editor.org/errata_search.php?rfc=8080&eid=4935
+  .rfcB64Signature = "3cPAHkmlnxcDHMyg7vFC34l0blBhuG1qpwLmjInI8w1CMB29FkEA"
+                     "IJUA0amxWndkmnBZ6SKiwZSAxGILn/NBtOXft0+Gj7FSvOKxE/07"
+                     "+4RQvE581N3Aj/JtIyaiYVdnYtyMWbSNyGEY2213WKsJlwEA",
+
+  .bits = 456,
+  .flags = 256,
+  .rfcFlags = 257,
+
+  .algorithm = DNSSECKeeper::ED448,
+  .isDeterministic = true,
+
+  .pem = "-----BEGIN PRIVATE KEY-----\n"
+         "MEcCAQAwBQYDK2VxBDsEOcWfuQoJuOt8boLZGOQdCcenqxU16dd62HoLLdKkbTbD\n"
+         "mJ2laTMdGf3ORIgPcfMFmww3Lf1NxYWFgA==\n"
+         "-----END PRIVATE KEY-----\n"};
+
+struct Fixture
 {
-  /* RSA from https://github.com/CZ-NIC/knot/blob/master/src/dnssec/tests/sample_keys.h */
-  SignerParams{
-    "Algorithm: 8\n"
-    "Modulus: qtunSiHnYq4XRLBehKAw1Glxb+48oIpAC7w3Jhpj570bb2uHt6orWGqnuyRtK8oqUi2ABoV0PFm8+IPgDMEdCQ==\n"
-    "PublicExponent: AQAB\n"
-    "PrivateExponent: MiItniUAngXzMeaGdWgDq/AcpvlCtOCcFlVt4TJRKkfp8DNRSxIxG53NNlOFkp1W00iLHqYC2GrH1qkKgT9l+Q==\n"
-    "Prime1: 3sZmM+5FKFy5xaRt0n2ZQOZ2C+CoKzVil6/al9LmYVs=\n"
-    "Prime2: xFcNWSIW6v8dDL2JQ1kxFDm/8RVeUSs1BNXXnvCjBGs=\n"
-    "Exponent1: WuUwhjfN1+4djlrMxHmisixWNfpwI1Eg7Ss/UXsnrMk=\n"
-    "Exponent2: vfMqas1cNsXRqP3Fym6D2Pl2BRuTQBv5E1B/ZrmQPTk=\n"
-    "Coefficient: Q10z43cA3hkwOkKsj5T0W5jrX97LBwZoY5lIjDCa4+M=\n",
-
-    "1506 8 1 172a500b374158d1a64ba3073cdbbc319b2fdf2c",
-    "1506 8 2 253b099ff47b02c6ffa52695a30a94c6681c56befe0e71a5077d6f79514972f9",
-    "1506 8 4 22ea940600dc2d9a98b1126c26ac0dc5c91b31eb50fe784b36ad675e9eecfe6573c1f85c53b6bc94580f3ac443d13c4c",
-
-    // clang-format off
-    /* from https://github.com/CZ-NIC/knot/blob/master/src/dnssec/tests/sign.c */
-    { 0x93, 0x93, 0x5f, 0xd8, 0xa1, 0x2b, 0x4c, 0x0b, 0xf3, 0x67, 0x42, 0x13, 0x52, 0x00, 0x35, 0xdc,
-      0x09, 0xe0, 0xdf, 0xe0, 0x3e, 0xc2, 0xcf, 0x64, 0xab, 0x9f, 0x9f, 0x51, 0x5f, 0x5c, 0x27, 0xbe,
-      0x13, 0xd6, 0x17, 0x07, 0xa6, 0xe4, 0x3b, 0x63, 0x44, 0x85, 0x06, 0x13, 0xaa, 0x01, 0x3c, 0x58,
-      0x52, 0xa3, 0x98, 0x20, 0x65, 0x03, 0xd0, 0x40, 0xc8, 0xa0, 0xe9, 0xd2, 0xc0, 0x03, 0x5a, 0xab },
-    // clang-format on
-
-    "256 3 8 AwEAAarbp0oh52KuF0SwXoSgMNRpcW/uPKCKQAu8NyYaY+e9G29rh7eqK1hqp7skbSvKKlItgAaFdDxZvPiD4AzBHQk=",
-    "rsa.",
-    "",
-    "",
-    512,
-    256,
-    0,
-    DNSSECKeeper::RSASHA256,
-    true,
-
-    std::nullopt},
-
-#ifdef HAVE_LIBCRYPTO_ECDSA
-    /* ECDSA-P256-SHA256 from https://github.com/CZ-NIC/knot/blob/master/src/dnssec/tests/sample_keys.h */
-    SignerParams{
-      "Algorithm: 13\n"
-      "PrivateKey: iyLIPdk3DOIxVmmSYlmTstbtUPiVlEyDX46psyCwNVQ=\n",
-
-      "5345 13 1 954103ac7c43810ce9f414e80f30ab1cbe49b236",
-      "5345 13 2 bac2107036e735b50f85006ce409a19a3438cab272e70769ebda032239a3d0ca",
-      "5345 13 4 a0ac6790483872be72a258314200a88ab75cdd70f66a18a09f0f414c074df0989fdb1df0e67d82d4312cda67b93a76c1",
-
-      // clang-format off
-      /* from https://github.com/CZ-NIC/knot/blob/master/src/dnssec/tests/sign.c */
-      { 0xa2, 0x95, 0x76, 0xb5, 0xf5, 0x7e, 0xbd, 0xdd, 0xf5, 0x62, 0xa2, 0xc3, 0xa4, 0x8d, 0xd4, 0x53,
-        0x5c, 0xba, 0x29, 0x71, 0x8c, 0xcc, 0x28, 0x7b, 0x58, 0xf3, 0x1e, 0x4e, 0x58, 0xe2, 0x36, 0x7e,
-        0xa0, 0x1a, 0xb6, 0xe6, 0x29, 0x71, 0x1b, 0xd3, 0x8c, 0x88, 0xc3, 0xee, 0x12, 0x0e, 0x69, 0x70,
-        0x55, 0x99, 0xec, 0xd5, 0xf6, 0x4f, 0x4b, 0xe2, 0x41, 0xd9, 0x10, 0x7e, 0x67, 0xe5, 0xad, 0x2f },
-      // clang-format on
-
-      "256 3 13 8uD7C4THTM/w7uhryRSToeE/jKT78/p853RX0L5EwrZrSLBubLPiBw7gbvUP6SsIga5ZQ4CSAxNmYA/gZsuXzA==",
-      "ecdsa.",
-      "",
-      "",
-      256,
-      256,
-      0,
-      DNSSECKeeper::ECDSA256,
-      false,
-
-      std::make_optional(std::string{
-        "-----BEGIN EC PRIVATE KEY-----\n"
-        "MHcCAQEEIIsiyD3ZNwziMVZpkmJZk7LW7VD4lZRMg1+OqbMgsDVUoAoGCCqGSM49\n"
-        "AwEHoUQDQgAE8uD7C4THTM/w7uhryRSToeE/jKT78/p853RX0L5EwrZrSLBubLPi\n"
-        "Bw7gbvUP6SsIga5ZQ4CSAxNmYA/gZsuXzA==\n"
-        "-----END EC PRIVATE KEY-----\n"})},
-#endif /* HAVE_LIBCRYPTO_ECDSA */
-
-#if defined(HAVE_LIBSODIUM) || defined(HAVE_LIBDECAF) || defined(HAVE_LIBCRYPTO_ED25519)
-    /* ed25519 from https://github.com/CZ-NIC/knot/blob/master/src/dnssec/tests/sample_keys.h,
-       also from rfc8080 section 6.1 */
-    SignerParams{
-      "Algorithm: 15\n"
-      "PrivateKey: ODIyNjAzODQ2MjgwODAxMjI2NDUxOTAyMDQxNDIyNjI=\n",
-
-      "3612 15 1 501249721e1f09a79d30d5c6c4dca1dc1da4ed5d",
-      "3612 15 2 1b1c8766b2a96566ff196f77c0c4194af86aaa109c5346ff60231a27d2b07ac0",
-      "3612 15 4 d11831153af4985efbd0ae792c967eb4aff3c35488db95f7e2f85dcec74ae8f59f9a72641798c91c67c675db1d710c18",
-
-      // clang-format off
-      /* from https://github.com/CZ-NIC/knot/blob/master/src/dnssec/tests/sign.c */
-      { 0x0a, 0x9e, 0x51, 0x5f, 0x16, 0x89, 0x49, 0x27, 0x0e, 0x98, 0x34, 0xd3, 0x48, 0xef, 0x5a, 0x6e,
-        0x85, 0x2f, 0x7c, 0xd6, 0xd7, 0xc8, 0xd0, 0xf4, 0x2c, 0x68, 0x8c, 0x1f, 0xf7, 0xdf, 0xeb, 0x7c,
-        0x25, 0xd6, 0x1a, 0x76, 0x3e, 0xaf, 0x28, 0x1f, 0x1d, 0x08, 0x10, 0x20, 0x1c, 0x01, 0x77, 0x1b,
-        0x5a, 0x48, 0xd6, 0xe5, 0x1c, 0xf9, 0xe3, 0xe0, 0x70, 0x34, 0x5e, 0x02, 0x49, 0xfb, 0x9e, 0x05 },
-      // clang-format on
-
-      "256 3 15 l02Woi0iS8Aa25FQkUd9RMzZHJpBoRQwAQEX1SxZJA4=",
-      "ed25519.",
-
-      // vector extracted from https://gitlab.labs.nic.cz/labs/ietf/blob/master/dnskey.py
-      // (rev 476d6ded) by printing signature_data
-      "00 0f 0f 02 00 00 0e 10 55 d4 fc 60 55 b9 4c e0 0e 1d 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 "
-      "07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 00 0f 00 01 00 00 0e 10 00 14 00 0a 04 6d 61 69 6c 07 "
-      "65 78 61 6d 70 6c 65 03 63 6f 6d 00 ",
-
-      // vector verified from dnskey.py as above, and confirmed with
-      // https://www.rfc-editor.org/errata_search.php?rfc=8080&eid=4935
-      "oL9krJun7xfBOIWcGHi7mag5/hdZrKWw15jPGrHpjQeRAvTdszaPD+QLs3fx8A4M3e23mRZ9VrbpMngwcrqNAg==",
-
-      256,
-      256,
-      257,
-      DNSSECKeeper::ED25519,
-      true,
-
-#if defined(HAVE_LIBCRYPTO_ED25519)
-      std::make_optional(std::string{
-        "-----BEGIN PRIVATE KEY-----\n"
-        "MC4CAQAwBQYDK2VwBCIEIDgyMjYwMzg0NjI4MDgwMTIyNjQ1MTkwMjA0MTQyMjYy\n"
-        "-----END PRIVATE KEY-----\n"})},
-#else
-      std::nullopt},
-#endif /* defined(HAVE_LIBCRYPTO_ED25519) */
-#endif /* defined(HAVE_LIBSODIUM) || defined(HAVE_LIBDECAF) || defined(HAVE_LIBCRYPTO_ED25519) */
+  Fixture()
+  {
+    BOOST_TEST_MESSAGE("All available/supported algorithms:");
+    auto pairs = DNSCryptoKeyEngine::listAllAlgosWithBackend();
+    for (auto const& pair : pairs) {
+      BOOST_TEST_MESSAGE("  " + std::to_string(pair.first) + ": " + pair.second);
+    }
+
+    BOOST_TEST_MESSAGE("Setting up signer params:");
+
+    addSignerParams(DNSSECKeeper::RSASHA256, "RSA SHA256", rsaSha256SignerParams);
+
+    #ifdef HAVE_LIBCRYPTO_ECDSA
+    addSignerParams(DNSSECKeeper::ECDSA256, "ECDSA SHA256", ecdsaSha256);
+    #endif
+
+    // We need to have HAVE_LIBCRYPTO_ED25519 for the PEM reader/writer.
+    #if defined(HAVE_LIBCRYPTO_ED25519)
+    addSignerParams(DNSSECKeeper::ED25519, "ED25519", ed25519);
+    #endif
+
+    #if defined(HAVE_LIBDECAF) || defined(HAVE_LIBCRYPTO_ED448)
+    addSignerParams(DNSSECKeeper::ED448, "ED448", ed448);
+    #endif
+  }
+
+  void addSignerParams(const uint8_t algorithm, const std::string& name, const SignerParams& params)
+  {
+    BOOST_TEST_MESSAGE("  " + std::to_string(algorithm) + ": " + name + " (" + params.name + ")");
+    signerParams.insert_or_assign(algorithm, params);
+  }
+
+  const std::string message{"Very good, young padawan."};
+  std::unordered_map<uint8_t, struct SignerParams> signerParams;
 };
 
 static void checkRR(const SignerParams& signer)
@@ -170,7 +309,7 @@ static void checkRR(const SignerParams& signer)
 
   sortedRecords_t rrs;
   /* values taken from rfc8080 for ed25519 and ed448, rfc5933 for gost */
-  DNSName qname(dpk.d_algorithm == 12 ? "www.example.net." : "example.com.");
+  DNSName qname(dpk.d_algorithm == DNSSECKeeper::ECCGOST ? "www.example.net." : "example.com.");
 
   reportBasicTypes();
 
@@ -178,7 +317,7 @@ static void checkRR(const SignerParams& signer)
   uint32_t expire = 1440021600;
   uint32_t inception = 1438207200;
 
-  if (dpk.d_algorithm == 12) {
+  if (dpk.d_algorithm == DNSSECKeeper::ECCGOST) {
     rrc.d_signer = DNSName("example.net.");
     inception = 946684800;
     expire = 1893456000;
@@ -216,7 +355,7 @@ static void checkRR(const SignerParams& signer)
   }
 }
 
-static auto test_generic_signer(std::shared_ptr<DNSCryptoKeyEngine> dcke, DNSKEYRecordContent& drc, const SignerParams& signer)
+static void test_generic_signer(std::shared_ptr<DNSCryptoKeyEngine> dcke, DNSKEYRecordContent& drc, const SignerParams& signer, const std::string& message)
 {
   BOOST_CHECK_EQUAL(dcke->getAlgorithm(), signer.algorithm);
   BOOST_CHECK_EQUAL(dcke->getBits(), signer.bits);
@@ -267,90 +406,47 @@ static auto test_generic_signer(std::shared_ptr<DNSCryptoKeyEngine> dcke, DNSKEY
   }
 }
 
-BOOST_AUTO_TEST_CASE(test_generic_signers)
+BOOST_FIXTURE_TEST_CASE(test_generic_signers, Fixture)
 {
-  for (const auto& signer : signers) {
+  for (const auto& algoSignerPair : signerParams) {
+    auto signer = algoSignerPair.second;
+
     DNSKEYRecordContent drc;
     auto dcke = std::shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::makeFromISCString(drc, signer.iscMap));
-    test_generic_signer(dcke, drc, signer);
+    test_generic_signer(dcke, drc, signer, message);
 
-    if (signer.pem.has_value()) {
-      unique_ptr<std::FILE, decltype(&std::fclose)> fp{fmemopen((void*)signer.pem->c_str(), signer.pem->length(), "r"), &std::fclose};
-      BOOST_REQUIRE(fp.get() != nullptr);
+    unique_ptr<std::FILE, decltype(&std::fclose)> fp{fmemopen((void*)signer.pem.c_str(), signer.pem.length(), "r"), &std::fclose};
+    BOOST_REQUIRE(fp.get() != nullptr);
 
-      DNSKEYRecordContent pemDRC;
-      shared_ptr<DNSCryptoKeyEngine> pemKey{DNSCryptoKeyEngine::makeFromPEMFile(pemDRC, "<buffer>", *fp, signer.algorithm)};
+    DNSKEYRecordContent pemDRC;
+    shared_ptr<DNSCryptoKeyEngine> pemKey{DNSCryptoKeyEngine::makeFromPEMFile(pemDRC, "<buffer>", *fp, signer.algorithm)};
 
-      BOOST_CHECK_EQUAL(pemKey->convertToISC(), dcke->convertToISC());
+    BOOST_CHECK_EQUAL(pemKey->convertToISC(), dcke->convertToISC());
 
-      test_generic_signer(pemKey, pemDRC, signer);
+    test_generic_signer(pemKey, pemDRC, signer, message);
 
-      const size_t buflen = 4096;
+    const size_t buflen = 4096;
 
-      std::string dckePEMOutput{};
-      dckePEMOutput.resize(buflen);
-      unique_ptr<std::FILE, decltype(&std::fclose)> dckePEMOutputFp{fmemopen(static_cast<void*>(dckePEMOutput.data()), dckePEMOutput.length() - 1, "w"), &std::fclose};
-      dcke->convertToPEM(*dckePEMOutputFp);
-      std::fflush(dckePEMOutputFp.get());
-      dckePEMOutput.resize(std::ftell(dckePEMOutputFp.get()));
+    std::string dckePEMOutput{};
+    dckePEMOutput.resize(buflen);
+    unique_ptr<std::FILE, decltype(&std::fclose)> dckePEMOutputFp{fmemopen(static_cast<void*>(dckePEMOutput.data()), dckePEMOutput.length() - 1, "w"), &std::fclose};
+    dcke->convertToPEM(*dckePEMOutputFp);
+    std::fflush(dckePEMOutputFp.get());
+    dckePEMOutput.resize(std::ftell(dckePEMOutputFp.get()));
 
-      BOOST_CHECK_EQUAL(dckePEMOutput, *signer.pem);
+    BOOST_CHECK_EQUAL(dckePEMOutput, signer.pem);
 
-      std::string pemKeyOutput{};
-      pemKeyOutput.resize(buflen);
-      unique_ptr<std::FILE, decltype(&std::fclose)> pemKeyOutputFp{fmemopen(static_cast<void*>(pemKeyOutput.data()), pemKeyOutput.length() - 1, "w"), &std::fclose};
-      pemKey->convertToPEM(*pemKeyOutputFp);
-      std::fflush(pemKeyOutputFp.get());
-      pemKeyOutput.resize(std::ftell(pemKeyOutputFp.get()));
+    std::string pemKeyOutput{};
+    pemKeyOutput.resize(buflen);
+    unique_ptr<std::FILE, decltype(&std::fclose)> pemKeyOutputFp{fmemopen(static_cast<void*>(pemKeyOutput.data()), pemKeyOutput.length() - 1, "w"), &std::fclose};
+    pemKey->convertToPEM(*pemKeyOutputFp);
+    std::fflush(pemKeyOutputFp.get());
+    pemKeyOutput.resize(std::ftell(pemKeyOutputFp.get()));
 
-      BOOST_CHECK_EQUAL(pemKeyOutput, *signer.pem);
-    }
+    BOOST_CHECK_EQUAL(pemKeyOutput, signer.pem);
   }
 }
 
-#if defined(HAVE_LIBDECAF) || defined(HAVE_LIBCRYPTO_ED448)
-BOOST_AUTO_TEST_CASE(test_ed448_signer) {
-    sortedRecords_t rrs;
-    DNSName qname("example.com.");
-    DNSKEYRecordContent drc;
-
-    // TODO: make this a collection of inputs and resulting sigs for various algos
-    shared_ptr<DNSCryptoKeyEngine> engine = DNSCryptoKeyEngine::makeFromISCString(drc,
-"Private-key-format: v1.2\n"
-"Algorithm: 16 (ED448)\n"
-"PrivateKey: xZ+5Cgm463xugtkY5B0Jx6erFTXp13rYegst0qRtNsOYnaVpMx0Z/c5EiA9x8wWbDDct/U3FhYWA\n");
-
-    DNSSECPrivateKey dpk;
-    dpk.setKey(engine);
-
-    reportBasicTypes();
-
-    rrs.insert(DNSRecordContent::mastermake(QType::MX, 1, "10 mail.example.com."));
-
-    RRSIGRecordContent rrc;
-    rrc.d_originalttl = 3600;
-    rrc.d_sigexpire = 1440021600;
-    rrc.d_siginception = 1438207200;
-    rrc.d_signer = qname;
-    rrc.d_type = QType::MX;
-    rrc.d_labels = 2;
-    // TODO: derive the next two from the key
-    rrc.d_tag = 9713;
-    rrc.d_algorithm = 16;
-
-    string msg = getMessageForRRSET(qname, rrc, rrs, false);
-
-    // vector extracted from https://gitlab.labs.nic.cz/labs/ietf/blob/master/dnskey.py (rev 476d6ded) by printing signature_data
-    BOOST_CHECK_EQUAL(makeHexDump(msg), "00 0f 10 02 00 00 0e 10 55 d4 fc 60 55 b9 4c e0 25 f1 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 00 0f 00 01 00 00 0e 10 00 14 00 0a 04 6d 61 69 6c 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 ");
-
-    string signature = engine->sign(msg);
-    string b64 = Base64Encode(signature);
-
-    // vector verified from dnskey.py as above, and confirmed with https://www.rfc-editor.org/errata_search.php?rfc=8080&eid=4935
-    BOOST_CHECK_EQUAL(b64, "3cPAHkmlnxcDHMyg7vFC34l0blBhuG1qpwLmjInI8w1CMB29FkEAIJUA0amxWndkmnBZ6SKiwZSAxGILn/NBtOXft0+Gj7FSvOKxE/07+4RQvE581N3Aj/JtIyaiYVdnYtyMWbSNyGEY2213WKsJlwEA");
-}
-#endif /* defined(HAVE_LIBDECAF) || defined(HAVE_LIBCRYPTO_ED448) */
-
 BOOST_AUTO_TEST_CASE(test_hash_qname_with_salt) {
   {
     // rfc5155 appendix A
index 942447837381ca81b455f83bdf5dcd852aad7b15..0688323ac275081a5dc3e54515bee4f6fd571a39 100644 (file)
@@ -159,6 +159,9 @@ void pdns::ZoneMD::verify(bool& validationDone, bool& validationOK)
   validationDone = false;
   validationOK = false;
 
+  if (!d_soaRecordContent) {
+    return;
+  }
   // Get all records and remember RRSets and TTLs
 
   // Determine which digests to compute based on accepted zonemd records present
index 5a9c10a648f8db53661c73eef2b88395bc81147d..6061d3123f5162abe93286272e77d7487b96c037 100644 (file)
@@ -24,12 +24,13 @@ class BadXFRServer(object):
         return self._currentSerial
 
     def moveToSerial(self, newSerial):
-        if newSerial == self._currentSerial:
+        if newSerial == self._currentSerial or newSerial == self._targetSerial:
             return False
 
         #if newSerial != self._currentSerial + 1:
         #    raise AssertionError("Asking the XFR server to serve serial %d, already serving %d" % (newSerial, self._currentSerial))
         self._targetSerial = newSerial
+        print("moveToSerial %d" % newSerial, file=sys.stderr)
         return True
 
     def _getAnswer(self, message):
@@ -73,11 +74,13 @@ class BadXFRServer(object):
     def _connectionHandler(self, conn):
         data = None
         while True:
+            print("Reading from connection...", file=sys.stderr)
             data = conn.recv(2)
             if not data:
                 break
             (datalen,) = struct.unpack("!H", data)
             data = conn.recv(datalen)
+            print("Received request with len %d" % datalen, file=sys.stderr)
             if not data:
                 break
 
@@ -88,6 +91,7 @@ class BadXFRServer(object):
             if not message.question[0].rdtype in [dns.rdatatype.AXFR, dns.rdatatype.IXFR]:
                 print('Invalid query, qtype is %d' % (message.question.rdtype), file=sys.stderr)
                 break
+            print(message, file=sys.stderr)
             (serial, answer) = self._getAnswer(message)
             if not answer:
                 print('Unable to get a response for %s %d' % (message.question[0].name, message.question[0].rdtype), file=sys.stderr)
@@ -96,9 +100,11 @@ class BadXFRServer(object):
             wire = answer.to_wire()
             conn.send(struct.pack("!H", len(wire)))
             conn.send(wire)
+            print("_currentSerial to %d" % serial, file=sys.stderr)
             self._currentSerial = serial
             break
 
+        print("_connectionHandler: stop", file=sys.stderr)
         conn.close()
 
     def _listener(self):
@@ -114,6 +120,7 @@ class BadXFRServer(object):
         while True:
             try:
                 (conn, _) = sock.accept()
+                print("New connection", file=sys.stderr)
                 thread = threading.Thread(name='IXFR Connection Handler',
                                       target=self._connectionHandler,
                                       args=[conn])
@@ -143,6 +150,8 @@ query-cache-ttl=0
 domain-metadata-cache-ttl=0
 negquery-cache-ttl=0
 slave-cycle-interval=1
+#loglevel=9
+#axfr-fetch-timeout=20
 """
 
     @classmethod
@@ -151,7 +160,7 @@ slave-cycle-interval=1
         os.system("$PDNSUTIL --config-dir=configs/auth create-slave-zone zone.rpz. 127.0.0.1:%s" % (badxfrServerPort,))
         os.system("$PDNSUTIL --config-dir=configs/auth set-meta zone.rpz. IXFR 1")
     
-    def waitUntilCorrectSerialIsLoaded(self, serial, timeout=5):
+    def waitUntilCorrectSerialIsLoaded(self, serial, timeout=20):
         global badxfrServer
 
         badxfrServer.moveToSerial(serial)
@@ -162,6 +171,7 @@ slave-cycle-interval=1
             if currentSerial > serial:
                 raise AssertionError("Expected serial %d, got %d" % (serial, currentSerial))
             if currentSerial == serial:
+                badxfrServer.moveToSerial(serial+1)
                 return
 
             attempts = attempts + 1
index dd60abd6f4e0a7defa211b7bad8315b0348bd644..5c6fd0b451608554fd13b96a1d543eaf1ba1683a 100644 (file)
@@ -121,6 +121,11 @@ class TestRecursorProtobuf(RecursorTest):
         print(msg)
         return msg
 
+    def emptyProtoBufQueue(self):
+        for param in protobufServersParameters:
+            while not param.queue.empty():
+                param.queue.get(False)
+
     def checkNoRemainingMessage(self):
         for param in protobufServersParameters:
           self.assertTrue(param.queue.empty())
@@ -284,9 +289,7 @@ class TestRecursorProtobuf(RecursorTest):
         super(TestRecursorProtobuf, self).setUp()
         # Make sure the queue is empty, in case
         # a previous test failed
-        for param in protobufServersParameters:
-            while not param.queue.empty():
-                param.queue.get(False)
+        self.emptyProtoBufQueue()
         # wait long enough to be sure that the housekeeping has
         # prime the root NS
         time.sleep(1)
@@ -575,17 +578,25 @@ class OutgoingProtobufDefaultTest(TestRecursorProtobuf):
     # Switch off QName Minimization, it generates much more protobuf messages
     # (or make the test much more smart!)
     qname-minimization=no
+    max-cache-ttl=600
+    loglevel=9
 """
     _lua_config_file = """
     outgoingProtobufServer({"127.0.0.1:%d", "127.0.0.1:%d"})
     """ % (protobufServersParameters[0].port, protobufServersParameters[1].port)
 
     def testA(self):
+        # There is a race in priming (having the . DNSKEY in cache in particular) and this code.
+        # So make sure we have the . DNSKEY in cache
+        query = dns.message.make_query('.', 'A', want_dnssec=True)
+        query.flags |= dns.flags.RD
+        res = self.sendUDPQuery(query)
+        time.sleep(1)
+        self.emptyProtoBufQueue()
+
         name = 'host1.secure.example.'
         expected = list()
 
-        # the root DNSKEY has been learned with priming the root NS already
-        # ('.', dns.rdatatype.DNSKEY, dnsmessage_pb2.PBDNSMessage.UDP, 201),
         for qname, qtype, proto, responseSize in [
                 ('host1.secure.example.', dns.rdatatype.A, dnsmessage_pb2.PBDNSMessage.UDP, 248),
                 ('host1.secure.example.', dns.rdatatype.A, dnsmessage_pb2.PBDNSMessage.UDP, 221),
@@ -636,7 +647,8 @@ class OutgoingProtobufWithECSMappingTest(TestRecursorProtobuf):
     edns-subnet-allow-list=example
     allow-from=1.2.3.4/32
     # this is to not let . queries interfere
-    max-cache-ttl=60
+    max-cache-ttl=600
+    loglevel=9
 """
     _lua_config_file = """
     outgoingProtobufServer({"127.0.0.1:%d", "127.0.0.1:%d"})
@@ -644,11 +656,17 @@ class OutgoingProtobufWithECSMappingTest(TestRecursorProtobuf):
     """ % (protobufServersParameters[0].port, protobufServersParameters[1].port)
 
     def testA(self):
+        # There is a race in priming (having the . DNSKEY in cache in particular) and this code.
+        # So make sure we have the . DNSKEY in cache
+        query = dns.message.make_query('.', 'A', want_dnssec=True)
+        query.flags |= dns.flags.RD
+        res = self.sendUDPQuery(query)
+        time.sleep(1)
+        self.emptyProtoBufQueue()
+
         name = 'host1.secure.example.'
         expected = list()
 
-        # the root DNSKEY has been learned with priming the root NS already
-        # ('.', dns.rdatatype.DNSKEY, dnsmessage_pb2.PBDNSMessage.UDP, 201),
         for qname, qtype, proto, responseSize, ecs in [
                 ('host1.secure.example.', dns.rdatatype.A, dnsmessage_pb2.PBDNSMessage.UDP, 248, "1.2.3.0"),
                 ('host1.secure.example.', dns.rdatatype.A, dnsmessage_pb2.PBDNSMessage.UDP, 221, "1.2.3.0"),
@@ -688,8 +706,6 @@ class OutgoingProtobufWithECSMappingTest(TestRecursorProtobuf):
         name = 'mx1.secure.example.'
         expected = list()
 
-        # the root DNSKEY has been learned with priming the root NS already
-        # ('.', dns.rdatatype.DNSKEY, dnsmessage_pb2.PBDNSMessage.UDP, 201),
         for qname, qtype, proto, responseSize, ecs in [
                 ('mx1.secure.example.', dns.rdatatype.A, dnsmessage_pb2.PBDNSMessage.UDP, 173, "127.0.0.1"),
         ]:
@@ -714,9 +730,7 @@ class OutgoingProtobufWithECSMappingTest(TestRecursorProtobuf):
                 continue
 
             msg = self.getFirstProtobufMessage()
-            print(qname, qtype, proto, responseSize, ecs, file=sys.stderr)
             self.checkProtobufOutgoingQuery(msg, proto, qry, dns.rdataclass.IN, qtype, qname, "127.0.0.1", None, ecs)
-            print("OK", file=sys.stderr);
             # Check the answer
             msg = self.getFirstProtobufMessage()
             self.checkProtobufIncomingResponse(msg, proto, ans, length=responseSize)
@@ -734,12 +748,23 @@ class OutgoingProtobufNoQueriesTest(TestRecursorProtobuf):
     _config_template = """
     # Switch off QName Minimization, it generates much more protobuf messages
     # (or make the test much more smart!)
-    qname-minimization=no"""
+    qname-minimization=no
+    max-cache-ttl=600
+    loglevel=9
+"""
     _lua_config_file = """
     outgoingProtobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=false, logResponses=true })
     """ % (protobufServersParameters[0].port, protobufServersParameters[1].port)
 
     def testA(self):
+        # There is a race in priming (having the . DNSKEY in cache in particular) and this code.
+        # So make sure we have the . DNSKEY in cache
+        query = dns.message.make_query('.', 'A', want_dnssec=True)
+        query.flags |= dns.flags.RD
+        res = self.sendUDPQuery(query)
+        time.sleep(1)
+        self.emptyProtoBufQueue()
+
         name = 'host1.secure.example.'
         expected = list()
         # the root DNSKEY has been learned with priming the root NS already