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)
#include <decaf.hxx>
#include <decaf/eddsa.hxx>
#include <decaf/spongerng.hxx>
+#include "dnsseckeeper.hh"
#include "dnssecinfra.hh"
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;
}
else if (commandResult == ConsoleCommandResult::TooLarge) {
return;
}
- else if (commandResult == ConsoleCommandResult::TooLarge) {
- close(fd);
- return;
- }
if (!command.empty()) {
sendMessageToServer(fd.getHandle(), command, readingNonce, writingNonce, false);
{ "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)" },
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;
}
}
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);
+ }
}
}
}
: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.
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"
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);
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()
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;
}
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;
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
{
#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;
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();
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);
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);
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
- 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
--------------
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: "
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);
});
}
+ 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);
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++;
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++;
#include <sodium.h>
}
#include "dnssecinfra.hh"
+#include "dnsseckeeper.hh"
class SodiumED25519DNSCryptoKeyEngine : public DNSCryptoKeyEngine
{
}
namespace {
-struct LoaderSodiumStruct
+const struct LoaderSodiumStruct
{
LoaderSodiumStruct()
{
- DNSCryptoKeyEngine::report(15, &SodiumED25519DNSCryptoKeyEngine::maker);
+ DNSCryptoKeyEngine::report(DNSSECKeeper::ED25519, &SodiumED25519DNSCryptoKeyEngine::maker);
}
} loadersodium;
}
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)
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);
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;
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;
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);
}
}
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;
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;
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;
}
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) {
>
> 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;
};
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) {}
};
struct ThreadLocalStorage {
- throttle_t throttle;
ednsstatus_t ednsstatus;
std::shared_ptr<domainmap_t> domainmap;
};
{
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();
#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;
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)
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();
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;
}
}
-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);
}
}
-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
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
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):
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
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)
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):
while True:
try:
(conn, _) = sock.accept()
+ print("New connection", file=sys.stderr)
thread = threading.Thread(name='IXFR Connection Handler',
target=self._connectionHandler,
args=[conn])
domain-metadata-cache-ttl=0
negquery-cache-ttl=0
slave-cycle-interval=1
+#loglevel=9
+#axfr-fetch-timeout=20
"""
@classmethod
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)
if currentSerial > serial:
raise AssertionError("Expected serial %d, got %d" % (serial, currentSerial))
if currentSerial == serial:
+ badxfrServer.moveToSerial(serial+1)
return
attempts = attempts + 1
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())
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)
# 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),
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"})
""" % (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"),
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"),
]:
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)
_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