"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "pdns/recursordist/test-syncres_cc.cc" between
pdns-auth-4.1.13.tar.gz and pdns-auth-4.2.0.tar.gz

About: PowerDNS Authoritative Nameserver is a versatile nameserver which supports a large number of backends (that can either be plain zone files or be more dynamic in nature).

test-syncres_cc.cc  (pdns-auth-4.1.13):test-syncres_cc.cc  (pdns-auth-4.2.0)
#define BOOST_TEST_DYN_LINK #define BOOST_TEST_DYN_LINK
#define BOOST_TEST_NO_MAIN
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include "arguments.hh"
#include "base32.hh" #include "base32.hh"
#include "dnssecinfra.hh"
#include "dnsseckeeper.hh"
#include "lua-recursor4.hh" #include "lua-recursor4.hh"
#include "namespaces.hh"
#include "rec-lua-conf.hh"
#include "root-dnssec.hh" #include "root-dnssec.hh"
#include "syncres.hh" #include "test-syncres_cc.hh"
#include "test-common.hh"
#include "utility.hh"
#include "validate-recursor.hh"
RecursorStats g_stats; RecursorStats g_stats;
GlobalStateHolder<LuaConfigItems> g_luaconfs; GlobalStateHolder<LuaConfigItems> g_luaconfs;
GlobalStateHolder<SuffixMatchNode> g_dontThrottleNames;
GlobalStateHolder<NetmaskGroup> g_dontThrottleNetmasks;
thread_local std::unique_ptr<MemRecursorCache> t_RC{nullptr}; thread_local std::unique_ptr<MemRecursorCache> t_RC{nullptr};
unsigned int g_numThreads = 1; unsigned int g_numThreads = 1;
bool g_lowercaseOutgoing = false; bool g_lowercaseOutgoing = false;
/* Fake some required functions we didn't want the trouble to /* Fake some required functions we didn't want the trouble to
link with */ link with */
ArgvMap &arg() ArgvMap &arg()
{ {
static ArgvMap theArg; static ArgvMap theArg;
return theArg; return theArg;
} }
int getMTaskerTID() int getMTaskerTID()
{ {
return 0; return 0;
} }
bool RecursorLua4::preoutquery(const ComboAddress& ns, const ComboAddress& reque stor, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& r es, int& ret) bool RecursorLua4::preoutquery(const ComboAddress& ns, const ComboAddress& reque stor, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& r es, int& ret) const
{ {
return false; return false;
} }
int asyncresolve(const ComboAddress& ip, const DNSName& domain, int type, bool d oTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Net mask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr< RemoteLogger> outgoingLogger, LWResult* res) int asyncresolve(const ComboAddress& ip, const DNSName& domain, int type, bool d oTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Net mask>& srcmask, boost::optional<const ResolveContext&> context, const std::share d_ptr<std::vector<std::unique_ptr<RemoteLogger>>>& outgoingLoggers, const std::s hared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstrmLoggers,const s td::set<uint16_t>& exportTypes, LWResult* res, bool* chained)
{ {
return 0; return 0;
} }
/* primeHints() is only here for now because it /* primeHints() is only here for now because it
was way too much trouble to link with the real one. was way too much trouble to link with the real one.
We should fix this, empty functions are one thing, but this is We should fix this, empty functions are one thing, but this is
bad. bad.
*/ */
skipping to change at line 69 skipping to change at line 62
t_RC = std::unique_ptr<MemRecursorCache>(new MemRecursorCache()); t_RC = std::unique_ptr<MemRecursorCache>(new MemRecursorCache());
DNSRecord arr, aaaarr, nsrr; DNSRecord arr, aaaarr, nsrr;
nsrr.d_name=g_rootdnsname; nsrr.d_name=g_rootdnsname;
arr.d_type=QType::A; arr.d_type=QType::A;
aaaarr.d_type=QType::AAAA; aaaarr.d_type=QType::AAAA;
nsrr.d_type=QType::NS; nsrr.d_type=QType::NS;
arr.d_ttl=aaaarr.d_ttl=nsrr.d_ttl=time(nullptr)+3600000; arr.d_ttl=aaaarr.d_ttl=nsrr.d_ttl=time(nullptr)+3600000;
for(char c='a';c<='m';++c) { for(char c='a';c<='m';++c) {
static char templ[40]; char templ[40];
strncpy(templ,"a.root-servers.net.", sizeof(templ) - 1); strncpy(templ,"a.root-servers.net.", sizeof(templ) - 1);
templ[sizeof(templ)-1] = '\0'; templ[sizeof(templ)-1] = '\0';
*templ=c; *templ=c;
aaaarr.d_name=arr.d_name=DNSName(templ); aaaarr.d_name=arr.d_name=DNSName(templ);
nsrr.d_content=std::make_shared<NSRecordContent>(DNSName(templ)); nsrr.d_content=std::make_shared<NSRecordContent>(DNSName(templ));
arr.d_content=std::make_shared<ARecordContent>(ComboAddress(rootIps4[c-'a']) ); arr.d_content=std::make_shared<ARecordContent>(ComboAddress(rootIps4[c-'a']) );
vector<DNSRecord> aset; vector<DNSRecord> aset;
aset.push_back(arr); aset.push_back(arr);
t_RC->replace(time(0), DNSName(templ), QType(QType::A), aset, vector<std::sh ared_ptr<RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), true); // auth, nuke it all t_RC->replace(time(nullptr), DNSName(templ), QType(QType::A), aset, vector<s td::shared_ptr<RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), tru e); // auth, nuke it all
if (rootIps6[c-'a'] != NULL) { if (rootIps6[c-'a'] != NULL) {
aaaarr.d_content=std::make_shared<AAAARecordContent>(ComboAddress(rootIps6 [c-'a'])); aaaarr.d_content=std::make_shared<AAAARecordContent>(ComboAddress(rootIps6 [c-'a']));
vector<DNSRecord> aaaaset; vector<DNSRecord> aaaaset;
aaaaset.push_back(aaaarr); aaaaset.push_back(aaaarr);
t_RC->replace(time(0), DNSName(templ), QType(QType::AAAA), aaaaset, vector <std::shared_ptr<RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), t rue); t_RC->replace(time(nullptr), DNSName(templ), QType(QType::AAAA), aaaaset, vector<std::shared_ptr<RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord> >(), true);
} }
nsset.push_back(nsrr); nsset.push_back(nsrr);
} }
t_RC->replace(time(0), g_rootdnsname, QType(QType::NS), nsset, vector<std::sha red_ptr<RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), false); // and stuff in the cache t_RC->replace(time(nullptr), g_rootdnsname, QType(QType::NS), nsset, vector<st d::shared_ptr<RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), fals e); // and stuff in the cache
} }
LuaConfigItems::LuaConfigItems() LuaConfigItems::LuaConfigItems()
{ {
for (const auto &dsRecord : rootDSs) { for (const auto &dsRecord : rootDSs) {
auto ds=std::dynamic_pointer_cast<DSRecordContent>(DSRecordContent::make(dsR ecord)); auto ds=std::dynamic_pointer_cast<DSRecordContent>(DSRecordContent::make(dsR ecord));
dsAnchors[g_rootdnsname].insert(*ds); dsAnchors[g_rootdnsname].insert(*ds);
} }
} }
/* Some helpers functions */ /* Some helpers functions */
static void init(bool debug=false) void initSR(bool debug)
{ {
L.setName("test"); g_log.setName("test");
L.disableSyslog(true); g_log.disableSyslog(true);
if (debug) { if (debug) {
L.setLoglevel((Logger::Urgency)(6)); // info and up g_log.setLoglevel((Logger::Urgency)(6)); // info and up
L.toConsole(Logger::Info); g_log.toConsole(Logger::Info);
} }
else { else {
L.setLoglevel(Logger::None); g_log.setLoglevel(Logger::None);
L.toConsole(Logger::Error); g_log.toConsole(Logger::Error);
} }
seedRandom("/dev/urandom");
reportAllTypes();
t_RC = std::unique_ptr<MemRecursorCache>(new MemRecursorCache()); t_RC = std::unique_ptr<MemRecursorCache>(new MemRecursorCache());
SyncRes::s_maxqperq = 50; SyncRes::s_maxqperq = 50;
SyncRes::s_maxtotusec = 1000*7000; SyncRes::s_maxtotusec = 1000*7000;
SyncRes::s_maxdepth = 40; SyncRes::s_maxdepth = 40;
SyncRes::s_maxnegttl = 3600; SyncRes::s_maxnegttl = 3600;
SyncRes::s_maxbogusttl = 3600;
SyncRes::s_maxcachettl = 86400; SyncRes::s_maxcachettl = 86400;
SyncRes::s_packetcachettl = 3600; SyncRes::s_packetcachettl = 3600;
SyncRes::s_packetcacheservfailttl = 60; SyncRes::s_packetcacheservfailttl = 60;
SyncRes::s_serverdownmaxfails = 64; SyncRes::s_serverdownmaxfails = 64;
SyncRes::s_serverdownthrottletime = 60; SyncRes::s_serverdownthrottletime = 60;
SyncRes::s_doIPv6 = true; SyncRes::s_doIPv6 = true;
SyncRes::s_ecsipv4limit = 24; SyncRes::s_ecsipv4limit = 24;
SyncRes::s_ecsipv6limit = 56; SyncRes::s_ecsipv6limit = 56;
SyncRes::s_ecsipv4cachelimit = 24;
SyncRes::s_ecsipv6cachelimit = 56;
SyncRes::s_ecscachelimitttl = 0;
SyncRes::s_rootNXTrust = true; SyncRes::s_rootNXTrust = true;
SyncRes::s_minimumTTL = 0; SyncRes::s_minimumTTL = 0;
SyncRes::s_minimumECSTTL = 0;
SyncRes::s_serverID = "PowerDNS Unit Tests Server ID"; SyncRes::s_serverID = "PowerDNS Unit Tests Server ID";
SyncRes::clearEDNSSubnets(); SyncRes::clearEDNSLocalSubnets();
SyncRes::addEDNSLocalSubnet("0.0.0.0/0");
SyncRes::addEDNSLocalSubnet("::/0");
SyncRes::clearEDNSRemoteSubnets();
SyncRes::clearEDNSDomains(); SyncRes::clearEDNSDomains();
SyncRes::clearDelegationOnly(); SyncRes::clearDelegationOnly();
SyncRes::clearDontQuery(); SyncRes::clearDontQuery();
SyncRes::setECSScopeZeroAddress(Netmask("127.0.0.1/32"));
SyncRes::clearNSSpeeds(); SyncRes::clearNSSpeeds();
BOOST_CHECK_EQUAL(SyncRes::getNSSpeedsSize(), 0); BOOST_CHECK_EQUAL(SyncRes::getNSSpeedsSize(), 0);
SyncRes::clearEDNSStatuses(); SyncRes::clearEDNSStatuses();
BOOST_CHECK_EQUAL(SyncRes::getEDNSStatusesSize(), 0); BOOST_CHECK_EQUAL(SyncRes::getEDNSStatusesSize(), 0);
SyncRes::clearThrottle(); SyncRes::clearThrottle();
BOOST_CHECK_EQUAL(SyncRes::getThrottledServersSize(), 0); BOOST_CHECK_EQUAL(SyncRes::getThrottledServersSize(), 0);
SyncRes::clearFailedServers(); SyncRes::clearFailedServers();
BOOST_CHECK_EQUAL(SyncRes::getFailedServersSize(), 0); BOOST_CHECK_EQUAL(SyncRes::getFailedServersSize(), 0);
SyncRes::clearECSStats();
auto luaconfsCopy = g_luaconfs.getCopy(); auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dfe.clear(); luaconfsCopy.dfe.clear();
luaconfsCopy.dsAnchors.clear(); luaconfsCopy.dsAnchors.clear();
for (const auto &dsRecord : rootDSs) { for (const auto &dsRecord : rootDSs) {
auto ds=std::dynamic_pointer_cast<DSRecordContent>(DSRecordContent::make(dsR ecord)); auto ds=std::dynamic_pointer_cast<DSRecordContent>(DSRecordContent::make(dsR ecord));
luaconfsCopy.dsAnchors[g_rootdnsname].insert(*ds); luaconfsCopy.dsAnchors[g_rootdnsname].insert(*ds);
} }
luaconfsCopy.negAnchors.clear(); luaconfsCopy.negAnchors.clear();
g_luaconfs.setState(luaconfsCopy); g_luaconfs.setState(luaconfsCopy);
g_dnssecmode = DNSSECMode::Off; g_dnssecmode = DNSSECMode::Off;
g_dnssecLOG = debug; g_dnssecLOG = debug;
g_maxNSEC3Iterations = 2500; g_maxNSEC3Iterations = 2500;
::arg().set("version-string", "string reported on version.pdns or version.bind ")="PowerDNS Unit Tests"; ::arg().set("version-string", "string reported on version.pdns or version.bind ")="PowerDNS Unit Tests";
::arg().set("rng")="auto";
::arg().set("entropy-source")="/dev/urandom";
::arg().setSwitch("qname-minimisation", "Use Query Name Minimisation") = "no";
} }
static void initSR(std::unique_ptr<SyncRes>& sr, bool dnssec=false, bool debug=f alse, time_t fakeNow=0) void initSR(std::unique_ptr<SyncRes>& sr, bool dnssec, bool debug, time_t fakeNo w)
{ {
struct timeval now; struct timeval now;
if (fakeNow > 0) { if (fakeNow > 0) {
now.tv_sec = fakeNow; now.tv_sec = fakeNow;
now.tv_usec = 0; now.tv_usec = 0;
} }
else { else {
Utility::gettimeofday(&now, 0); Utility::gettimeofday(&now, 0);
} }
init(debug); initSR(debug);
sr = std::unique_ptr<SyncRes>(new SyncRes(now)); sr = std::unique_ptr<SyncRes>(new SyncRes(now));
sr->setDoEDNS0(true); sr->setDoEDNS0(true);
if (dnssec) { if (dnssec) {
sr->setDoDNSSEC(dnssec); sr->setDoDNSSEC(dnssec);
} }
sr->setLogMode(debug == false ? SyncRes::LogNone : SyncRes::Log); sr->setLogMode(debug == false ? SyncRes::LogNone : SyncRes::Log);
SyncRes::setDomainMap(std::make_shared<SyncRes::domainmap_t>()); SyncRes::setDomainMap(std::make_shared<SyncRes::domainmap_t>());
SyncRes::clearNegCache(); SyncRes::clearNegCache();
} }
static void setDNSSECValidation(std::unique_ptr<SyncRes>& sr, const DNSSECMode& mode) void setDNSSECValidation(std::unique_ptr<SyncRes>& sr, const DNSSECMode& mode)
{ {
sr->setDNSSECValidationRequested(true); sr->setDNSSECValidationRequested(true);
g_dnssecmode = mode; g_dnssecmode = mode;
} }
static void setLWResult(LWResult* res, int rcode, bool aa=false, bool tc=false, void setDoQNameMinimisation(void)
bool edns=false) {
::arg().setSwitch("qname-minimisation", "") = "yes";
}
void setLWResult(LWResult* res, int rcode, bool aa, bool tc, bool edns, bool val
idpacket)
{ {
res->d_rcode = rcode; res->d_rcode = rcode;
res->d_aabit = aa; res->d_aabit = aa;
res->d_tcbit = tc; res->d_tcbit = tc;
res->d_haveEDNS = edns; res->d_haveEDNS = edns;
res->d_validpacket = validpacket;
} }
static void addRecordToLW(LWResult* res, const DNSName& name, uint16_t type, con st std::string& content, DNSResourceRecord::Place place=DNSResourceRecord::ANSWE R, uint32_t ttl=60) void addRecordToLW(LWResult* res, const DNSName& name, uint16_t type, const std: :string& content, DNSResourceRecord::Place place, uint32_t ttl)
{ {
addRecordToList(res->d_records, name, type, content, place, ttl); addRecordToList(res->d_records, name, type, content, place, ttl);
} }
static void addRecordToLW(LWResult* res, const std::string& name, uint16_t type, const std::string& content, DNSResourceRecord::Place place=DNSResourceRecord::A NSWER, uint32_t ttl=60) void addRecordToLW(LWResult* res, const std::string& name, uint16_t type, const std::string& content, DNSResourceRecord::Place place, uint32_t ttl)
{ {
addRecordToLW(res, DNSName(name), type, content, place, ttl); addRecordToLW(res, DNSName(name), type, content, place, ttl);
} }
static bool isRootServer(const ComboAddress& ip) bool isRootServer(const ComboAddress& ip)
{ {
if (ip.isIPv4()) { if (ip.isIPv4()) {
for (size_t idx = 0; idx < rootIps4Count; idx++) { for (size_t idx = 0; idx < rootIps4Count; idx++) {
if (ip.toString() == rootIps4[idx]) { if (ip.toString() == rootIps4[idx]) {
return true; return true;
} }
} }
} }
else { else {
for (size_t idx = 0; idx < rootIps6Count; idx++) { for (size_t idx = 0; idx < rootIps6Count; idx++) {
if (ip.toString() == rootIps6[idx]) { if (ip.toString() == rootIps6[idx]) {
return true; return true;
} }
} }
} }
return false; return false;
} }
static void computeRRSIG(const DNSSECPrivateKey& dpk, const DNSName& signer, con st DNSName& signQName, uint16_t signQType, uint32_t signTTL, uint32_t sigValidit y, RRSIGRecordContent& rrc, vector<shared_ptr<DNSRecordContent> >& toSign, boost ::optional<uint8_t> algo=boost::none, boost::optional<uint32_t> inception=boost: :none) void computeRRSIG(const DNSSECPrivateKey& dpk, const DNSName& signer, const DNSN ame& signQName, uint16_t signQType, uint32_t signTTL, uint32_t sigValidity, RRSI GRecordContent& rrc, vector<shared_ptr<DNSRecordContent> >& toSign, boost::optio nal<uint8_t> algo, boost::optional<uint32_t> inception, boost::optional<time_t> now)
{ {
time_t now = time(nullptr); if (!now) {
now = time(nullptr);
}
DNSKEYRecordContent drc = dpk.getDNSKEY(); DNSKEYRecordContent drc = dpk.getDNSKEY();
const std::shared_ptr<DNSCryptoKeyEngine> rc = dpk.getKey(); const std::shared_ptr<DNSCryptoKeyEngine> rc = dpk.getKey();
rrc.d_type = signQType; rrc.d_type = signQType;
rrc.d_labels = signQName.countLabels() - signQName.isWildcard(); rrc.d_labels = signQName.countLabels() - signQName.isWildcard();
rrc.d_originalttl = signTTL; rrc.d_originalttl = signTTL;
rrc.d_siginception = inception ? *inception : (now - 10); rrc.d_siginception = inception ? *inception : (*now - 10);
rrc.d_sigexpire = now + sigValidity; rrc.d_sigexpire = *now + sigValidity;
rrc.d_signer = signer; rrc.d_signer = signer;
rrc.d_tag = 0; rrc.d_tag = 0;
rrc.d_tag = drc.getTag(); rrc.d_tag = drc.getTag();
rrc.d_algorithm = algo ? *algo : drc.d_algorithm; rrc.d_algorithm = algo ? *algo : drc.d_algorithm;
std::string msg = getMessageForRRSET(signQName, rrc, toSign); std::string msg = getMessageForRRSET(signQName, rrc, toSign);
rrc.d_signature = rc->sign(msg); rrc.d_signature = rc->sign(msg);
} }
typedef std::unordered_map<DNSName, std::pair<DNSSECPrivateKey, DSRecordContent> > testkeysset_t; typedef std::unordered_map<DNSName, std::pair<DNSSECPrivateKey, DSRecordContent> > testkeysset_t;
static bool addRRSIG(const testkeysset_t& keys, std::vector<DNSRecord>& records, const DNSName& signer, uint32_t sigValidity, bool broken=false, boost::optional <uint8_t> algo=boost::none, boost::optional<DNSName> wildcard=boost::none) bool addRRSIG(const testkeysset_t& keys, std::vector<DNSRecord>& records, const DNSName& signer, uint32_t sigValidity, bool broken, boost::optional<uint8_t> alg o, boost::optional<DNSName> wildcard, boost::optional<time_t> now)
{ {
if (records.empty()) { if (records.empty()) {
return false; return false;
} }
const auto it = keys.find(signer); const auto it = keys.find(signer);
if (it == keys.cend()) { if (it == keys.cend()) {
throw std::runtime_error("No DNSKEY found for " + signer.toString() + ", una ble to compute the requested RRSIG"); throw std::runtime_error("No DNSKEY found for " + signer.toLogString() + ", unable to compute the requested RRSIG");
} }
size_t recordsCount = records.size(); size_t recordsCount = records.size();
const DNSName& name = records[recordsCount-1].d_name; const DNSName& name = records[recordsCount-1].d_name;
const uint16_t type = records[recordsCount-1].d_type; const uint16_t type = records[recordsCount-1].d_type;
std::vector<std::shared_ptr<DNSRecordContent> > recordcontents; std::vector<std::shared_ptr<DNSRecordContent> > recordcontents;
for (const auto record : records) { for (const auto record : records) {
if (record.d_name == name && record.d_type == type) { if (record.d_name == name && record.d_type == type) {
recordcontents.push_back(record.d_content); recordcontents.push_back(record.d_content);
} }
} }
RRSIGRecordContent rrc; RRSIGRecordContent rrc;
computeRRSIG(it->second.first, signer, wildcard ? *wildcard : records[recordsC ount-1].d_name, records[recordsCount-1].d_type, records[recordsCount-1].d_ttl, s igValidity, rrc, recordcontents, algo); computeRRSIG(it->second.first, signer, wildcard ? *wildcard : records[recordsC ount-1].d_name, records[recordsCount-1].d_type, records[recordsCount-1].d_ttl, s igValidity, rrc, recordcontents, algo, boost::none, now);
if (broken) { if (broken) {
rrc.d_signature[0] ^= 42; rrc.d_signature[0] ^= 42;
} }
DNSRecord rec; DNSRecord rec;
rec.d_type = QType::RRSIG; rec.d_type = QType::RRSIG;
rec.d_place = records[recordsCount-1].d_place; rec.d_place = records[recordsCount-1].d_place;
rec.d_name = records[recordsCount-1].d_name; rec.d_name = records[recordsCount-1].d_name;
rec.d_ttl = records[recordsCount-1].d_ttl; rec.d_ttl = records[recordsCount-1].d_ttl;
rec.d_content = std::make_shared<RRSIGRecordContent>(rrc); rec.d_content = std::make_shared<RRSIGRecordContent>(rrc);
records.push_back(rec); records.push_back(rec);
return true; return true;
} }
static void addDNSKEY(const testkeysset_t& keys, const DNSName& signer, uint32_t ttl, std::vector<DNSRecord>& records) void addDNSKEY(const testkeysset_t& keys, const DNSName& signer, uint32_t ttl, s td::vector<DNSRecord>& records)
{ {
const auto it = keys.find(signer); const auto it = keys.find(signer);
if (it == keys.cend()) { if (it == keys.cend()) {
throw std::runtime_error("No DNSKEY found for " + signer.toString()); throw std::runtime_error("No DNSKEY found for " + signer.toLogString());
} }
DNSRecord rec; DNSRecord rec;
rec.d_place = DNSResourceRecord::ANSWER; rec.d_place = DNSResourceRecord::ANSWER;
rec.d_name = signer; rec.d_name = signer;
rec.d_type = QType::DNSKEY; rec.d_type = QType::DNSKEY;
rec.d_ttl = ttl; rec.d_ttl = ttl;
rec.d_content = std::make_shared<DNSKEYRecordContent>(it->second.first.getDNSK EY()); rec.d_content = std::make_shared<DNSKEYRecordContent>(it->second.first.getDNSK EY());
records.push_back(rec); records.push_back(rec);
} }
static bool addDS(const DNSName& domain, uint32_t ttl, std::vector<DNSRecord>& r ecords, const testkeysset_t& keys, DNSResourceRecord::Place place=DNSResourceRec ord::AUTHORITY) bool addDS(const DNSName& domain, uint32_t ttl, std::vector<DNSRecord>& records, const testkeysset_t& keys, DNSResourceRecord::Place place)
{ {
const auto it = keys.find(domain); const auto it = keys.find(domain);
if (it == keys.cend()) { if (it == keys.cend()) {
return false; return false;
} }
DNSRecord rec; DNSRecord rec;
rec.d_name = domain; rec.d_name = domain;
rec.d_type = QType::DS; rec.d_type = QType::DS;
rec.d_place = place; rec.d_place = place;
rec.d_ttl = ttl; rec.d_ttl = ttl;
rec.d_content = std::make_shared<DSRecordContent>(it->second.second); rec.d_content = std::make_shared<DSRecordContent>(it->second.second);
records.push_back(rec); records.push_back(rec);
return true; return true;
} }
static void addNSECRecordToLW(const DNSName& domain, const DNSName& next, const std::set<uint16_t>& types, uint32_t ttl, std::vector<DNSRecord>& records) void addNSECRecordToLW(const DNSName& domain, const DNSName& next, const std::se t<uint16_t>& types, uint32_t ttl, std::vector<DNSRecord>& records)
{ {
NSECRecordContent nrc; NSECRecordContent nrc;
nrc.d_next = next; nrc.d_next = next;
nrc.d_set = types; for (const auto& type : types) {
nrc.set(type);
}
DNSRecord rec; DNSRecord rec;
rec.d_name = domain; rec.d_name = domain;
rec.d_ttl = ttl; rec.d_ttl = ttl;
rec.d_type = QType::NSEC; rec.d_type = QType::NSEC;
rec.d_content = std::make_shared<NSECRecordContent>(nrc); rec.d_content = std::make_shared<NSECRecordContent>(std::move(nrc));
rec.d_place = DNSResourceRecord::AUTHORITY; rec.d_place = DNSResourceRecord::AUTHORITY;
records.push_back(rec); records.push_back(rec);
} }
static void addNSEC3RecordToLW(const DNSName& hashedName, const std::string& has hedNext, const std::string& salt, unsigned int iterations, const std::set<uint16 _t>& types, uint32_t ttl, std::vector<DNSRecord>& records) void addNSEC3RecordToLW(const DNSName& hashedName, const std::string& hashedNext , const std::string& salt, unsigned int iterations, const std::set<uint16_t>& ty pes, uint32_t ttl, std::vector<DNSRecord>& records)
{ {
NSEC3RecordContent nrc; NSEC3RecordContent nrc;
nrc.d_algorithm = 1; nrc.d_algorithm = 1;
nrc.d_flags = 0; nrc.d_flags = 0;
nrc.d_iterations = iterations; nrc.d_iterations = iterations;
nrc.d_salt = salt; nrc.d_salt = salt;
nrc.d_nexthash = hashedNext; nrc.d_nexthash = hashedNext;
nrc.d_set = types; for (const auto& type : types) {
nrc.set(type);
}
DNSRecord rec; DNSRecord rec;
rec.d_name = hashedName; rec.d_name = hashedName;
rec.d_ttl = ttl; rec.d_ttl = ttl;
rec.d_type = QType::NSEC3; rec.d_type = QType::NSEC3;
rec.d_content = std::make_shared<NSEC3RecordContent>(nrc); rec.d_content = std::make_shared<NSEC3RecordContent>(std::move(nrc));
rec.d_place = DNSResourceRecord::AUTHORITY; rec.d_place = DNSResourceRecord::AUTHORITY;
records.push_back(rec); records.push_back(rec);
} }
static void addNSEC3UnhashedRecordToLW(const DNSName& domain, const DNSName& zon e, const std::string& next, const std::set<uint16_t>& types, uint32_t ttl, std: :vector<DNSRecord>& records, unsigned int iterations=10) void addNSEC3UnhashedRecordToLW(const DNSName& domain, const DNSName& zone, cons t std::string& next, const std::set<uint16_t>& types, uint32_t ttl, std::vector <DNSRecord>& records, unsigned int iterations)
{ {
static const std::string salt = "deadbeef"; static const std::string salt = "deadbeef";
std::string hashed = hashQNameWithSalt(salt, iterations, domain); std::string hashed = hashQNameWithSalt(salt, iterations, domain);
addNSEC3RecordToLW(DNSName(toBase32Hex(hashed)) + zone, next, salt, iterations , types, ttl, records); addNSEC3RecordToLW(DNSName(toBase32Hex(hashed)) + zone, next, salt, iterations , types, ttl, records);
} }
static void addNSEC3NarrowRecordToLW(const DNSName& domain, const DNSName& zone, const std::set<uint16_t>& types, uint32_t ttl, std::vector<DNSRecord>& records , unsigned int iterations=10) void addNSEC3NarrowRecordToLW(const DNSName& domain, const DNSName& zone, const std::set<uint16_t>& types, uint32_t ttl, std::vector<DNSRecord>& records, unsig ned int iterations)
{ {
static const std::string salt = "deadbeef"; static const std::string salt = "deadbeef";
std::string hashed = hashQNameWithSalt(salt, iterations, domain); std::string hashed = hashQNameWithSalt(salt, iterations, domain);
std::string hashedNext(hashed); std::string hashedNext(hashed);
incrementHash(hashedNext); incrementHash(hashedNext);
decrementHash(hashed); decrementHash(hashed);
addNSEC3RecordToLW(DNSName(toBase32Hex(hashed)) + zone, hashedNext, salt, iter ations, types, ttl, records); addNSEC3RecordToLW(DNSName(toBase32Hex(hashed)) + zone, hashedNext, salt, iter ations, types, ttl, records);
} }
static void generateKeyMaterial(const DNSName& name, unsigned int algo, uint8_t digest, testkeysset_t& keys) void generateKeyMaterial(const DNSName& name, unsigned int algo, uint8_t digest, testkeysset_t& keys)
{ {
auto dcke = std::shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::make(algo) ); auto dcke = std::shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::make(algo) );
dcke->create((algo <= 10) ? 2048 : dcke->getBits()); dcke->create((algo <= 10) ? 2048 : dcke->getBits());
DNSSECPrivateKey dpk; DNSSECPrivateKey dpk;
dpk.d_flags = 256; dpk.d_flags = 256;
dpk.setKey(dcke); dpk.setKey(dcke);
DSRecordContent ds = makeDSFromDNSKey(name, dpk.getDNSKEY(), digest); DSRecordContent ds = makeDSFromDNSKey(name, dpk.getDNSKEY(), digest);
keys[name] = std::pair<DNSSECPrivateKey,DSRecordContent>(dpk,ds); keys[name] = std::pair<DNSSECPrivateKey,DSRecordContent>(dpk,ds);
} }
static void generateKeyMaterial(const DNSName& name, unsigned int algo, uint8_t digest, testkeysset_t& keys, map<DNSName,dsmap_t>& dsAnchors) void generateKeyMaterial(const DNSName& name, unsigned int algo, uint8_t digest, testkeysset_t& keys, map<DNSName,dsmap_t>& dsAnchors)
{ {
generateKeyMaterial(name, algo, digest, keys); generateKeyMaterial(name, algo, digest, keys);
dsAnchors[name].insert(keys[name].second); dsAnchors[name].insert(keys[name].second);
} }
static int genericDSAndDNSKEYHandler(LWResult* res, const DNSName& domain, DNSNa me auth, int type, const testkeysset_t& keys, bool proveCut=true) int genericDSAndDNSKEYHandler(LWResult* res, const DNSName& domain, DNSName auth , int type, const testkeysset_t& keys, bool proveCut)
{ {
if (type == QType::DS) { if (type == QType::DS) {
auth.chopOff(); auth.chopOff();
setLWResult(res, 0, true, false, true); setLWResult(res, 0, true, false, true);
if (addDS(domain, 300, res->d_records, keys, DNSResourceRecord::ANSWER)) { if (addDS(domain, 300, res->d_records, keys, DNSResourceRecord::ANSWER)) {
addRRSIG(keys, res->d_records, auth, 300); addRRSIG(keys, res->d_records, auth, 300);
} }
else { else {
skipping to change at line 448 skipping to change at line 464
if (type == QType::DNSKEY) { if (type == QType::DNSKEY) {
setLWResult(res, 0, true, false, true); setLWResult(res, 0, true, false, true);
addDNSKEY(keys, domain, 300, res->d_records); addDNSKEY(keys, domain, 300, res->d_records);
addRRSIG(keys, res->d_records, domain, 300); addRRSIG(keys, res->d_records, domain, 300);
return 1; return 1;
} }
return 0; return 0;
} }
/* Real tests */ int basicRecordsForQnameMinimisation(LWResult* res, const DNSName& domain, int t
ype) {
BOOST_AUTO_TEST_SUITE(syncres_cc) if (domain == DNSName(".") && type == QType::A) {
setLWResult(res, 0, true);
BOOST_AUTO_TEST_CASE(test_root_primed) { addRecordToLW(res, DNSName("."), QType::SOA, "a.root-servers.net. nstld.veri
std::unique_ptr<SyncRes> sr; sign-grs.com. 2019042400 1800 900 604800 86400", DNSResourceRecord::AUTHORITY);
initSR(sr); return 1;
primeHints();
const DNSName target("a.root-servers.net.");
/* we are primed, we should be able to resolve A a.root-servers.net. without a
ny query */
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK(ret[0].d_type == QType::A);
BOOST_CHECK_EQUAL(ret[0].d_name, target);
ret.clear();
res = sr->beginResolve(target, QType(QType::AAAA), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK(ret[0].d_type == QType::AAAA);
BOOST_CHECK_EQUAL(ret[0].d_name, target);
}
BOOST_AUTO_TEST_CASE(test_root_primed_ns) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
const DNSName target(".");
/* we are primed, but we should not be able to NS . without any query
because the . NS entry is not stored as authoritative */
size_t queriesCount = 0;
sr->setAsyncCallback([target,&queriesCount](const ComboAddress& ip, const DNSN
ame& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct time
val* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContex
t&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
if (domain == target && type == QType::NS) {
setLWResult(res, 0, true, false, true);
char addr[] = "a.root-servers.net.";
for (char idx = 'a'; idx <= 'm'; idx++) {
addr[0] = idx;
addRecordToLW(res, g_rootdnsname, QType::NS, std::string(addr), DNSRes
ourceRecord::ANSWER, 3600);
}
addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSRes
ourceRecord::ADDITIONAL, 3600);
addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2
:30", DNSResourceRecord::ADDITIONAL, 3600);
return 1;
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_REQUIRE_EQUAL(ret.size(), 13);
BOOST_CHECK_EQUAL(queriesCount, 1);
}
BOOST_AUTO_TEST_CASE(test_root_not_primed) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
size_t queriesCount = 0;
sr->setAsyncCallback([&queriesCount](const ComboAddress& ip, const DNSName& do
main, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* no
w, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> con
text, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
if (domain == g_rootdnsname && type == QType::NS) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, g_rootdnsname, QType::NS, "a.root-servers.net.", DNSR
esourceRecord::ANSWER, 3600);
addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSRes
ourceRecord::ADDITIONAL, 3600);
addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2
:30", DNSResourceRecord::ADDITIONAL, 3600);
return 1;
}
return 0;
});
/* we are not primed yet, so SyncRes will have to call primeHints()
then call getRootNS(), for which at least one of the root servers needs to
answer */
vector<DNSRecord> ret;
int res = sr->beginResolve(DNSName("."), QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(ret.size(), 1);
BOOST_CHECK_EQUAL(queriesCount, 2);
}
BOOST_AUTO_TEST_CASE(test_root_not_primed_and_no_response) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
std::set<ComboAddress> downServers;
/* we are not primed yet, so SyncRes will have to call primeHints()
then call getRootNS(), for which at least one of the root servers needs to
answer.
None will, so it should ServFail.
*/
sr->setAsyncCallback([&downServers](const ComboAddress& ip, const DNSName& dom
ain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now
, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> cont
ext, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
downServers.insert(ip);
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(DNSName("."), QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::ServFail);
BOOST_CHECK_EQUAL(ret.size(), 0);
BOOST_CHECK(downServers.size() > 0);
/* we explicitly refuse to mark the root servers down */
for (const auto& server : downServers) {
BOOST_CHECK_EQUAL(SyncRes::getServerFailsCount(server), 0);
}
}
BOOST_AUTO_TEST_CASE(test_edns_formerr_fallback) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
ComboAddress noEDNSServer;
size_t queriesWithEDNS = 0;
size_t queriesWithoutEDNS = 0;
sr->setAsyncCallback([&queriesWithEDNS, &queriesWithoutEDNS, &noEDNSServer](co
nst ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQu
ery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boo
st::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgo
ingLogger, LWResult* res) {
if (EDNS0Level != 0) {
queriesWithEDNS++;
noEDNSServer = ip;
setLWResult(res, RCode::FormErr);
return 1;
}
queriesWithoutEDNS++;
if (domain == DNSName("powerdns.com") && type == QType::A && !doTCP) {
setLWResult(res, 0, true, false, false);
addRecordToLW(res, domain, QType::A, "192.0.2.1");
return 1;
}
return 0;
});
primeHints();
/* fake that the root NS doesn't handle EDNS, check that we fallback */
vector<DNSRecord> ret;
int res = sr->beginResolve(DNSName("powerdns.com."), QType(QType::A), QClass::
IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(ret.size(), 1);
BOOST_CHECK_EQUAL(queriesWithEDNS, 1);
BOOST_CHECK_EQUAL(queriesWithoutEDNS, 1);
BOOST_CHECK_EQUAL(SyncRes::getEDNSStatusesSize(), 1);
BOOST_CHECK_EQUAL(SyncRes::getEDNSStatus(noEDNSServer), SyncRes::EDNSStatus::N
OEDNS);
}
BOOST_AUTO_TEST_CASE(test_edns_notimp_fallback) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
size_t queriesWithEDNS = 0;
size_t queriesWithoutEDNS = 0;
sr->setAsyncCallback([&queriesWithEDNS, &queriesWithoutEDNS](const ComboAddres
s& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0L
evel, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<co
nst ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWRe
sult* res) {
if (EDNS0Level != 0) {
queriesWithEDNS++;
setLWResult(res, RCode::NotImp);
return 1;
}
queriesWithoutEDNS++;
if (domain == DNSName("powerdns.com") && type == QType::A && !doTCP) {
setLWResult(res, 0, true, false, false);
addRecordToLW(res, domain, QType::A, "192.0.2.1");
return 1;
}
return 0;
});
primeHints();
/* fake that the NS doesn't handle EDNS, check that we fallback */
vector<DNSRecord> ret;
int res = sr->beginResolve(DNSName("powerdns.com."), QType(QType::A), QClass::
IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(ret.size(), 1);
BOOST_CHECK_EQUAL(queriesWithEDNS, 1);
BOOST_CHECK_EQUAL(queriesWithoutEDNS, 1);
}
BOOST_AUTO_TEST_CASE(test_tc_fallback_to_tcp) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
sr->setAsyncCallback([](const ComboAddress& ip, const DNSName& domain, int typ
e, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::opt
ional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::sh
ared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
if (!doTCP) {
setLWResult(res, 0, false, true, false);
return 1;
}
if (domain == DNSName("powerdns.com") && type == QType::A && doTCP) {
setLWResult(res, 0, true, false, false);
addRecordToLW(res, domain, QType::A, "192.0.2.1");
return 1;
}
return 0;
});
primeHints();
/* fake that the NS truncates every request over UDP, we should fallback to TC
P */
vector<DNSRecord> ret;
int res = sr->beginResolve(DNSName("powerdns.com."), QType(QType::A), QClass::
IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
}
BOOST_AUTO_TEST_CASE(test_tc_over_tcp) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
size_t tcpQueriesCount = 0;
sr->setAsyncCallback([&tcpQueriesCount](const ComboAddress& ip, const DNSName&
domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval*
now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&>
context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
if (!doTCP) {
setLWResult(res, 0, true, true, false);
return 1;
}
/* first TCP query is answered with a TC response */
tcpQueriesCount++;
if (tcpQueriesCount == 1) {
setLWResult(res, 0, true, true, false);
}
else {
setLWResult(res, 0, true, false, false);
}
addRecordToLW(res, domain, QType::A, "192.0.2.1");
return 1;
});
primeHints();
vector<DNSRecord> ret;
int res = sr->beginResolve(DNSName("powerdns.com."), QType(QType::A), QClass::
IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(tcpQueriesCount, 2);
}
BOOST_AUTO_TEST_CASE(test_all_nss_down) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
std::set<ComboAddress> downServers;
primeHints();
sr->setAsyncCallback([&downServers](const ComboAddress& ip, const DNSName& dom
ain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now
, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> cont
ext, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResource
Record::AUTHORITY, 172800);
addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSReso
urceRecord::ADDITIONAL, 3600);
addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DN
SResourceRecord::ADDITIONAL, 3600);
return 1;
}
else if (ip == ComboAddress("192.0.2.1:53") || ip == ComboAddress("[2001:D
B8::1]:53")) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns
.com.", DNSResourceRecord::AUTHORITY, 172800);
addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns
.com.", DNSResourceRecord::AUTHORITY, 172800);
addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.2
", DNSResourceRecord::ADDITIONAL, 172800);
addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::AAAA, "2001:D
B8::2", DNSResourceRecord::ADDITIONAL, 172800);
addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::A, "192.0.2.3
", DNSResourceRecord::ADDITIONAL, 172800);
addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::AAAA, "2001:D
B8::3", DNSResourceRecord::ADDITIONAL, 172800);
return 1;
}
else {
downServers.insert(ip);
return 0;
}
});
DNSName target("powerdns.com.");
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::ServFail);
BOOST_CHECK_EQUAL(ret.size(), 0);
BOOST_CHECK_EQUAL(downServers.size(), 4);
for (const auto& server : downServers) {
BOOST_CHECK_EQUAL(SyncRes::getServerFailsCount(server), 1);
BOOST_CHECK(SyncRes::isThrottled(time(nullptr), server, target, QType::A));
}
}
BOOST_AUTO_TEST_CASE(test_all_nss_network_error) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
std::set<ComboAddress> downServers;
primeHints();
sr->setAsyncCallback([&downServers](const ComboAddress& ip, const DNSName& dom
ain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now
, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> cont
ext, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResource
Record::AUTHORITY, 172800);
addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSReso
urceRecord::ADDITIONAL, 3600);
addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DN
SResourceRecord::ADDITIONAL, 3600);
return 1;
}
else if (ip == ComboAddress("192.0.2.1:53") || ip == ComboAddress("[2001:D
B8::1]:53")) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns
.com.", DNSResourceRecord::AUTHORITY, 172800);
addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns
.com.", DNSResourceRecord::AUTHORITY, 172800);
addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.2
", DNSResourceRecord::ADDITIONAL, 172800);
addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::AAAA, "2001:D
B8::2", DNSResourceRecord::ADDITIONAL, 172800);
addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::A, "192.0.2.3
", DNSResourceRecord::ADDITIONAL, 172800);
addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::AAAA, "2001:D
B8::3", DNSResourceRecord::ADDITIONAL, 172800);
return 1;
}
else {
downServers.insert(ip);
return 0;
}
});
/* exact same test than the previous one, except instead of a time out we fake
a network error */
DNSName target("powerdns.com.");
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::ServFail);
BOOST_CHECK_EQUAL(ret.size(), 0);
BOOST_CHECK_EQUAL(downServers.size(), 4);
for (const auto& server : downServers) {
BOOST_CHECK_EQUAL(SyncRes::getServerFailsCount(server), 1);
BOOST_CHECK(SyncRes::isThrottled(time(nullptr), server, target, QType::A));
}
}
BOOST_AUTO_TEST_CASE(test_only_one_ns_up_resolving_itself_with_glue) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
DNSName target("www.powerdns.com.");
sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, i
nt type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boos
t::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, s
td::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
if (domain == target) {
addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerd
ns.com.", DNSResourceRecord::AUTHORITY, 172800);
addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerd
ns.net.", DNSResourceRecord::AUTHORITY, 172800);
addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2
.2", DNSResourceRecord::ADDITIONAL, 172800);
addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::AAAA, "2001
:DB8::2", DNSResourceRecord::ADDITIONAL, 172800);
}
else if (domain == DNSName("pdns-public-ns2.powerdns.net.")) {
addRecordToLW(res, "powerdns.net.", QType::NS, "pdns-public-ns2.powerd
ns.net.", DNSResourceRecord::AUTHORITY, 172800);
addRecordToLW(res, "powerdns.net.", QType::NS, "pdns-public-ns1.powerd
ns.com.", DNSResourceRecord::AUTHORITY, 172800);
addRecordToLW(res, "pdns-public-ns2.powerdns.net.", QType::A, "192.0.2
.3", DNSResourceRecord::ADDITIONAL, 172800);
addRecordToLW(res, "pdns-public-ns2.powerdns.net.", QType::AAAA, "2001
:DB8::3", DNSResourceRecord::ADDITIONAL, 172800);
}
return 1;
}
else if (ip == ComboAddress("192.0.2.3:53")) {
setLWResult(res, 0, true, false, true);
if (domain == DNSName("pdns-public-ns2.powerdns.net.")) {
if (type == QType::A) {
addRecordToLW(res, "pdns-public-ns2.powerdns.net.", QType::A, "192.0
.2.3");
}
else if (type == QType::AAAA) {
addRecordToLW(res, "pdns-public-ns2.powerdns.net.", QType::AAAA, "20
01:DB8::3");
}
}
else if (domain == target) {
if (type == QType::A) {
addRecordToLW(res, domain, QType::A, "192.0.2.1");
}
else if (type == QType::AAAA) {
addRecordToLW(res, domain, QType::AAAA, "2001:DB8::1");
}
}
return 1;
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(ret.size(), 1);
}
BOOST_AUTO_TEST_CASE(test_os_limit_errors) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
std::set<ComboAddress> downServers;
primeHints();
sr->setAsyncCallback([&downServers](const ComboAddress& ip, const DNSName& dom
ain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now
, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> cont
ext, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResource
Record::AUTHORITY, 172800);
addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSReso
urceRecord::ADDITIONAL, 3600);
addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DN
SResourceRecord::ADDITIONAL, 3600);
return 1;
}
else if (ip == ComboAddress("192.0.2.1:53") || ip == ComboAddress("[2001:D
B8::1]:53")) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns
.com.", DNSResourceRecord::AUTHORITY, 172800);
addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns
.com.", DNSResourceRecord::AUTHORITY, 172800);
addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.2
", DNSResourceRecord::ADDITIONAL, 172800);
addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::AAAA, "2001:D
B8::2", DNSResourceRecord::ADDITIONAL, 172800);
addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::A, "192.0.2.3
", DNSResourceRecord::ADDITIONAL, 172800);
addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::AAAA, "2001:D
B8::3", DNSResourceRecord::ADDITIONAL, 172800);
return 1;
}
else {
if (downServers.size() < 3) {
/* only the last one will answer */
downServers.insert(ip);
return -2;
}
else {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, "powerdns.com.", QType::A, "192.0.2.42");
return 1;
}
}
});
DNSName target("powerdns.com.");
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(ret.size(), 1);
BOOST_CHECK_EQUAL(downServers.size(), 3);
/* Error is reported as "OS limit error" (-2) so the servers should _NOT_ be m
arked down */
for (const auto& server : downServers) {
BOOST_CHECK_EQUAL(SyncRes::getServerFailsCount(server), 0);
BOOST_CHECK(!SyncRes::isThrottled(time(nullptr), server, target, QType::A));
}
}
BOOST_AUTO_TEST_CASE(test_glued_referral) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
const DNSName target("powerdns.com.");
sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, i
nt type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boos
t::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, s
td::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
/* this will cause issue with qname minimization if we ever implement it *
/
if (domain != target) {
return 0;
}
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResource
Record::AUTHORITY, 172800);
addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSReso
urceRecord::ADDITIONAL, 3600);
addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DN
SResourceRecord::ADDITIONAL, 3600);
return 1;
}
else if (ip == ComboAddress("192.0.2.1:53") || ip == ComboAddress("[2001:D
B8::1]:53")) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns
.com.", DNSResourceRecord::AUTHORITY, 172800);
addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns
.com.", DNSResourceRecord::AUTHORITY, 172800);
addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.2
", DNSResourceRecord::ADDITIONAL, 172800);
addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::AAAA, "2001:D
B8::2", DNSResourceRecord::ADDITIONAL, 172800);
addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::A, "192.0.2.3
", DNSResourceRecord::ADDITIONAL, 172800);
addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::AAAA, "2001:D
B8::3", DNSResourceRecord::ADDITIONAL, 172800);
return 1;
}
else if (ip == ComboAddress("192.0.2.2:53") || ip == ComboAddress("192.0.2
.3:53") || ip == ComboAddress("[2001:DB8::2]:53") || ip == ComboAddress("[2001:D
B8::3]:53")) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, target, QType::A, "192.0.2.4");
return 1;
}
else {
return 0;
}
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK(ret[0].d_type == QType::A);
BOOST_CHECK_EQUAL(ret[0].d_name, target);
}
BOOST_AUTO_TEST_CASE(test_glueless_referral) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
const DNSName target("powerdns.com.");
sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, i
nt type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boos
t::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, s
td::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
if (domain.isPartOf(DNSName("com."))) {
addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResour
ceRecord::AUTHORITY, 172800);
} else if (domain.isPartOf(DNSName("org."))) {
addRecordToLW(res, "org.", QType::NS, "a.gtld-servers.net.", DNSResour
ceRecord::AUTHORITY, 172800);
}
else {
setLWResult(res, RCode::NXDomain, false, false, true);
return 1;
}
addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSReso
urceRecord::ADDITIONAL, 3600);
addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DN
SResourceRecord::ADDITIONAL, 3600);
return 1;
}
else if (ip == ComboAddress("192.0.2.1:53") || ip == ComboAddress("[2001:D
B8::1]:53")) {
if (domain == target) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerd
ns.org.", DNSResourceRecord::AUTHORITY, 172800);
addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerd
ns.org.", DNSResourceRecord::AUTHORITY, 172800);
return 1;
}
else if (domain == DNSName("pdns-public-ns1.powerdns.org.")) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, "pdns-public-ns1.powerdns.org.", QType::A, "192.0.2
.2");
addRecordToLW(res, "pdns-public-ns1.powerdns.org.", QType::AAAA, "2001
:DB8::2");
return 1;
}
else if (domain == DNSName("pdns-public-ns2.powerdns.org.")) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, "pdns-public-ns2.powerdns.org.", QType::A, "192.0.2
.3");
addRecordToLW(res, "pdns-public-ns2.powerdns.org.", QType::AAAA, "2001
:DB8::3");
return 1;
}
setLWResult(res, RCode::NXDomain, false, false, true);
return 1;
}
else if (ip == ComboAddress("192.0.2.2:53") || ip == ComboAddress("192.0.2
.3:53") || ip == ComboAddress("[2001:DB8::2]:53") || ip == ComboAddress("[2001:D
B8::3]:53")) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, target, QType::A, "192.0.2.4");
return 1;
}
else {
return 0;
}
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK(ret[0].d_type == QType::A);
BOOST_CHECK_EQUAL(ret[0].d_name, target);
}
BOOST_AUTO_TEST_CASE(test_edns_submask_by_domain) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
const DNSName target("powerdns.com.");
SyncRes::addEDNSDomain(target);
EDNSSubnetOpts incomingECS;
incomingECS.source = Netmask("192.0.2.128/32");
sr->setIncomingECSFound(true);
sr->setIncomingECS(incomingECS);
sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, i
nt type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boos
t::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, s
td::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
BOOST_REQUIRE(srcmask);
BOOST_CHECK_EQUAL(srcmask->toString(), "192.0.2.0/24");
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::ServFail);
}
BOOST_AUTO_TEST_CASE(test_edns_submask_by_addr) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
const DNSName target("powerdns.com.");
SyncRes::addEDNSSubnet(Netmask("192.0.2.1/32"));
EDNSSubnetOpts incomingECS;
incomingECS.source = Netmask("2001:DB8::FF/128");
sr->setIncomingECSFound(true);
sr->setIncomingECS(incomingECS);
sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, i
nt type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boos
t::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, s
td::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
if (isRootServer(ip)) {
BOOST_REQUIRE(!srcmask);
setLWResult(res, 0, false, false, true);
addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResource
Record::AUTHORITY, 172800);
addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSReso
urceRecord::ADDITIONAL, 3600);
return 1;
} else if (ip == ComboAddress("192.0.2.1:53")) {
BOOST_REQUIRE(srcmask);
BOOST_CHECK_EQUAL(srcmask->toString(), "2001:db8::/56");
setLWResult(res, 0, true, false, false);
addRecordToLW(res, domain, QType::A, "192.0.2.2");
return 1;
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK(ret[0].d_type == QType::A);
BOOST_CHECK_EQUAL(ret[0].d_name, target);
}
BOOST_AUTO_TEST_CASE(test_following_cname) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
const DNSName target("cname.powerdns.com.");
const DNSName cnameTarget("cname-target.powerdns.com");
sr->setAsyncCallback([target, cnameTarget](const ComboAddress& ip, const DNSNa
me& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timev
al* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext
&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResource
Record::AUTHORITY, 172800);
addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSReso
urceRecord::ADDITIONAL, 3600);
return 1;
} else if (ip == ComboAddress("192.0.2.1:53")) {
if (domain == target) {
setLWResult(res, 0, true, false, false);
addRecordToLW(res, domain, QType::CNAME, cnameTarget.toString());
return 1;
}
else if (domain == cnameTarget) {
setLWResult(res, 0, true, false, false);
addRecordToLW(res, domain, QType::A, "192.0.2.2");
}
return 1;
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_REQUIRE_EQUAL(ret.size(), 2);
BOOST_CHECK(ret[0].d_type == QType::CNAME);
BOOST_CHECK_EQUAL(ret[0].d_name, target);
BOOST_CHECK(ret[1].d_type == QType::A);
BOOST_CHECK_EQUAL(ret[1].d_name, cnameTarget);
}
BOOST_AUTO_TEST_CASE(test_cname_nxdomain) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
const DNSName target("cname.powerdns.com.");
const DNSName cnameTarget("cname-target.powerdns.com");
sr->setAsyncCallback([target, cnameTarget](const ComboAddress& ip, const DNSNa
me& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timev
al* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext
&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "powerdns.com.", QType::NS, "a.gtld-servers.net.", DN
SResourceRecord::AUTHORITY, 172800);
addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSReso
urceRecord::ADDITIONAL, 3600);
return 1;
} else if (ip == ComboAddress("192.0.2.1:53")) {
if (domain == target) {
setLWResult(res, RCode::NXDomain, true, false, false);
addRecordToLW(res, domain, QType::CNAME, cnameTarget.toString());
addRecordToLW(res, "powerdns.com.", QType::SOA, "a.powerdns.com. nstld
.verisign-grs.com. 2017032800 1800 900 604800 86400", DNSResourceRecord::AUTHORI
TY, 86400);
} else if (domain == cnameTarget) {
setLWResult(res, RCode::NXDomain, true, false, false);
addRecordToLW(res, "powerdns.com.", QType::SOA, "a.powerdns.com. nstld
.verisign-grs.com. 2017032800 1800 900 604800 86400", DNSResourceRecord::AUTHORI
TY, 86400);
return 1;
}
return 1;
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NXDomain);
BOOST_REQUIRE_EQUAL(ret.size(), 2);
BOOST_CHECK(ret[0].d_type == QType::CNAME);
BOOST_CHECK_EQUAL(ret[0].d_name, target);
BOOST_CHECK(ret[1].d_type == QType::SOA);
/* a second time, to check the cache */
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NXDomain);
BOOST_REQUIRE_EQUAL(ret.size(), 2);
BOOST_CHECK(ret[0].d_type == QType::CNAME);
BOOST_CHECK_EQUAL(ret[0].d_name, target);
BOOST_CHECK(ret[1].d_type == QType::SOA);
}
BOOST_AUTO_TEST_CASE(test_included_poisonous_cname) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
/* In this test we directly get the NS server for cname.powerdns.com.,
and we don't know whether it's also authoritative for
cname-target.powerdns.com or powerdns.com, so we shouldn't accept
the additional A record for cname-target.powerdns.com. */
const DNSName target("cname.powerdns.com.");
const DNSName cnameTarget("cname-target.powerdns.com");
sr->setAsyncCallback([target, cnameTarget](const ComboAddress& ip, const DNSNa
me& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timev
al* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext
&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResource
Record::AUTHORITY, 172800);
addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSReso
urceRecord::ADDITIONAL, 3600);
return 1;
} else if (ip == ComboAddress("192.0.2.1:53")) {
if (domain == target) {
setLWResult(res, 0, true, false, false);
addRecordToLW(res, domain, QType::CNAME, cnameTarget.toString());
addRecordToLW(res, cnameTarget, QType::A, "192.0.2.2", DNSResourceReco
rd::ADDITIONAL);
return 1;
} else if (domain == cnameTarget) {
setLWResult(res, 0, true, false, false);
addRecordToLW(res, cnameTarget, QType::A, "192.0.2.3");
return 1;
}
return 1;
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_REQUIRE_EQUAL(ret.size(), 2);
BOOST_REQUIRE(ret[0].d_type == QType::CNAME);
BOOST_CHECK_EQUAL(ret[0].d_name, target);
BOOST_CHECK_EQUAL(getRR<CNAMERecordContent>(ret[0])->getTarget(), cnameTarget)
;
BOOST_REQUIRE(ret[1].d_type == QType::A);
BOOST_CHECK_EQUAL(ret[1].d_name, cnameTarget);
BOOST_CHECK(getRR<ARecordContent>(ret[1])->getCA() == ComboAddress("192.0.2.3"
));
}
BOOST_AUTO_TEST_CASE(test_cname_loop) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
size_t count = 0;
const DNSName target("cname.powerdns.com.");
sr->setAsyncCallback([target,&count](const ComboAddress& ip, const DNSName& do
main, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* no
w, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> con
text, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
count++;
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResource
Record::AUTHORITY, 172800);
addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSReso
urceRecord::ADDITIONAL, 3600);
return 1;
} else if (ip == ComboAddress("192.0.2.1:53")) {
if (domain == target) {
setLWResult(res, 0, true, false, false);
addRecordToLW(res, domain, QType::CNAME, domain.toString());
return 1;
}
return 1;
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::ServFail);
BOOST_CHECK_GT(ret.size(), 0);
BOOST_CHECK_EQUAL(count, 2);
}
BOOST_AUTO_TEST_CASE(test_cname_depth) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
size_t depth = 0;
const DNSName target("cname.powerdns.com.");
sr->setAsyncCallback([target,&depth](const ComboAddress& ip, const DNSName& do
main, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* no
w, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> con
text, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResource
Record::AUTHORITY, 172800);
addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSReso
urceRecord::ADDITIONAL, 3600);
return 1;
} else if (ip == ComboAddress("192.0.2.1:53")) {
setLWResult(res, 0, true, false, false);
addRecordToLW(res, domain, QType::CNAME, std::to_string(depth) + "-cname
.powerdns.com");
depth++;
return 1;
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::ServFail);
BOOST_CHECK_EQUAL(ret.size(), depth);
/* we have an arbitrary limit at 10 when following a CNAME chain */
BOOST_CHECK_EQUAL(depth, 10 + 2);
}
BOOST_AUTO_TEST_CASE(test_time_limit) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
size_t queries = 0;
const DNSName target("cname.powerdns.com.");
sr->setAsyncCallback([target,&queries](const ComboAddress& ip, const DNSName&
domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval*
now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> c
ontext, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queries++;
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
/* Pretend that this query took 2000 ms */
res->d_usec = 2000;
addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResource
Record::AUTHORITY, 172800);
addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSReso
urceRecord::ADDITIONAL, 3600);
return 1;
} else if (ip == ComboAddress("192.0.2.1:53")) {
setLWResult(res, 0, true, false, false);
addRecordToLW(res, domain, QType::A, "192.0.2.2");
return 1;
}
return 0;
});
/* Set the maximum time to 1 ms */
SyncRes::s_maxtotusec = 1000;
try {
vector<DNSRecord> ret;
sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK(false);
}
catch(const ImmediateServFailException& e) {
}
BOOST_CHECK_EQUAL(queries, 1);
}
BOOST_AUTO_TEST_CASE(test_referral_depth) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
size_t queries = 0;
const DNSName target("www.powerdns.com.");
sr->setAsyncCallback([target,&queries](const ComboAddress& ip, const DNSName&
domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval*
now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> c
ontext, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queries++;
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
if (domain == DNSName("www.powerdns.com.")) {
addRecordToLW(res, domain, QType::NS, "ns.powerdns.com.", DNSResourceR
ecord::AUTHORITY, 172800);
}
else if (domain == DNSName("ns.powerdns.com.")) {
addRecordToLW(res, domain, QType::NS, "ns1.powerdns.org.", DNSResource
Record::AUTHORITY, 172800);
}
else if (domain == DNSName("ns1.powerdns.org.")) {
addRecordToLW(res, domain, QType::NS, "ns2.powerdns.org.", DNSResource
Record::AUTHORITY, 172800);
}
else if (domain == DNSName("ns2.powerdns.org.")) {
addRecordToLW(res, domain, QType::NS, "ns3.powerdns.org.", DNSResource
Record::AUTHORITY, 172800);
}
else if (domain == DNSName("ns3.powerdns.org.")) {
addRecordToLW(res, domain, QType::NS, "ns4.powerdns.org.", DNSResource
Record::AUTHORITY, 172800);
}
else if (domain == DNSName("ns4.powerdns.org.")) {
addRecordToLW(res, domain, QType::NS, "ns5.powerdns.org.", DNSResource
Record::AUTHORITY, 172800);
addRecordToLW(res, domain, QType::A, "192.0.2.1", DNSResourceRecord::A
UTHORITY, 172800);
}
return 1;
} else if (ip == ComboAddress("192.0.2.1:53")) {
setLWResult(res, 0, true, false, false);
addRecordToLW(res, domain, QType::A, "192.0.2.2");
return 1;
}
return 0;
});
/* Set the maximum depth low */
SyncRes::s_maxdepth = 10;
try {
vector<DNSRecord> ret;
sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK(false);
}
catch(const ImmediateServFailException& e) {
}
}
BOOST_AUTO_TEST_CASE(test_cname_qperq) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
size_t queries = 0;
const DNSName target("cname.powerdns.com.");
sr->setAsyncCallback([target,&queries](const ComboAddress& ip, const DNSName&
domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval*
now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> c
ontext, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queries++;
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResource
Record::AUTHORITY, 172800);
addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSReso
urceRecord::ADDITIONAL, 3600);
return 1;
} else if (ip == ComboAddress("192.0.2.1:53")) {
setLWResult(res, 0, true, false, false);
addRecordToLW(res, domain, QType::CNAME, std::to_string(queries) + "-cna
me.powerdns.com");
return 1;
}
return 0;
});
/* Set the maximum number of questions very low */
SyncRes::s_maxqperq = 5;
try {
vector<DNSRecord> ret;
sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK(false);
}
catch(const ImmediateServFailException& e) {
BOOST_CHECK_EQUAL(queries, SyncRes::s_maxqperq);
}
}
BOOST_AUTO_TEST_CASE(test_throttled_server) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
const DNSName target("throttled.powerdns.com.");
const ComboAddress ns("192.0.2.1:53");
size_t queriesToNS = 0;
sr->setAsyncCallback([target,ns,&queriesToNS](const ComboAddress& ip, const DN
SName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct ti
meval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveCont
ext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResource
Record::AUTHORITY, 172800);
addRecordToLW(res, "a.gtld-servers.net.", QType::A, ns.toString(), DNSRe
sourceRecord::ADDITIONAL, 3600);
return 1;
} else if (ip == ns) {
queriesToNS++;
setLWResult(res, 0, true, false, false);
addRecordToLW(res, domain, QType::A, "192.0.2.2");
return 1;
}
return 0;
});
/* mark ns as down */
SyncRes::doThrottle(time(nullptr), ns, SyncRes::s_serverdownthrottletime, 1000
0);
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::ServFail);
BOOST_CHECK_EQUAL(ret.size(), 0);
/* we should not have sent any queries to ns */
BOOST_CHECK_EQUAL(queriesToNS, 0);
}
BOOST_AUTO_TEST_CASE(test_throttled_server_count) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
const ComboAddress ns("192.0.2.1:53");
const size_t blocks = 10;
/* mark ns as down for 'blocks' queries */
SyncRes::doThrottle(time(nullptr), ns, SyncRes::s_serverdownthrottletime, bloc
ks);
for (size_t idx = 0; idx < blocks; idx++) {
BOOST_CHECK(SyncRes::isThrottled(time(nullptr), ns));
}
/* we have been throttled 'blocks' times, we should not be throttled anymore *
/
BOOST_CHECK(!SyncRes::isThrottled(time(nullptr), ns));
}
BOOST_AUTO_TEST_CASE(test_throttled_server_time) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
const ComboAddress ns("192.0.2.1:53");
const size_t seconds = 1;
/* mark ns as down for 'seconds' seconds */
SyncRes::doThrottle(time(nullptr), ns, seconds, 10000);
BOOST_CHECK(SyncRes::isThrottled(time(nullptr), ns));
sleep(seconds + 1);
/* we should not be throttled anymore */
BOOST_CHECK(!SyncRes::isThrottled(time(nullptr), ns));
}
BOOST_AUTO_TEST_CASE(test_dont_query_server) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
const DNSName target("throttled.powerdns.com.");
const ComboAddress ns("192.0.2.1:53");
size_t queriesToNS = 0;
sr->setAsyncCallback([target,ns,&queriesToNS](const ComboAddress& ip, const DN
SName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct ti
meval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveCont
ext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResource
Record::AUTHORITY, 172800);
addRecordToLW(res, "a.gtld-servers.net.", QType::A, ns.toString(), DNSRe
sourceRecord::ADDITIONAL, 3600);
return 1;
} else if (ip == ns) {
queriesToNS++;
setLWResult(res, 0, true, false, false);
addRecordToLW(res, domain, QType::A, "192.0.2.2");
return 1;
}
return 0;
});
/* prevent querying this NS */
SyncRes::addDontQuery(Netmask(ns));
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::ServFail);
BOOST_CHECK_EQUAL(ret.size(), 0);
/* we should not have sent any queries to ns */
BOOST_CHECK_EQUAL(queriesToNS, 0);
}
BOOST_AUTO_TEST_CASE(test_root_nx_trust) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
const DNSName target1("powerdns.com.");
const DNSName target2("notpowerdns.com.");
const ComboAddress ns("192.0.2.1:53");
size_t queriesCount = 0;
sr->setAsyncCallback([target1, target2, ns, &queriesCount](const ComboAddress&
ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Lev
el, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<cons
t ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResu
lt* res) {
queriesCount++;
if (isRootServer(ip)) {
if (domain == target1) {
setLWResult(res, RCode::NXDomain, true, false, true);
addRecordToLW(res, ".", QType::SOA, "a.root-servers.net. nstld.verisig
n-grs.com. 2017032800 1800 900 604800 86400", DNSResourceRecord::AUTHORITY, 8640
0);
}
else {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResour
ceRecord::AUTHORITY, 172800);
addRecordToLW(res, "a.gtld-servers.net.", QType::A, ns.toString(), DNS
ResourceRecord::ADDITIONAL, 3600);
}
return 1;
} else if (ip == ns) {
setLWResult(res, 0, true, false, false);
addRecordToLW(res, domain, QType::A, "192.0.2.2");
return 1;
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target1, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NXDomain);
BOOST_CHECK_EQUAL(ret.size(), 1);
/* one for target1 and one for the entire TLD */
BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 2);
ret.clear();
res = sr->beginResolve(target2, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NXDomain);
BOOST_CHECK_EQUAL(ret.size(), 1);
/* one for target1 and one for the entire TLD */
BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 2);
/* we should have sent only one query */
BOOST_CHECK_EQUAL(queriesCount, 1);
}
BOOST_AUTO_TEST_CASE(test_root_nx_trust_specific) {
std::unique_ptr<SyncRes> sr;
init();
initSR(sr, true, false);
primeHints();
const DNSName target1("powerdns.com.");
const DNSName target2("notpowerdns.com.");
const ComboAddress ns("192.0.2.1:53");
size_t queriesCount = 0;
/* This time the root denies target1 with a "com." SOA instead of a "." one.
We should add target1 to the negcache, but not "com.". */
sr->setAsyncCallback([target1, target2, ns, &queriesCount](const ComboAddress&
ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Lev
el, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<cons
t ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResu
lt* res) {
queriesCount++;
if (isRootServer(ip)) {
if (domain == target1) {
setLWResult(res, RCode::NXDomain, true, false, true);
addRecordToLW(res, "com.", QType::SOA, "a.root-servers.net. nstld.veri
sign-grs.com. 2017032800 1800 900 604800 86400", DNSResourceRecord::AUTHORITY, 8
6400);
}
else {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResour
ceRecord::AUTHORITY, 172800);
addRecordToLW(res, "a.gtld-servers.net.", QType::A, ns.toString(), DNS
ResourceRecord::ADDITIONAL, 3600);
}
return 1;
} else if (ip == ns) {
setLWResult(res, 0, true, false, false);
addRecordToLW(res, domain, QType::A, "192.0.2.2");
return 1;
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target1, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NXDomain);
BOOST_CHECK_EQUAL(ret.size(), 1);
/* even with root-nx-trust on and a NX answer from the root,
we should not have cached the entire TLD this time. */
BOOST_CHECK_EQUAL(SyncRes::t_sstorage.negcache.size(), 1);
ret.clear();
res = sr->beginResolve(target2, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_REQUIRE(ret[0].d_type == QType::A);
BOOST_CHECK_EQUAL(ret[0].d_name, target2);
BOOST_CHECK(getRR<ARecordContent>(ret[0])->getCA() == ComboAddress("192.0.2.2"
));
BOOST_CHECK_EQUAL(SyncRes::t_sstorage.negcache.size(), 1);
BOOST_CHECK_EQUAL(queriesCount, 3);
}
BOOST_AUTO_TEST_CASE(test_root_nx_dont_trust) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
const DNSName target1("powerdns.com.");
const DNSName target2("notpowerdns.com.");
const ComboAddress ns("192.0.2.1:53");
size_t queriesCount = 0;
sr->setAsyncCallback([target1, target2, ns, &queriesCount](const ComboAddress&
ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Lev
el, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<cons
t ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResu
lt* res) {
queriesCount++;
if (isRootServer(ip)) {
if (domain == target1) {
setLWResult(res, RCode::NXDomain, true, false, true);
addRecordToLW(res, ".", QType::SOA, "a.root-servers.net. nstld.verisig
n-grs.com. 2017032800 1800 900 604800 86400", DNSResourceRecord::AUTHORITY, 8640
0);
}
else {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResour
ceRecord::AUTHORITY, 172800);
addRecordToLW(res, "a.gtld-servers.net.", QType::A, ns.toString(), DNS
ResourceRecord::ADDITIONAL, 3600);
}
return 1;
} else if (ip == ns) {
setLWResult(res, 0, true, false, false);
addRecordToLW(res, domain, QType::A, "192.0.2.2");
return 1;
}
return 0;
});
SyncRes::s_rootNXTrust = false;
vector<DNSRecord> ret;
int res = sr->beginResolve(target1, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NXDomain);
BOOST_CHECK_EQUAL(ret.size(), 1);
/* one for target1 */
BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 1);
ret.clear();
res = sr->beginResolve(target2, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(ret.size(), 1);
/* one for target1 */
BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 1);
/* we should have sent three queries */
BOOST_CHECK_EQUAL(queriesCount, 3);
}
BOOST_AUTO_TEST_CASE(test_skip_negcache_for_variable_response) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
const DNSName target("www.powerdns.com.");
const DNSName cnameTarget("cname.powerdns.com.");
SyncRes::addEDNSDomain(DNSName("powerdns.com."));
EDNSSubnetOpts incomingECS;
incomingECS.source = Netmask("192.0.2.128/32");
sr->setIncomingECSFound(true);
sr->setIncomingECS(incomingECS);
sr->setAsyncCallback([target,cnameTarget](const ComboAddress& ip, const DNSNam
e& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeva
l* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&
> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
BOOST_REQUIRE(srcmask);
BOOST_CHECK_EQUAL(srcmask->toString(), "192.0.2.0/24");
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns
.com.", DNSResourceRecord::AUTHORITY, 172800);
addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.1
", DNSResourceRecord::ADDITIONAL, 3600);
return 1;
} else if (ip == ComboAddress("192.0.2.1:53")) {
if (domain == target) {
/* Type 2 NXDOMAIN (rfc2308 section-2.1) */
setLWResult(res, RCode::NXDomain, true, false, true);
addRecordToLW(res, domain, QType::CNAME, cnameTarget.toString());
addRecordToLW(res, "powerdns.com", QType::SOA, "pdns-public-ns1.powerd
ns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSReso
urceRecord::AUTHORITY, 3600);
}
else if (domain == cnameTarget) {
/* we shouldn't get there since the Type NXDOMAIN should have been eno
ugh,
but we might if we still chase the CNAME. */
setLWResult(res, RCode::NXDomain, true, false, true);
addRecordToLW(res, "powerdns.com", QType::SOA, "pdns-public-ns1.powerd
ns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSReso
urceRecord::AUTHORITY, 3600);
}
return 1;
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NXDomain);
BOOST_CHECK_EQUAL(ret.size(), 2);
/* no negative cache entry because the response was variable */
BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 0);
}
BOOST_AUTO_TEST_CASE(test_ns_speed) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
const DNSName target("powerdns.com.");
std::map<ComboAddress, uint64_t> nsCounts;
sr->setAsyncCallback([target,&nsCounts](const ComboAddress& ip, const DNSName&
domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval*
now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&>
context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, domain, QType::NS, "pdns-public-ns1.powerdns.com.", D
NSResourceRecord::AUTHORITY, 172800);
addRecordToLW(res, domain, QType::NS, "pdns-public-ns2.powerdns.com.", D
NSResourceRecord::AUTHORITY, 172800);
addRecordToLW(res, domain, QType::NS, "pdns-public-ns3.powerdns.com.", D
NSResourceRecord::AUTHORITY, 172800);
addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.1
", DNSResourceRecord::ADDITIONAL, 3600);
addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::AAAA, "2001:D
B8::1", DNSResourceRecord::ADDITIONAL, 3600);
addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::A, "192.0.2.2
", DNSResourceRecord::ADDITIONAL, 3600);
addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::AAAA, "2001:D
B8::2", DNSResourceRecord::ADDITIONAL, 3600);
addRecordToLW(res, "pdns-public-ns3.powerdns.com.", QType::A, "192.0.2.3
", DNSResourceRecord::ADDITIONAL, 3600);
addRecordToLW(res, "pdns-public-ns3.powerdns.com.", QType::AAAA, "2001:D
B8::3", DNSResourceRecord::ADDITIONAL, 3600);
return 1;
} else {
nsCounts[ip]++;
if (ip == ComboAddress("[2001:DB8::2]:53") || ip == ComboAddress("192.0.
2.2:53")) {
BOOST_CHECK_LT(nsCounts.size(), 3);
/* let's time out on pdns-public-ns2.powerdns.com. */
return 0;
}
else if (ip == ComboAddress("192.0.2.1:53")) {
BOOST_CHECK_EQUAL(nsCounts.size(), 3);
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::A, "192.0.2.254");
return 1;
}
return 0;
}
return 0;
});
struct timeval now;
gettimeofday(&now, 0);
/* make pdns-public-ns2.powerdns.com. the fastest NS, with its IPv6 address fa
ster than the IPV4 one,
then pdns-public-ns1.powerdns.com. on IPv4 */
SyncRes::submitNSSpeed(DNSName("pdns-public-ns1.powerdns.com."), ComboAddress(
"192.0.2.1:53"), 100, &now);
SyncRes::submitNSSpeed(DNSName("pdns-public-ns1.powerdns.com."), ComboAddress(
"[2001:DB8::1]:53"), 10000, &now);
SyncRes::submitNSSpeed(DNSName("pdns-public-ns2.powerdns.com."), ComboAddress(
"192.0.2.2:53"), 10, &now);
SyncRes::submitNSSpeed(DNSName("pdns-public-ns2.powerdns.com."), ComboAddress(
"[2001:DB8::2]:53"), 1, &now);
SyncRes::submitNSSpeed(DNSName("pdns-public-ns3.powerdns.com."), ComboAddress(
"192.0.2.3:53"), 10000, &now);
SyncRes::submitNSSpeed(DNSName("pdns-public-ns3.powerdns.com."), ComboAddress(
"[2001:DB8::3]:53"), 10000, &now);
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(ret.size(), 1);
BOOST_CHECK_EQUAL(nsCounts.size(), 3);
BOOST_CHECK_EQUAL(nsCounts[ComboAddress("192.0.2.1:53")], 1);
BOOST_CHECK_EQUAL(nsCounts[ComboAddress("192.0.2.2:53")], 1);
BOOST_CHECK_EQUAL(nsCounts[ComboAddress("[2001:DB8::2]:53")], 1);
}
BOOST_AUTO_TEST_CASE(test_flawed_nsset) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
const DNSName target("powerdns.com.");
sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, i
nt type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boos
t::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, s
td::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, domain, QType::NS, "pdns-public-ns1.powerdns.com.", D
NSResourceRecord::AUTHORITY, 172800);
addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.1
", DNSResourceRecord::ADDITIONAL, 3600);
return 1;
} else if (ip == ComboAddress("192.0.2.1:53")) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::A, "192.0.2.254");
return 1;
}
return 0;
});
/* we populate the cache with a flawed NSset, i.e. there is a NS entry but no
corresponding glue */
time_t now = time(nullptr);
std::vector<DNSRecord> records;
std::vector<shared_ptr<RRSIGRecordContent> > sigs;
addRecordToList(records, target, QType::NS, "pdns-public-ns1.powerdns.com.", D
NSResourceRecord::AUTHORITY, now + 3600);
t_RC->replace(now, target, QType(QType::NS), records, sigs, vector<std::shared
_ptr<DNSRecord>>(), true, boost::optional<Netmask>());
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(ret.size(), 1);
}
BOOST_AUTO_TEST_CASE(test_completely_flawed_nsset) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
const DNSName target("powerdns.com.");
size_t queriesCount = 0;
sr->setAsyncCallback([&queriesCount,target](const ComboAddress& ip, const DNSN
ame& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct time
val* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContex
t&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
if (isRootServer(ip) && domain == target) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, domain, QType::NS, "pdns-public-ns2.powerdns.com.", D
NSResourceRecord::AUTHORITY, 172800);
addRecordToLW(res, domain, QType::NS, "pdns-public-ns3.powerdns.com.", D
NSResourceRecord::AUTHORITY, 172800);
return 1;
} else if (domain == DNSName("pdns-public-ns2.powerdns.com.") || domain ==
DNSName("pdns-public-ns3.powerdns.com.")){
setLWResult(res, 0, true, false, true);
addRecordToLW(res, ".", QType::SOA, "a.root-servers.net. nstld.verisign-
grs.com. 2017032800 1800 900 604800 86400", DNSResourceRecord::AUTHORITY, 86400)
;
return 1;
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::ServFail);
BOOST_CHECK_EQUAL(ret.size(), 0);
/* one query to get NSs, then A and AAAA for each NS */
BOOST_CHECK_EQUAL(queriesCount, 5);
}
BOOST_AUTO_TEST_CASE(test_cache_hit) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
const DNSName target("powerdns.com.");
sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, i
nt type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boos
t::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, s
td::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
return 0;
});
/* we populate the cache with eveything we need */
time_t now = time(nullptr);
std::vector<DNSRecord> records;
std::vector<shared_ptr<RRSIGRecordContent> > sigs;
addRecordToList(records, target, QType::A, "192.0.2.1", DNSResourceRecord::ANS
WER, now + 3600);
t_RC->replace(now, target , QType(QType::A), records, sigs, vector<std::shared
_ptr<DNSRecord>>(), true, boost::optional<Netmask>());
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(ret.size(), 1);
}
BOOST_AUTO_TEST_CASE(test_no_rd) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
const DNSName target("powerdns.com.");
size_t queriesCount = 0;
sr->setCacheOnly();
sr->setAsyncCallback([target,&queriesCount](const ComboAddress& ip, const DNSN
ame& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct time
val* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContex
t&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(ret.size(), 0);
BOOST_CHECK_EQUAL(queriesCount, 0);
}
BOOST_AUTO_TEST_CASE(test_cache_min_max_ttl) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
const DNSName target("cachettl.powerdns.com.");
const ComboAddress ns("192.0.2.1:53");
sr->setAsyncCallback([target,ns](const ComboAddress& ip, const DNSName& domain
, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, b
oost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context
, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResource
Record::AUTHORITY, 172800);
addRecordToLW(res, "a.gtld-servers.net.", QType::A, ns.toString(), DNSRe
sourceRecord::ADDITIONAL, 7200);
return 1;
} else if (ip == ns) {
setLWResult(res, 0, true, false, false);
addRecordToLW(res, domain, QType::A, "192.0.2.2", DNSResourceRecord::ANS
WER, 10);
return 1;
}
return 0;
});
const time_t now = time(nullptr);
SyncRes::s_minimumTTL = 60;
SyncRes::s_maxcachettl = 3600;
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK_EQUAL(ret[0].d_ttl, SyncRes::s_minimumTTL);
const ComboAddress who;
vector<DNSRecord> cached;
BOOST_REQUIRE_GT(t_RC->get(now, target, QType(QType::A), true, &cached, who),
0);
BOOST_REQUIRE_EQUAL(cached.size(), 1);
BOOST_REQUIRE_GT(cached[0].d_ttl, now);
BOOST_CHECK_EQUAL((cached[0].d_ttl - now), SyncRes::s_minimumTTL);
cached.clear();
BOOST_REQUIRE_GT(t_RC->get(now, target, QType(QType::NS), false, &cached, who)
, 0);
BOOST_REQUIRE_EQUAL(cached.size(), 1);
BOOST_REQUIRE_GT(cached[0].d_ttl, now);
BOOST_CHECK_LE((cached[0].d_ttl - now), SyncRes::s_maxcachettl);
}
BOOST_AUTO_TEST_CASE(test_cache_expired_ttl) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
const DNSName target("powerdns.com.");
sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, i
nt type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boos
t::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, s
td::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, domain, QType::NS, "pdns-public-ns1.powerdns.com.", D
NSResourceRecord::AUTHORITY, 172800);
addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.1
", DNSResourceRecord::ADDITIONAL, 3600);
return 1;
} else if (ip == ComboAddress("192.0.2.1:53")) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::A, "192.0.2.2");
return 1;
}
return 0;
});
/* we populate the cache with entries that expired 60s ago*/
time_t now = time(nullptr);
std::vector<DNSRecord> records;
std::vector<shared_ptr<RRSIGRecordContent> > sigs;
addRecordToList(records, target, QType::A, "192.0.2.42", DNSResourceRecord::AN
SWER, now - 60);
t_RC->replace(now - 3600, target, QType(QType::A), records, sigs, vector<std::
shared_ptr<DNSRecord>>(), true, boost::optional<Netmask>());
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_REQUIRE(ret[0].d_type == QType::A);
BOOST_CHECK_EQUAL(getRR<ARecordContent>(ret[0])->getCA().toStringWithPort(), C
omboAddress("192.0.2.2").toStringWithPort());
}
BOOST_AUTO_TEST_CASE(test_delegation_only) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
/* Thanks, Verisign */
SyncRes::addDelegationOnly(DNSName("com."));
SyncRes::addDelegationOnly(DNSName("net."));
const DNSName target("nx-powerdns.com.");
sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, i
nt type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boos
t::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, s
td::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResource
Record::AUTHORITY, 172800);
addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSReso
urceRecord::ADDITIONAL, 3600);
return 1;
} else if (ip == ComboAddress("192.0.2.1:53")) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::A, "192.0.2.42");
return 1;
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NXDomain);
BOOST_CHECK_EQUAL(ret.size(), 0);
}
BOOST_AUTO_TEST_CASE(test_unauth_any) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
const DNSName target("powerdns.com.");
sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, i
nt type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boos
t::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, s
td::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResource
Record::AUTHORITY, 172800);
addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSReso
urceRecord::ADDITIONAL, 3600);
return 1;
} else if (ip == ComboAddress("192.0.2.1:53")) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, domain, QType::A, "192.0.2.42");
return 1;
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::ANY), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::ServFail);
BOOST_CHECK_EQUAL(ret.size(), 0);
}
BOOST_AUTO_TEST_CASE(test_no_data) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
const DNSName target("powerdns.com.");
sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, i
nt type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boos
t::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, s
td::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
setLWResult(res, 0, true, false, true);
return 1;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(ret.size(), 0);
}
BOOST_AUTO_TEST_CASE(test_skip_opt_any) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
const DNSName target("powerdns.com.");
sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, i
nt type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boos
t::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, s
td::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::A, "192.0.2.42");
addRecordToLW(res, domain, QType::ANY, "0 0");
addRecordToLW(res, domain, QType::OPT, "");
return 1;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(ret.size(), 1);
}
BOOST_AUTO_TEST_CASE(test_nodata_nsec_nodnssec) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
const DNSName target("powerdns.com.");
sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, i
nt type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boos
t::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, s
td::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. piet
er\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::
AUTHORITY, 3600);
/* the NSEC and RRSIG contents are complete garbage, please ignore them */
addRecordToLW(res, domain, QType::NSEC, "deadbeef", DNSResourceRecord::AUT
HORITY);
addRecordToLW(res, domain, QType::RRSIG, "NSEC 5 2 600 2100010100000000 21
00010100000000 24567 dummy data", DNSResourceRecord::AUTHORITY);
addRecordToLW(res, domain, QType::RRSIG, "SOA 5 3 600 2100010100000000 210
0010100000000 24567 dummy data", DNSResourceRecord::AUTHORITY);
return 1;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(ret.size(), 1);
}
BOOST_AUTO_TEST_CASE(test_nodata_nsec_dnssec) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
primeHints();
const DNSName target("powerdns.com.");
sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, i
nt type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boos
t::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, s
td::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. piet
er\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::
AUTHORITY, 3600);
/* the NSEC and RRSIG contents are complete garbage, please ignore them */
addRecordToLW(res, domain, QType::NSEC, "deadbeef", DNSResourceRecord::AUT
HORITY);
addRecordToLW(res, domain, QType::RRSIG, "NSEC 5 2 600 2100010100000000 21
00010100000000 24567 dummy data", DNSResourceRecord::AUTHORITY);
addRecordToLW(res, domain, QType::RRSIG, "SOA 5 3 600 2100010100000000 210
0010100000000 24567 dummy data", DNSResourceRecord::AUTHORITY);
return 1;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(ret.size(), 4);
}
BOOST_AUTO_TEST_CASE(test_nx_nsec_nodnssec) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
const DNSName target("powerdns.com.");
sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, i
nt type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boos
t::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, s
td::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
setLWResult(res, RCode::NXDomain, true, false, true);
addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. piet
er\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::
AUTHORITY, 3600);
/* the NSEC and RRSIG contents are complete garbage, please ignore them */
addRecordToLW(res, domain, QType::NSEC, "deadbeef", DNSResourceRecord::AUT
HORITY);
addRecordToLW(res, domain, QType::RRSIG, "NSEC 5 2 600 2100010100000000 21
00010100000000 24567 dummy data", DNSResourceRecord::AUTHORITY);
addRecordToLW(res, domain, QType::RRSIG, "SOA 5 3 600 2100010100000000 210
0010100000000 24567 dummy data", DNSResourceRecord::AUTHORITY);
return 1;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NXDomain);
BOOST_CHECK_EQUAL(ret.size(), 1);
}
BOOST_AUTO_TEST_CASE(test_nx_nsec_dnssec) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
primeHints();
const DNSName target("powerdns.com.");
sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, i
nt type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boos
t::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, s
td::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
setLWResult(res, RCode::NXDomain, true, false, true);
addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. piet
er\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::
AUTHORITY, 3600);
/* the NSEC and RRSIG contents are complete garbage, please ignore them */
addRecordToLW(res, domain, QType::NSEC, "deadbeef", DNSResourceRecord::AUT
HORITY);
addRecordToLW(res, domain, QType::RRSIG, "NSEC 5 2 600 2100010100000000 21
00010100000000 24567 dummy data", DNSResourceRecord::AUTHORITY);
addRecordToLW(res, domain, QType::RRSIG, "SOA 5 3 600 2100010100000000 210
0010100000000 24567 dummy data", DNSResourceRecord::AUTHORITY);
return 1;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NXDomain);
BOOST_CHECK_EQUAL(ret.size(), 4);
}
BOOST_AUTO_TEST_CASE(test_qclass_none) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
/* apart from special names and QClass::ANY, anything else than QClass::IN sho
uld be rejected right away */
size_t queriesCount = 0;
sr->setAsyncCallback([&queriesCount](const ComboAddress& ip, const DNSName& do
main, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* no
w, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> con
text, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
return 0;
});
const DNSName target("powerdns.com.");
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::NONE, ret);
BOOST_CHECK_EQUAL(res, -1);
BOOST_CHECK_EQUAL(ret.size(), 0);
BOOST_CHECK_EQUAL(queriesCount, 0);
}
BOOST_AUTO_TEST_CASE(test_special_types) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
/* {A,I}XFR, RRSIG and NSEC3 should be rejected right away */
size_t queriesCount = 0;
sr->setAsyncCallback([&queriesCount](const ComboAddress& ip, const DNSName& do
main, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* no
w, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> con
text, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
cerr<<"asyncresolve called to ask "<<ip.toStringWithPort()<<" about "<<dom
ain.toString()<<" / "<<QType(type).getName()<<" over "<<(doTCP ? "TCP" : "UDP")<
<" (rd: "<<sendRDQuery<<", EDNS0 level: "<<EDNS0Level<<")"<<endl;
queriesCount++;
return 0;
});
const DNSName target("powerdns.com.");
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::AXFR), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, -1);
BOOST_CHECK_EQUAL(ret.size(), 0);
BOOST_CHECK_EQUAL(queriesCount, 0);
res = sr->beginResolve(target, QType(QType::IXFR), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, -1);
BOOST_CHECK_EQUAL(ret.size(), 0);
BOOST_CHECK_EQUAL(queriesCount, 0);
res = sr->beginResolve(target, QType(QType::RRSIG), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, -1);
BOOST_CHECK_EQUAL(ret.size(), 0);
BOOST_CHECK_EQUAL(queriesCount, 0);
res = sr->beginResolve(target, QType(QType::NSEC3), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, -1);
BOOST_CHECK_EQUAL(ret.size(), 0);
BOOST_CHECK_EQUAL(queriesCount, 0);
}
BOOST_AUTO_TEST_CASE(test_special_names) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
/* special names should be handled internally */
size_t queriesCount = 0;
sr->setAsyncCallback([&queriesCount](const ComboAddress& ip, const DNSName& do
main, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* no
w, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> con
text, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(DNSName("1.0.0.127.in-addr.arpa."), QType(QType::PT
R), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK(ret[0].d_type == QType::PTR);
BOOST_CHECK_EQUAL(queriesCount, 0);
ret.clear();
res = sr->beginResolve(DNSName("1.0.0.127.in-addr.arpa."), QType(QType::ANY),
QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK(ret[0].d_type == QType::PTR);
BOOST_CHECK_EQUAL(queriesCount, 0);
ret.clear();
res = sr->beginResolve(DNSName("1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.
0.0.0.0.0.0.0.0.0.ip6.arpa."), QType(QType::PTR), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK(ret[0].d_type == QType::PTR);
BOOST_CHECK_EQUAL(queriesCount, 0);
ret.clear();
res = sr->beginResolve(DNSName("1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.
0.0.0.0.0.0.0.0.0.ip6.arpa."), QType(QType::ANY), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK(ret[0].d_type == QType::PTR);
BOOST_CHECK_EQUAL(queriesCount, 0);
ret.clear();
res = sr->beginResolve(DNSName("localhost."), QType(QType::A), QClass::IN, ret
);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK(ret[0].d_type == QType::A);
BOOST_CHECK_EQUAL(getRR<ARecordContent>(ret[0])->getCA().toString(), "127.0.0.
1");
BOOST_CHECK_EQUAL(queriesCount, 0);
ret.clear();
res = sr->beginResolve(DNSName("localhost."), QType(QType::AAAA), QClass::IN,
ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK(ret[0].d_type == QType::AAAA);
BOOST_CHECK_EQUAL(getRR<AAAARecordContent>(ret[0])->getCA().toString(), "::1")
;
BOOST_CHECK_EQUAL(queriesCount, 0);
ret.clear();
res = sr->beginResolve(DNSName("localhost."), QType(QType::ANY), QClass::IN, r
et);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_REQUIRE_EQUAL(ret.size(), 2);
for (const auto& rec : ret) {
BOOST_REQUIRE((rec.d_type == QType::A) || rec.d_type == QType::AAAA);
if (rec.d_type == QType::A) {
BOOST_CHECK_EQUAL(getRR<ARecordContent>(rec)->getCA().toString(), "127.0.0
.1");
}
else {
BOOST_CHECK_EQUAL(getRR<AAAARecordContent>(rec)->getCA().toString(), "::1"
);
}
}
BOOST_CHECK_EQUAL(queriesCount, 0);
ret.clear();
res = sr->beginResolve(DNSName("version.bind."), QType(QType::TXT), QClass::CH
AOS, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK(ret[0].d_type == QType::TXT);
BOOST_CHECK_EQUAL(getRR<TXTRecordContent>(ret[0])->d_text, "\"PowerDNS Unit Te
sts\"");
BOOST_CHECK_EQUAL(queriesCount, 0);
ret.clear();
res = sr->beginResolve(DNSName("version.bind."), QType(QType::ANY), QClass::CH
AOS, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK(ret[0].d_type == QType::TXT);
BOOST_CHECK_EQUAL(getRR<TXTRecordContent>(ret[0])->d_text, "\"PowerDNS Unit Te
sts\"");
BOOST_CHECK_EQUAL(queriesCount, 0);
ret.clear();
res = sr->beginResolve(DNSName("version.pdns."), QType(QType::TXT), QClass::CH
AOS, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK(ret[0].d_type == QType::TXT);
BOOST_CHECK_EQUAL(getRR<TXTRecordContent>(ret[0])->d_text, "\"PowerDNS Unit Te
sts\"");
BOOST_CHECK_EQUAL(queriesCount, 0);
ret.clear();
res = sr->beginResolve(DNSName("version.pdns."), QType(QType::ANY), QClass::CH
AOS, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK(ret[0].d_type == QType::TXT);
BOOST_CHECK_EQUAL(getRR<TXTRecordContent>(ret[0])->d_text, "\"PowerDNS Unit Te
sts\"");
BOOST_CHECK_EQUAL(queriesCount, 0);
ret.clear();
res = sr->beginResolve(DNSName("id.server."), QType(QType::TXT), QClass::CHAOS
, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK(ret[0].d_type == QType::TXT);
BOOST_CHECK_EQUAL(getRR<TXTRecordContent>(ret[0])->d_text, "\"PowerDNS Unit Te
sts Server ID\"");
BOOST_CHECK_EQUAL(queriesCount, 0);
ret.clear();
res = sr->beginResolve(DNSName("id.server."), QType(QType::ANY), QClass::CHAOS
, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK(ret[0].d_type == QType::TXT);
BOOST_CHECK_EQUAL(getRR<TXTRecordContent>(ret[0])->d_text, "\"PowerDNS Unit Te
sts Server ID\"");
BOOST_CHECK_EQUAL(queriesCount, 0);
}
BOOST_AUTO_TEST_CASE(test_nameserver_ipv4_rpz) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
const DNSName target("rpz.powerdns.com.");
const ComboAddress ns("192.0.2.1:53");
sr->setAsyncCallback([target,ns](const ComboAddress& ip, const DNSName& domain
, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, b
oost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context
, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
if (isRootServer(ip)) {
setLWResult(res, false, true, false, true);
addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResource
Record::AUTHORITY, 172800);
addRecordToLW(res, "a.gtld-servers.net.", QType::A, ns.toString(), DNSRe
sourceRecord::ADDITIONAL, 3600);
return 1;
} else if (ip == ns) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::A, "192.0.2.42");
return 1;
}
return 0;
});
DNSFilterEngine::Policy pol;
pol.d_kind = DNSFilterEngine::PolicyKind::Drop;
std::shared_ptr<DNSFilterEngine::Zone> zone = std::make_shared<DNSFilterEngine
::Zone>();
zone->setName("Unit test policy 0");
zone->addNSIPTrigger(Netmask(ns, 32), pol);
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dfe.addZone(zone);
g_luaconfs.setState(luaconfsCopy);
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, -2);
BOOST_CHECK_EQUAL(ret.size(), 0);
}
BOOST_AUTO_TEST_CASE(test_nameserver_ipv6_rpz) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
const DNSName target("rpz.powerdns.com.");
const ComboAddress ns("[2001:DB8::42]:53");
sr->setAsyncCallback([target,ns](const ComboAddress& ip, const DNSName& domain
, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, b
oost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context
, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResource
Record::AUTHORITY, 172800);
addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, ns.toString(), DN
SResourceRecord::ADDITIONAL, 3600);
return 1;
} else if (ip == ns) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::A, "192.0.2.42");
return 1;
}
return 0;
});
DNSFilterEngine::Policy pol;
pol.d_kind = DNSFilterEngine::PolicyKind::Drop;
std::shared_ptr<DNSFilterEngine::Zone> zone = std::make_shared<DNSFilterEngine
::Zone>();
zone->setName("Unit test policy 0");
zone->addNSIPTrigger(Netmask(ns, 128), pol);
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dfe.addZone(zone);
g_luaconfs.setState(luaconfsCopy);
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, -2);
BOOST_CHECK_EQUAL(ret.size(), 0);
}
BOOST_AUTO_TEST_CASE(test_nameserver_name_rpz) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
const DNSName target("rpz.powerdns.com.");
const ComboAddress ns("192.0.2.1:53");
const DNSName nsName("ns1.powerdns.com.");
sr->setAsyncCallback([target,ns,nsName](const ComboAddress& ip, const DNSName&
domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval*
now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&>
context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, domain, QType::NS, nsName.toString(), DNSResourceReco
rd::AUTHORITY, 172800);
addRecordToLW(res, nsName, QType::A, ns.toString(), DNSResourceRecord::A
DDITIONAL, 3600);
return 1;
} else if (ip == ns) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::A, "192.0.2.42");
return 1;
}
return 0;
});
DNSFilterEngine::Policy pol;
pol.d_kind = DNSFilterEngine::PolicyKind::Drop;
std::shared_ptr<DNSFilterEngine::Zone> zone = std::make_shared<DNSFilterEngine
::Zone>();
zone->setName("Unit test policy 0");
zone->addNSTrigger(nsName, pol);
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dfe.addZone(zone);
g_luaconfs.setState(luaconfsCopy);
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, -2);
BOOST_CHECK_EQUAL(ret.size(), 0);
}
BOOST_AUTO_TEST_CASE(test_nameserver_name_rpz_disabled) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
const DNSName target("rpz.powerdns.com.");
const ComboAddress ns("192.0.2.1:53");
const DNSName nsName("ns1.powerdns.com.");
sr->setAsyncCallback([target,ns,nsName](const ComboAddress& ip, const DNSName&
domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval*
now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&>
context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, domain, QType::NS, nsName.toString(), DNSResourceReco
rd::AUTHORITY, 172800);
addRecordToLW(res, nsName, QType::A, ns.toString(), DNSResourceRecord::A
DDITIONAL, 3600);
return 1;
} else if (ip == ns) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::A, "192.0.2.42");
return 1;
}
return 0;
});
DNSFilterEngine::Policy pol;
pol.d_kind = DNSFilterEngine::PolicyKind::Drop;
std::shared_ptr<DNSFilterEngine::Zone> zone = std::make_shared<DNSFilterEngine
::Zone>();
zone->setName("Unit test policy 0");
zone->addNSIPTrigger(Netmask(ns, 128), pol);
zone->addNSTrigger(nsName, pol);
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dfe.addZone(zone);
g_luaconfs.setState(luaconfsCopy);
/* RPZ is disabled for this query, we should not be blocked */
sr->setWantsRPZ(false);
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(ret.size(), 1);
}
BOOST_AUTO_TEST_CASE(test_forward_zone_nord) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
const DNSName target("powerdns.com.");
const ComboAddress ns("192.0.2.1:53");
const ComboAddress forwardedNS("192.0.2.42:53");
SyncRes::AuthDomain ad;
ad.d_rdForward = false;
ad.d_servers.push_back(forwardedNS);
(*SyncRes::t_sstorage.domainmap)[target] = ad;
sr->setAsyncCallback([forwardedNS](const ComboAddress& ip, const DNSName& doma
in, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now,
boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> conte
xt, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
if (ip == forwardedNS) {
BOOST_CHECK_EQUAL(sendRDQuery, false);
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::A, "192.0.2.42");
return 1;
}
return 0;
});
/* simulate a no-RD query */
sr->setCacheOnly();
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(ret.size(), 1);
}
BOOST_AUTO_TEST_CASE(test_forward_zone_rd) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
const DNSName target("powerdns.com.");
const ComboAddress ns("192.0.2.1:53");
const ComboAddress forwardedNS("192.0.2.42:53");
SyncRes::AuthDomain ad;
ad.d_rdForward = false;
ad.d_servers.push_back(forwardedNS);
(*SyncRes::t_sstorage.domainmap)[target] = ad;
sr->setAsyncCallback([forwardedNS](const ComboAddress& ip, const DNSName& doma
in, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now,
boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> conte
xt, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
if (ip == forwardedNS) {
BOOST_CHECK_EQUAL(sendRDQuery, false);
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::A, "192.0.2.42");
return 1;
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(ret.size(), 1);
}
BOOST_AUTO_TEST_CASE(test_forward_zone_recurse_nord) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
const DNSName target("powerdns.com.");
const ComboAddress ns("192.0.2.1:53");
const ComboAddress forwardedNS("192.0.2.42:53");
SyncRes::AuthDomain ad;
ad.d_rdForward = true;
ad.d_servers.push_back(forwardedNS);
(*SyncRes::t_sstorage.domainmap)[target] = ad;
sr->setAsyncCallback([forwardedNS](const ComboAddress& ip, const DNSName& doma
in, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now,
boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> conte
xt, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
if (ip == forwardedNS) {
BOOST_CHECK_EQUAL(sendRDQuery, false);
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::A, "192.0.2.42");
return 1;
}
return 0;
});
/* simulate a no-RD query */
sr->setCacheOnly();
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(ret.size(), 1);
}
BOOST_AUTO_TEST_CASE(test_forward_zone_recurse_rd) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
const DNSName target("powerdns.com.");
const ComboAddress ns("192.0.2.1:53");
const ComboAddress forwardedNS("192.0.2.42:53");
SyncRes::AuthDomain ad;
ad.d_rdForward = true;
ad.d_servers.push_back(forwardedNS);
(*SyncRes::t_sstorage.domainmap)[target] = ad;
sr->setAsyncCallback([forwardedNS](const ComboAddress& ip, const DNSName& doma
in, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now,
boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> conte
xt, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
if (ip == forwardedNS) {
BOOST_CHECK_EQUAL(sendRDQuery, true);
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::A, "192.0.2.42");
return 1;
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(ret.size(), 1);
}
BOOST_AUTO_TEST_CASE(test_auth_zone_oob) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
primeHints();
size_t queriesCount = 0;
const DNSName target("test.xx.");
const ComboAddress targetAddr("127.0.0.1");
const DNSName authZone("test.xx");
SyncRes::AuthDomain ad;
DNSRecord dr;
dr.d_place = DNSResourceRecord::ANSWER;
dr.d_name = target;
dr.d_type = QType::A;
dr.d_ttl = 1800;
dr.d_content = std::make_shared<ARecordContent>(targetAddr);
ad.d_records.insert(dr);
(*SyncRes::t_sstorage.domainmap)[authZone] = ad;
sr->setAsyncCallback([&queriesCount](const ComboAddress& ip, const DNSName& do
main, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* no
w, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> con
text, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, 0);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK(ret[0].d_type == QType::A);
BOOST_CHECK_EQUAL(queriesCount, 0);
BOOST_CHECK(sr->wasOutOfBand());
BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
/* a second time, to check that the OOB flag is set when the query cache is us
ed */
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, 0);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK(ret[0].d_type == QType::A);
BOOST_CHECK_EQUAL(queriesCount, 0);
BOOST_CHECK(sr->wasOutOfBand());
BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
/* a third time, to check that the validation is disabled when the OOB flag is
set */
ret.clear();
sr->setDNSSECValidationRequested(true);
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, 0);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK(ret[0].d_type == QType::A);
BOOST_CHECK_EQUAL(queriesCount, 0);
BOOST_CHECK(sr->wasOutOfBand());
BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
}
BOOST_AUTO_TEST_CASE(test_auth_zone_oob_cname) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
primeHints();
size_t queriesCount = 0;
const DNSName target("cname.test.xx.");
const DNSName targetCname("cname-target.test.xx.");
const ComboAddress targetCnameAddr("127.0.0.1");
const DNSName authZone("test.xx");
SyncRes::AuthDomain ad;
DNSRecord dr;
dr.d_place = DNSResourceRecord::ANSWER;
dr.d_name = target;
dr.d_type = QType::CNAME;
dr.d_ttl = 1800;
dr.d_content = std::make_shared<CNAMERecordContent>(targetCname);
ad.d_records.insert(dr);
dr.d_place = DNSResourceRecord::ANSWER;
dr.d_name = targetCname;
dr.d_type = QType::A;
dr.d_ttl = 1800;
dr.d_content = std::make_shared<ARecordContent>(targetCnameAddr);
ad.d_records.insert(dr);
(*SyncRes::t_sstorage.domainmap)[authZone] = ad;
sr->setAsyncCallback([&queriesCount](const ComboAddress& ip, const DNSName& do
main, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* no
w, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> con
text, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, 0);
BOOST_REQUIRE_EQUAL(ret.size(), 2);
BOOST_CHECK(ret[0].d_type == QType::CNAME);
BOOST_CHECK(ret[1].d_type == QType::A);
BOOST_CHECK_EQUAL(queriesCount, 0);
BOOST_CHECK(sr->wasOutOfBand());
BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
/* a second time, to check that the OOB flag is set when the query cache is us
ed */
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, 0);
BOOST_REQUIRE_EQUAL(ret.size(), 2);
BOOST_CHECK(ret[0].d_type == QType::CNAME);
BOOST_CHECK(ret[1].d_type == QType::A);
BOOST_CHECK_EQUAL(queriesCount, 0);
BOOST_CHECK(sr->wasOutOfBand());
BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
/* a third time, to check that the validation is disabled when the OOB flag is
set */
ret.clear();
sr->setDNSSECValidationRequested(true);
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, 0);
BOOST_REQUIRE_EQUAL(ret.size(), 2);
BOOST_CHECK(ret[0].d_type == QType::CNAME);
BOOST_CHECK(ret[1].d_type == QType::A);
BOOST_CHECK_EQUAL(queriesCount, 0);
BOOST_CHECK(sr->wasOutOfBand());
BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
}
BOOST_AUTO_TEST_CASE(test_auth_zone) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
size_t queriesCount = 0;
const DNSName target("powerdns.com.");
const ComboAddress addr("192.0.2.5");
SyncRes::AuthDomain ad;
ad.d_name = target;
DNSRecord dr;
dr.d_place = DNSResourceRecord::ANSWER;
dr.d_name = target;
dr.d_type = QType::SOA;
dr.d_ttl = 3600;
dr.d_content = std::make_shared<SOARecordContent>("pdns-public-ns1.powerdns.co
m. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600");
ad.d_records.insert(dr);
dr.d_place = DNSResourceRecord::ANSWER;
dr.d_name = target;
dr.d_type = QType::A;
dr.d_ttl = 3600;
dr.d_content = std::make_shared<ARecordContent>(addr);
ad.d_records.insert(dr);
auto map = std::make_shared<SyncRes::domainmap_t>();
(*map)[target] = ad;
SyncRes::setDomainMap(map);
sr->setAsyncCallback([&queriesCount](const ComboAddress& ip, const DNSName& do
main, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* no
w, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> con
text, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::A, "192.0.2.42");
return 1;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(ret.size(), 1);
BOOST_CHECK(ret[0].d_type == QType::A);
BOOST_CHECK_EQUAL(getRR<ARecordContent>(ret[0])->getCA().toString(), addr.toSt
ring());
BOOST_CHECK_EQUAL(queriesCount, 0);
}
BOOST_AUTO_TEST_CASE(test_auth_zone_cname_lead_to_oob) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
size_t queriesCount = 0;
const DNSName target("powerdns.com.");
const DNSName authZone("internal.powerdns.com.");
const ComboAddress addr("192.0.2.5");
SyncRes::AuthDomain ad;
ad.d_name = authZone;
DNSRecord dr;
dr.d_place = DNSResourceRecord::ANSWER;
dr.d_name = authZone;
dr.d_type = QType::SOA;
dr.d_ttl = 3600;
dr.d_content = std::make_shared<SOARecordContent>("pdns-public-ns1.powerdns.co
m. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600");
ad.d_records.insert(dr);
dr.d_place = DNSResourceRecord::ANSWER;
dr.d_name = authZone;
dr.d_type = QType::A;
dr.d_ttl = 3600;
dr.d_content = std::make_shared<ARecordContent>(addr);
ad.d_records.insert(dr);
auto map = std::make_shared<SyncRes::domainmap_t>();
(*map)[authZone] = ad;
SyncRes::setDomainMap(map);
sr->setAsyncCallback([&queriesCount,target,authZone](const ComboAddress& ip, c
onst DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, st
ruct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const Reso
lveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* re
s) {
queriesCount++;
if (domain == target) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, target, QType::CNAME, authZone.toString(), DNSResourc
eRecord::ANSWER, 3600);
return 1;
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(ret.size(), 2);
BOOST_CHECK(ret[0].d_type == QType::CNAME);
BOOST_CHECK_EQUAL(getRR<CNAMERecordContent>(ret[0])->getTarget().toString(), a
uthZone.toString());
BOOST_CHECK(ret[1].d_type == QType::A);
BOOST_CHECK_EQUAL(getRR<ARecordContent>(ret[1])->getCA().toString(), addr.toSt
ring());
BOOST_CHECK_EQUAL(queriesCount, 1);
}
BOOST_AUTO_TEST_CASE(test_auth_zone_oob_lead_to_outgoing_queryb) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
size_t queriesCount = 0;
const DNSName target("powerdns.com.");
const DNSName externalCNAME("www.open-xchange.com.");
const ComboAddress addr("192.0.2.5");
SyncRes::AuthDomain ad;
ad.d_name = target;
DNSRecord dr;
dr.d_place = DNSResourceRecord::ANSWER;
dr.d_name = target;
dr.d_type = QType::SOA;
dr.d_ttl = 3600;
dr.d_content = std::make_shared<SOARecordContent>("pdns-public-ns1.powerdns.co
m. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600");
ad.d_records.insert(dr);
dr.d_place = DNSResourceRecord::ANSWER;
dr.d_name = target;
dr.d_type = QType::CNAME;
dr.d_ttl = 3600;
dr.d_content = std::make_shared<CNAMERecordContent>(externalCNAME);
ad.d_records.insert(dr);
auto map = std::make_shared<SyncRes::domainmap_t>();
(*map)[target] = ad;
SyncRes::setDomainMap(map);
sr->setAsyncCallback([&queriesCount,externalCNAME,addr](const ComboAddress& ip
, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level,
struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const R
esolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult*
res) {
queriesCount++;
if (domain == externalCNAME) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, externalCNAME, QType::A, addr.toString(), DNSResource
Record::ANSWER, 3600);
return 1;
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(ret.size(), 2);
BOOST_CHECK(ret[0].d_type == QType::CNAME);
BOOST_CHECK_EQUAL(getRR<CNAMERecordContent>(ret[0])->getTarget().toString(), e
xternalCNAME.toString());
BOOST_CHECK(ret[1].d_type == QType::A);
BOOST_CHECK_EQUAL(getRR<ARecordContent>(ret[1])->getCA().toString(), addr.toSt
ring());
BOOST_CHECK_EQUAL(queriesCount, 1);
}
BOOST_AUTO_TEST_CASE(test_auth_zone_nodata) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
size_t queriesCount = 0;
const DNSName target("nodata.powerdns.com.");
const DNSName authZone("powerdns.com");
SyncRes::AuthDomain ad;
ad.d_name = authZone;
DNSRecord dr;
dr.d_place = DNSResourceRecord::ANSWER;
dr.d_name = target;
dr.d_type = QType::A;
dr.d_ttl = 3600;
dr.d_content = std::make_shared<ARecordContent>(ComboAddress("192.0.2.1"));
ad.d_records.insert(dr);
dr.d_place = DNSResourceRecord::ANSWER;
dr.d_name = authZone;
dr.d_type = QType::SOA;
dr.d_ttl = 3600;
dr.d_content = std::make_shared<SOARecordContent>("pdns-public-ns1.powerdns.co
m. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600");
ad.d_records.insert(dr);
auto map = std::make_shared<SyncRes::domainmap_t>();
(*map)[authZone] = ad;
SyncRes::setDomainMap(map);
sr->setAsyncCallback([&queriesCount](const ComboAddress& ip, const DNSName& do
main, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* no
w, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> con
text, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::AAAA), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK(ret[0].d_type == QType::SOA);
BOOST_CHECK_EQUAL(queriesCount, 0);
}
BOOST_AUTO_TEST_CASE(test_auth_zone_nx) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
size_t queriesCount = 0;
const DNSName target("nx.powerdns.com.");
const DNSName authZone("powerdns.com");
SyncRes::AuthDomain ad;
ad.d_name = authZone;
DNSRecord dr;
dr.d_place = DNSResourceRecord::ANSWER;
dr.d_name = DNSName("powerdns.com.");
dr.d_type = QType::SOA;
dr.d_ttl = 3600;
dr.d_content = std::make_shared<SOARecordContent>("pdns-public-ns1.powerdns.co
m. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600");
ad.d_records.insert(dr);
auto map = std::make_shared<SyncRes::domainmap_t>();
(*map)[authZone] = ad;
SyncRes::setDomainMap(map);
sr->setAsyncCallback([&queriesCount](const ComboAddress& ip, const DNSName& do
main, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* no
w, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> con
text, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NXDomain);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK(ret[0].d_type == QType::SOA);
BOOST_CHECK_EQUAL(queriesCount, 0);
}
BOOST_AUTO_TEST_CASE(test_auth_zone_delegation) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true, false);
primeHints();
size_t queriesCount = 0;
const DNSName target("www.test.powerdns.com.");
const ComboAddress targetAddr("192.0.2.2");
const DNSName ns("ns1.test.powerdns.com.");
const ComboAddress nsAddr("192.0.2.1");
const DNSName authZone("powerdns.com");
SyncRes::AuthDomain ad;
ad.d_name = authZone;
DNSRecord dr;
dr.d_place = DNSResourceRecord::ANSWER;
dr.d_name = authZone;
dr.d_type = QType::SOA;
dr.d_ttl = 3600;
dr.d_content = std::make_shared<SOARecordContent>("pdns-public-ns1.powerdns.co
m. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600");
ad.d_records.insert(dr);
dr.d_place = DNSResourceRecord::ANSWER;
dr.d_name = DNSName("test.powerdns.com.");
dr.d_type = QType::NS;
dr.d_ttl = 3600;
dr.d_content = std::make_shared<NSRecordContent>(ns);
ad.d_records.insert(dr);
dr.d_place = DNSResourceRecord::ANSWER;
dr.d_name = ns;
dr.d_type = QType::A;
dr.d_ttl = 3600;
dr.d_content = std::make_shared<ARecordContent>(nsAddr);
ad.d_records.insert(dr);
auto map = std::make_shared<SyncRes::domainmap_t>();
(*map)[authZone] = ad;
SyncRes::setDomainMap(map);
testkeysset_t keys;
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dsAnchors.clear();
generateKeyMaterial(g_rootdnsname, DNSSECKeeper::RSASHA512, DNSSECKeeper::SHA3
84, keys, luaconfsCopy.dsAnchors);
g_luaconfs.setState(luaconfsCopy);
sr->setAsyncCallback([&queriesCount,target,targetAddr,nsAddr,authZone,keys](co
nst ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQu
ery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boo
st::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgo
ingLogger, LWResult* res) {
queriesCount++;
if (type == QType::DS || type == QType::DNSKEY) {
return genericDSAndDNSKEYHandler(res, domain, DNSName("."), type, keys,
domain == authZone);
}
if (ip == ComboAddress(nsAddr.toString(), 53) && domain == target) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceR
ecord::ANSWER, 3600);
return 1;
}
return 0;
});
sr->setDNSSECValidationRequested(true);
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK(ret[0].d_type == QType::A);
BOOST_CHECK_EQUAL(queriesCount, 4);
BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
}
BOOST_AUTO_TEST_CASE(test_auth_zone_delegation_point) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
size_t queriesCount = 0;
const DNSName target("test.powerdns.com.");
const ComboAddress targetAddr("192.0.2.2");
const DNSName ns("ns1.test.powerdns.com.");
const ComboAddress nsAddr("192.0.2.1");
const DNSName authZone("powerdns.com");
SyncRes::AuthDomain ad;
ad.d_name = authZone;
DNSRecord dr;
dr.d_place = DNSResourceRecord::ANSWER;
dr.d_name = authZone;
dr.d_type = QType::SOA;
dr.d_ttl = 3600;
dr.d_content = std::make_shared<SOARecordContent>("pdns-public-ns1.powerdns.co
m. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600");
ad.d_records.insert(dr);
dr.d_place = DNSResourceRecord::ANSWER;
dr.d_name = DNSName("test.powerdns.com.");
dr.d_type = QType::NS;
dr.d_ttl = 3600;
dr.d_content = std::make_shared<NSRecordContent>(ns);
ad.d_records.insert(dr);
dr.d_place = DNSResourceRecord::ANSWER;
dr.d_name = ns;
dr.d_type = QType::A;
dr.d_ttl = 3600;
dr.d_content = std::make_shared<ARecordContent>(nsAddr);
ad.d_records.insert(dr);
auto map = std::make_shared<SyncRes::domainmap_t>();
(*map)[authZone] = ad;
SyncRes::setDomainMap(map);
sr->setAsyncCallback([&queriesCount,nsAddr,target,targetAddr](const ComboAddre
ss& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0
Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<c
onst ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWR
esult* res) {
queriesCount++;
if (ip == ComboAddress(nsAddr.toString(), 53) && domain == target) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceR
ecord::ANSWER, 3600);
return 1;
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK(ret[0].d_type == QType::A);
BOOST_CHECK_EQUAL(queriesCount, 1);
}
BOOST_AUTO_TEST_CASE(test_auth_zone_wildcard) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
size_t queriesCount = 0;
const DNSName target("test.powerdns.com.");
const ComboAddress targetAddr("192.0.2.2");
const DNSName authZone("powerdns.com");
SyncRes::AuthDomain ad;
ad.d_name = authZone;
DNSRecord dr;
dr.d_place = DNSResourceRecord::ANSWER;
dr.d_name = authZone;
dr.d_type = QType::SOA;
dr.d_ttl = 3600;
dr.d_content = std::make_shared<SOARecordContent>("pdns-public-ns1.powerdns.co
m. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600");
ad.d_records.insert(dr);
dr.d_place = DNSResourceRecord::ANSWER;
dr.d_name = DNSName("*.powerdns.com.");
dr.d_type = QType::A;
dr.d_ttl = 3600;
dr.d_content = std::make_shared<ARecordContent>(targetAddr);
ad.d_records.insert(dr);
auto map = std::make_shared<SyncRes::domainmap_t>();
(*map)[authZone] = ad;
SyncRes::setDomainMap(map);
sr->setAsyncCallback([&queriesCount](const ComboAddress& ip, const DNSName& do
main, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* no
w, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> con
text, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK(ret[0].d_type == QType::A);
BOOST_CHECK_EQUAL(queriesCount, 0);
}
BOOST_AUTO_TEST_CASE(test_auth_zone_wildcard_nodata) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
size_t queriesCount = 0;
const DNSName target("test.powerdns.com.");
const ComboAddress targetAddr("192.0.2.2");
const DNSName authZone("powerdns.com");
SyncRes::AuthDomain ad;
ad.d_name = authZone;
DNSRecord dr;
dr.d_place = DNSResourceRecord::ANSWER;
dr.d_name = authZone;
dr.d_type = QType::SOA;
dr.d_ttl = 3600;
dr.d_content = std::make_shared<SOARecordContent>("pdns-public-ns1.powerdns.co
m. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600");
ad.d_records.insert(dr);
dr.d_place = DNSResourceRecord::ANSWER;
dr.d_name = DNSName("*.powerdns.com.");
dr.d_type = QType::A;
dr.d_ttl = 3600;
dr.d_content = std::make_shared<ARecordContent>(targetAddr);
ad.d_records.insert(dr);
auto map = std::make_shared<SyncRes::domainmap_t>();
(*map)[authZone] = ad;
SyncRes::setDomainMap(map);
sr->setAsyncCallback([&queriesCount](const ComboAddress& ip, const DNSName& do
main, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* no
w, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> con
text, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::AAAA), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK(ret[0].d_type == QType::SOA);
BOOST_CHECK_EQUAL(queriesCount, 0);
}
BOOST_AUTO_TEST_CASE(test_auth_zone_cache_only) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
primeHints();
size_t queriesCount = 0;
const DNSName target("powerdns.com.");
const ComboAddress addr("192.0.2.5");
SyncRes::AuthDomain ad;
ad.d_name = target;
DNSRecord dr;
dr.d_place = DNSResourceRecord::ANSWER;
dr.d_name = target;
dr.d_type = QType::SOA;
dr.d_ttl = 3600;
dr.d_content = std::make_shared<SOARecordContent>("pdns-public-ns1.powerdns.co
m. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600");
ad.d_records.insert(dr);
dr.d_place = DNSResourceRecord::ANSWER;
dr.d_name = target;
dr.d_type = QType::A;
dr.d_ttl = 3600;
dr.d_content = std::make_shared<ARecordContent>(addr);
ad.d_records.insert(dr);
auto map = std::make_shared<SyncRes::domainmap_t>();
(*map)[target] = ad;
SyncRes::setDomainMap(map);
sr->setAsyncCallback([&queriesCount](const ComboAddress& ip, const DNSName& do
main, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* no
w, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> con
text, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::A, "192.0.2.42");
return 1;
});
/* simulate a no-RD query */
sr->setCacheOnly();
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(ret.size(), 1);
BOOST_CHECK(ret[0].d_type == QType::A);
BOOST_CHECK_EQUAL(getRR<ARecordContent>(ret[0])->getCA().toString(), addr.toSt
ring());
BOOST_CHECK_EQUAL(queriesCount, 0);
}
BOOST_AUTO_TEST_CASE(test_dnssec_rrsig) {
init();
auto dcke = std::shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::make(DNSSE
CKeeper::ECDSA256));
dcke->create(dcke->getBits());
// cerr<<dcke->convertToISC()<<endl;
DNSSECPrivateKey dpk;
dpk.d_flags = 256;
dpk.setKey(dcke);
std::vector<std::shared_ptr<DNSRecordContent> > recordcontents;
recordcontents.push_back(getRecordContent(QType::A, "192.0.2.1"));
DNSName qname("powerdns.com.");
time_t now = time(nullptr);
RRSIGRecordContent rrc;
/* this RRSIG is valid for the current second only */
computeRRSIG(dpk, qname, qname, QType::A, 600, 0, rrc, recordcontents, boost::
none, now);
skeyset_t keyset;
keyset.insert(std::make_shared<DNSKEYRecordContent>(dpk.getDNSKEY()));
std::vector<std::shared_ptr<RRSIGRecordContent> > sigs;
sigs.push_back(std::make_shared<RRSIGRecordContent>(rrc));
BOOST_CHECK(validateWithKeySet(now, qname, recordcontents, sigs, keyset));
}
BOOST_AUTO_TEST_CASE(test_dnssec_root_validation_csk) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
setDNSSECValidation(sr, DNSSECMode::ValidateAll);
primeHints();
const DNSName target(".");
testkeysset_t keys;
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dsAnchors.clear();
generateKeyMaterial(target, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys
, luaconfsCopy.dsAnchors);
g_luaconfs.setState(luaconfsCopy);
size_t queriesCount = 0;
sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const
DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct
timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveC
ontext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
if (domain == target && type == QType::NS) {
setLWResult(res, 0, true, false, true);
char addr[] = "a.root-servers.net.";
for (char idx = 'a'; idx <= 'm'; idx++) {
addr[0] = idx;
addRecordToLW(res, domain, QType::NS, std::string(addr), DNSResourceRe
cord::ANSWER, 3600);
}
addRRSIG(keys, res->d_records, domain, 300);
addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSRes
ourceRecord::ADDITIONAL, 3600);
addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2
:30", DNSResourceRecord::ADDITIONAL, 3600);
return 1;
} else if (domain == target && type == QType::DNSKEY) {
setLWResult(res, 0, true, false, true);
addDNSKEY(keys, domain, 300, res->d_records);
addRRSIG(keys, res->d_records, domain, 300);
return 1;
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
/* 13 NS + 1 RRSIG */
BOOST_REQUIRE_EQUAL(ret.size(), 14);
BOOST_CHECK_EQUAL(queriesCount, 2);
/* again, to test the cache */
ret.clear();
res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 14);
BOOST_CHECK_EQUAL(queriesCount, 2);
}
BOOST_AUTO_TEST_CASE(test_dnssec_root_validation_ksk_zsk) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
setDNSSECValidation(sr, DNSSECMode::ValidateAll);
primeHints();
const DNSName target(".");
testkeysset_t zskeys;
testkeysset_t kskeys;
/* Generate key material for "." */
auto dckeZ = std::shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::make(DNSS
ECKeeper::ECDSA256));
dckeZ->create(dckeZ->getBits());
DNSSECPrivateKey ksk;
ksk.d_flags = 257;
ksk.setKey(dckeZ);
DSRecordContent kskds = makeDSFromDNSKey(target, ksk.getDNSKEY(), DNSSECKeeper
::SHA256);
auto dckeK = std::shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::make(DNSS
ECKeeper::ECDSA256));
dckeK->create(dckeK->getBits());
DNSSECPrivateKey zsk;
zsk.d_flags = 256;
zsk.setKey(dckeK);
DSRecordContent zskds = makeDSFromDNSKey(target, zsk.getDNSKEY(), DNSSECKeeper
::SHA256);
kskeys[target] = std::pair<DNSSECPrivateKey,DSRecordContent>(ksk, kskds);
zskeys[target] = std::pair<DNSSECPrivateKey,DSRecordContent>(zsk, zskds);
/* Set the root DS */
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dsAnchors.clear();
luaconfsCopy.dsAnchors[g_rootdnsname].insert(kskds);
g_luaconfs.setState(luaconfsCopy);
size_t queriesCount = 0;
sr->setAsyncCallback([target,&queriesCount,zskeys,kskeys](const ComboAddress&
ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Leve
l, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const
ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResul
t* res) {
queriesCount++;
if (domain == target && type == QType::NS) {
setLWResult(res, 0, true, false, true);
char addr[] = "a.root-servers.net.";
for (char idx = 'a'; idx <= 'm'; idx++) {
addr[0] = idx;
addRecordToLW(res, domain, QType::NS, std::string(addr), DNSResourceRe
cord::ANSWER, 3600);
}
addRRSIG(zskeys, res->d_records, domain, 300);
addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSRes
ourceRecord::ADDITIONAL, 3600);
addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2
:30", DNSResourceRecord::ADDITIONAL, 3600);
return 1;
} else if (domain == target && type == QType::DNSKEY) {
setLWResult(res, 0, true, false, true);
addDNSKEY(kskeys, domain, 300, res->d_records);
addDNSKEY(zskeys, domain, 300, res->d_records);
addRRSIG(kskeys, res->d_records, domain, 300);
return 1;
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
/* 13 NS + 1 RRSIG */
BOOST_REQUIRE_EQUAL(ret.size(), 14);
BOOST_CHECK_EQUAL(queriesCount, 2);
/* again, to test the cache */
ret.clear();
res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 14);
BOOST_CHECK_EQUAL(queriesCount, 2);
}
BOOST_AUTO_TEST_CASE(test_dnssec_bogus_no_dnskey) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
setDNSSECValidation(sr, DNSSECMode::ValidateAll);
primeHints();
const DNSName target(".");
testkeysset_t keys;
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dsAnchors.clear();
generateKeyMaterial(target, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys
, luaconfsCopy.dsAnchors);
g_luaconfs.setState(luaconfsCopy);
size_t queriesCount = 0;
sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const
DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct
timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveC
ontext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
if (domain == target && type == QType::NS) {
setLWResult(res, 0, true, false, true);
char addr[] = "a.root-servers.net.";
for (char idx = 'a'; idx <= 'm'; idx++) {
addr[0] = idx;
addRecordToLW(res, domain, QType::NS, std::string(addr), DNSResourceRe
cord::ANSWER, 3600);
}
addRRSIG(keys, res->d_records, domain, 300);
addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSRes
ourceRecord::ADDITIONAL, 3600);
addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2
:30", DNSResourceRecord::ADDITIONAL, 3600);
return 1;
} else if (domain == target && type == QType::DNSKEY) {
setLWResult(res, 0, true, false, true);
/* No DNSKEY */
return 1;
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
/* 13 NS + 1 RRSIG */
BOOST_REQUIRE_EQUAL(ret.size(), 14);
BOOST_CHECK_EQUAL(queriesCount, 2);
/* again, to test the cache */
ret.clear();
res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 14);
BOOST_CHECK_EQUAL(queriesCount, 2);
}
BOOST_AUTO_TEST_CASE(test_dnssec_bogus_dnskey_doesnt_match_ds) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
setDNSSECValidation(sr, DNSSECMode::ValidateAll);
primeHints();
const DNSName target(".");
testkeysset_t dskeys;
testkeysset_t keys;
/* Generate key material for "." */
auto dckeDS = std::shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::make(DNS
SECKeeper::ECDSA256));
dckeDS->create(dckeDS->getBits());
DNSSECPrivateKey dskey;
dskey.d_flags = 257;
dskey.setKey(dckeDS);
DSRecordContent drc = makeDSFromDNSKey(target, dskey.getDNSKEY(), DNSSECKeeper
::SHA256);
auto dcke = std::shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::make(DNSSE
CKeeper::ECDSA256));
dcke->create(dcke->getBits());
DNSSECPrivateKey dpk;
dpk.d_flags = 256;
dpk.setKey(dcke);
DSRecordContent uselessdrc = makeDSFromDNSKey(target, dpk.getDNSKEY(), DNSSECK
eeper::SHA256);
dskeys[target] = std::pair<DNSSECPrivateKey,DSRecordContent>(dskey, drc);
keys[target] = std::pair<DNSSECPrivateKey,DSRecordContent>(dpk, uselessdrc);
/* Set the root DS */
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dsAnchors.clear();
luaconfsCopy.dsAnchors[g_rootdnsname].insert(drc);
g_luaconfs.setState(luaconfsCopy);
size_t queriesCount = 0;
sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const
DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct
timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveC
ontext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
if (domain == target && type == QType::NS) {
setLWResult(res, 0, true, false, true);
char addr[] = "a.root-servers.net.";
for (char idx = 'a'; idx <= 'm'; idx++) {
addr[0] = idx;
addRecordToLW(res, domain, QType::NS, std::string(addr), DNSResourceRe
cord::ANSWER, 3600);
}
addRRSIG(keys, res->d_records, domain, 300);
addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSRes
ourceRecord::ADDITIONAL, 3600);
addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2
:30", DNSResourceRecord::ADDITIONAL, 3600);
return 1;
} else if (domain == target && type == QType::DNSKEY) {
setLWResult(res, 0, true, false, true);
addDNSKEY(keys, domain, 300, res->d_records);
addRRSIG(keys, res->d_records, domain, 300);
return 1;
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
/* 13 NS + 1 RRSIG */
BOOST_REQUIRE_EQUAL(ret.size(), 14);
BOOST_CHECK_EQUAL(queriesCount, 2);
/* again, to test the cache */
ret.clear();
res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 14);
BOOST_CHECK_EQUAL(queriesCount, 2);
}
BOOST_AUTO_TEST_CASE(test_dnssec_bogus_rrsig_signed_with_unknown_dnskey) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
setDNSSECValidation(sr, DNSSECMode::ValidateAll);
primeHints();
const DNSName target(".");
testkeysset_t keys;
testkeysset_t rrsigkeys;
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dsAnchors.clear();
generateKeyMaterial(target, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys
, luaconfsCopy.dsAnchors);
g_luaconfs.setState(luaconfsCopy);
auto dckeRRSIG = std::shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::make(
DNSSECKeeper::ECDSA256));
dckeRRSIG->create(dckeRRSIG->getBits());
DNSSECPrivateKey rrsigkey;
rrsigkey.d_flags = 257;
rrsigkey.setKey(dckeRRSIG);
DSRecordContent rrsigds = makeDSFromDNSKey(target, rrsigkey.getDNSKEY(), DNSSE
CKeeper::SHA256);
rrsigkeys[target] = std::pair<DNSSECPrivateKey,DSRecordContent>(rrsigkey, rrsi
gds);
size_t queriesCount = 0;
sr->setAsyncCallback([target,&queriesCount,keys,rrsigkeys](const ComboAddress&
ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Lev
el, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<cons
t ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResu
lt* res) {
queriesCount++;
if (domain == target && type == QType::NS) {
setLWResult(res, 0, true, false, true);
char addr[] = "a.root-servers.net.";
for (char idx = 'a'; idx <= 'm'; idx++) {
addr[0] = idx;
addRecordToLW(res, domain, QType::NS, std::string(addr), DNSResourceRe
cord::ANSWER, 3600);
}
addRRSIG(rrsigkeys, res->d_records, domain, 300);
addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSRes
ourceRecord::ADDITIONAL, 3600);
addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2
:30", DNSResourceRecord::ADDITIONAL, 3600);
return 1;
} else if (domain == target && type == QType::DNSKEY) {
setLWResult(res, 0, true, false, true);
addDNSKEY(keys, domain, 300, res->d_records);
addRRSIG(rrsigkeys, res->d_records, domain, 300);
return 1;
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
/* 13 NS + 1 RRSIG */
BOOST_REQUIRE_EQUAL(ret.size(), 14);
BOOST_CHECK_EQUAL(queriesCount, 2);
/* again, to test the cache */
ret.clear();
res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 14);
BOOST_CHECK_EQUAL(queriesCount, 2);
}
BOOST_AUTO_TEST_CASE(test_dnssec_bogus_no_rrsig) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
setDNSSECValidation(sr, DNSSECMode::ValidateAll);
primeHints();
const DNSName target(".");
testkeysset_t keys;
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dsAnchors.clear();
generateKeyMaterial(target, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys
, luaconfsCopy.dsAnchors);
g_luaconfs.setState(luaconfsCopy);
size_t queriesCount = 0;
sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const
DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct
timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveC
ontext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
if (domain == target && type == QType::NS) {
setLWResult(res, 0, true, false, true);
char addr[] = "a.root-servers.net.";
for (char idx = 'a'; idx <= 'm'; idx++) {
addr[0] = idx;
addRecordToLW(res, domain, QType::NS, std::string(addr), DNSResourceRe
cord::ANSWER, 3600);
}
/* No RRSIG */
addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSRes
ourceRecord::ADDITIONAL, 3600);
addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2
:30", DNSResourceRecord::ADDITIONAL, 3600);
return 1;
} else if (domain == target && type == QType::DNSKEY) {
setLWResult(res, 0, true, false, true);
addDNSKEY(keys, domain, 300, res->d_records);
addRRSIG(keys, res->d_records, domain, 300);
return 1;
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
/* 13 NS + 0 RRSIG */
BOOST_REQUIRE_EQUAL(ret.size(), 13);
/* no RRSIG so no query for DNSKEYs */
BOOST_CHECK_EQUAL(queriesCount, 1);
/* again, to test the cache */
ret.clear();
res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 13);
BOOST_CHECK_EQUAL(queriesCount, 1);
}
BOOST_AUTO_TEST_CASE(test_dnssec_insecure_unknown_ds_algorithm) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
setDNSSECValidation(sr, DNSSECMode::ValidateAll);
primeHints();
const DNSName target(".");
testkeysset_t keys;
/* Generate key material for "." */
auto dcke = std::shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::make(DNSSE
CKeeper::ECDSA256));
dcke->create(dcke->getBits());
DNSSECPrivateKey dpk;
dpk.d_flags = 256;
dpk.setKey(dcke);
/* Fake algorithm number (private) */
dpk.d_algorithm = 253;
DSRecordContent drc = makeDSFromDNSKey(target, dpk.getDNSKEY(), DNSSECKeeper::
SHA256);
keys[target] = std::pair<DNSSECPrivateKey,DSRecordContent>(dpk, drc);
/* Fake algorithm number (private) */
drc.d_algorithm = 253;
/* Set the root DS */
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dsAnchors.clear();
luaconfsCopy.dsAnchors[g_rootdnsname].insert(drc);
g_luaconfs.setState(luaconfsCopy);
size_t queriesCount = 0;
sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const
DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct
timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveC
ontext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
if (domain == target && type == QType::NS) {
setLWResult(res, 0, true, false, true);
char addr[] = "a.root-servers.net.";
for (char idx = 'a'; idx <= 'm'; idx++) {
addr[0] = idx;
addRecordToLW(res, domain, QType::NS, std::string(addr), DNSResourceRe
cord::ANSWER, 3600);
}
addRRSIG(keys, res->d_records, domain, 300);
addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSRes
ourceRecord::ADDITIONAL, 3600);
addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2
:30", DNSResourceRecord::ADDITIONAL, 3600);
return 1;
} else if (domain == target && type == QType::DNSKEY) {
setLWResult(res, 0, true, false, true);
addDNSKEY(keys, domain, 300, res->d_records);
addRRSIG(keys, res->d_records, domain, 300);
return 1;
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
/* 13 NS + 1 RRSIG */
BOOST_REQUIRE_EQUAL(ret.size(), 14);
/* no supported DS so no query for DNSKEYs */
BOOST_CHECK_EQUAL(queriesCount, 1);
/* again, to test the cache */
ret.clear();
res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 14);
BOOST_CHECK_EQUAL(queriesCount, 1);
}
BOOST_AUTO_TEST_CASE(test_dnssec_insecure_unknown_ds_digest) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
setDNSSECValidation(sr, DNSSECMode::ValidateAll);
primeHints();
const DNSName target(".");
testkeysset_t keys;
/* Generate key material for "." */
auto dcke = std::shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::make(DNSSE
CKeeper::ECDSA256));
dcke->create(dcke->getBits());
DNSSECPrivateKey dpk;
dpk.d_flags = 256;
dpk.setKey(dcke);
DSRecordContent drc = makeDSFromDNSKey(target, dpk.getDNSKEY(), DNSSECKeeper::
SHA256);
/* Fake digest number (reserved) */
drc.d_digesttype = 0;
keys[target] = std::pair<DNSSECPrivateKey, DSRecordContent>(dpk, drc);
/* Set the root DS */
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dsAnchors.clear();
luaconfsCopy.dsAnchors[g_rootdnsname].insert(drc);
g_luaconfs.setState(luaconfsCopy);
size_t queriesCount = 0;
sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const
DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct
timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveC
ontext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
if (domain == target && type == QType::NS) {
setLWResult(res, 0, true, false, true);
char addr[] = "a.root-servers.net.";
for (char idx = 'a'; idx <= 'm'; idx++) {
addr[0] = idx;
addRecordToLW(res, domain, QType::NS, std::string(addr), DNSResourceRe
cord::ANSWER, 3600);
}
addRRSIG(keys, res->d_records, domain, 300);
addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSRes
ourceRecord::ADDITIONAL, 3600);
addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2
:30", DNSResourceRecord::ADDITIONAL, 3600);
return 1;
} else if (domain == target && type == QType::DNSKEY) {
setLWResult(res, 0, true, false, true);
addDNSKEY(keys, domain, 300, res->d_records);
addRRSIG(keys, res->d_records, domain, 300);
return 1;
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
/* 13 NS + 1 RRSIG */
BOOST_REQUIRE_EQUAL(ret.size(), 14);
/* no supported DS so no query for DNSKEYs */
BOOST_CHECK_EQUAL(queriesCount, 1);
/* again, to test the cache */
ret.clear();
res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 14);
BOOST_CHECK_EQUAL(queriesCount, 1);
}
BOOST_AUTO_TEST_CASE(test_dnssec_bogus_bad_sig) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
setDNSSECValidation(sr, DNSSECMode::ValidateAll);
primeHints();
const DNSName target(".");
testkeysset_t keys;
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dsAnchors.clear();
generateKeyMaterial(g_rootdnsname, DNSSECKeeper::RSASHA512, DNSSECKeeper::SHA3
84, keys, luaconfsCopy.dsAnchors);
g_luaconfs.setState(luaconfsCopy);
size_t queriesCount = 0;
sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const
DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct
timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveC
ontext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
if (domain == target && type == QType::NS) {
setLWResult(res, 0, true, false, true);
char addr[] = "a.root-servers.net.";
for (char idx = 'a'; idx <= 'm'; idx++) {
addr[0] = idx;
addRecordToLW(res, domain, QType::NS, std::string(addr), DNSResourceRe
cord::ANSWER, 3600);
}
addRRSIG(keys, res->d_records, domain, 300, true);
addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSRes
ourceRecord::ADDITIONAL, 3600);
addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2
:30", DNSResourceRecord::ADDITIONAL, 3600);
return 1;
} else if (domain == target && type == QType::DNSKEY) {
setLWResult(res, 0, true, false, true);
addDNSKEY(keys, domain, 300, res->d_records);
addRRSIG(keys, res->d_records, domain, 300);
return 1;
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
/* 13 NS + 1 RRSIG */
BOOST_REQUIRE_EQUAL(ret.size(), 14);
BOOST_CHECK_EQUAL(queriesCount, 2);
/* again, to test the cache */
ret.clear();
res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 14);
BOOST_CHECK_EQUAL(queriesCount, 2);
}
BOOST_AUTO_TEST_CASE(test_dnssec_bogus_bad_algo) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
setDNSSECValidation(sr, DNSSECMode::ValidateAll);
primeHints();
const DNSName target(".");
testkeysset_t keys;
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dsAnchors.clear();
generateKeyMaterial(g_rootdnsname, DNSSECKeeper::RSASHA512, DNSSECKeeper::SHA3
84, keys, luaconfsCopy.dsAnchors);
g_luaconfs.setState(luaconfsCopy);
size_t queriesCount = 0;
sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const
DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct
timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveC
ontext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
if (domain == target && type == QType::NS) {
setLWResult(res, 0, true, false, true);
char addr[] = "a.root-servers.net.";
for (char idx = 'a'; idx <= 'm'; idx++) {
addr[0] = idx;
addRecordToLW(res, domain, QType::NS, std::string(addr), DNSResourceRe
cord::ANSWER, 3600);
}
/* FORCE WRONG ALGO */
addRRSIG(keys, res->d_records, domain, 300, false, DNSSECKeeper::RSASHA2
56);
addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSRes
ourceRecord::ADDITIONAL, 3600);
addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2
:30", DNSResourceRecord::ADDITIONAL, 3600);
return 1;
} else if (domain == target && type == QType::DNSKEY) {
setLWResult(res, 0, true, false, true);
addDNSKEY(keys, domain, 300, res->d_records);
addRRSIG(keys, res->d_records, domain, 300);
return 1;
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
/* 13 NS + 1 RRSIG */
BOOST_REQUIRE_EQUAL(ret.size(), 14);
BOOST_CHECK_EQUAL(queriesCount, 2);
/* again, to test the cache */
ret.clear();
res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 14);
BOOST_CHECK_EQUAL(queriesCount, 2);
}
BOOST_AUTO_TEST_CASE(test_dnssec_secure_various_algos) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
setDNSSECValidation(sr, DNSSECMode::ValidateAll);
primeHints();
const DNSName target("powerdns.com.");
const ComboAddress targetAddr("192.0.2.42");
testkeysset_t keys;
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dsAnchors.clear();
generateKeyMaterial(g_rootdnsname, DNSSECKeeper::RSASHA512, DNSSECKeeper::SHA3
84, keys, luaconfsCopy.dsAnchors);
generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA
256, keys);
generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA384, DNSSECKe
eper::SHA384, keys);
g_luaconfs.setState(luaconfsCopy);
size_t queriesCount = 0;
sr->setAsyncCallback([target,targetAddr,&queriesCount,keys](const ComboAddress
& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Le
vel, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<con
st ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWRes
ult* res) {
queriesCount++;
DNSName auth = domain;
if (domain == target) {
auth = DNSName("powerdns.com.");
}
if (type == QType::DS || type == QType::DNSKEY) {
return genericDSAndDNSKEYHandler(res, domain, auth, type, keys);
}
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResource
Record::AUTHORITY, 3600);
addDS(DNSName("com."), 300, res->d_records, keys);
addRRSIG(keys, res->d_records, DNSName("."), 300);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSReso
urceRecord::ADDITIONAL, 3600);
return 1;
}
if (ip == ComboAddress("192.0.2.1:53")) {
if (domain == DNSName("com.")) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
addRRSIG(keys, res->d_records, domain, 300);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSRe
sourceRecord::ADDITIONAL, 3600);
addRRSIG(keys, res->d_records, domain, 300);
}
else {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, auth, QType::NS, "ns1.powerdns.com.", DNSResourceRe
cord::AUTHORITY, 3600);
addDS(auth, 300, res->d_records, keys);
addRRSIG(keys, res->d_records, DNSName("com."), 300);
addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSReso
urceRecord::ADDITIONAL, 3600);
}
return 1;
}
if (ip == ComboAddress("192.0.2.2:53")) {
if (type == QType::NS) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
addRRSIG(keys, res->d_records, auth, 300);
addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSReso
urceRecord::ADDITIONAL, 3600);
addRRSIG(keys, res->d_records, auth, 300);
}
else {
setLWResult(res, RCode::NoError, true, false, true);
addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourc
eRecord::ANSWER, 3600);
addRRSIG(keys, res->d_records, auth, 300);
}
return 1;
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 2);
BOOST_CHECK_EQUAL(queriesCount, 8);
/* again, to test the cache */
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 2);
BOOST_CHECK_EQUAL(queriesCount, 8);
}
BOOST_AUTO_TEST_CASE(test_dnssec_secure_a_then_ns) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
setDNSSECValidation(sr, DNSSECMode::ValidateAll);
primeHints();
const DNSName target("powerdns.com.");
const ComboAddress targetAddr("192.0.2.42");
testkeysset_t keys;
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dsAnchors.clear();
generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA25
6, keys, luaconfsCopy.dsAnchors);
generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA
256, keys);
generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKe
eper::SHA256, keys);
g_luaconfs.setState(luaconfsCopy);
size_t queriesCount = 0;
sr->setAsyncCallback([target,targetAddr,&queriesCount,keys](const ComboAddress
& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Le
vel, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<con
st ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWRes
ult* res) {
queriesCount++;
DNSName auth = domain;
if (domain == target) {
auth = DNSName("powerdns.com.");
}
if (type == QType::DS || type == QType::DNSKEY) {
return genericDSAndDNSKEYHandler(res, domain, auth, type, keys);
}
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResource
Record::AUTHORITY, 3600);
addDS(DNSName("com."), 300, res->d_records, keys);
addRRSIG(keys, res->d_records, DNSName("."), 300);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSReso
urceRecord::ADDITIONAL, 3600);
return 1;
}
if (ip == ComboAddress("192.0.2.1:53")) {
if (domain == DNSName("com.")) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
addRRSIG(keys, res->d_records, domain, 300);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSRe
sourceRecord::ADDITIONAL, 3600);
addRRSIG(keys, res->d_records, domain, 300);
}
else {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, auth, QType::NS, "ns1.powerdns.com.", DNSResourceRe
cord::AUTHORITY, 3600);
addDS(auth, 300, res->d_records, keys);
addRRSIG(keys, res->d_records, DNSName("com."), 300);
addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSReso
urceRecord::ADDITIONAL, 3600);
}
return 1;
}
if (ip == ComboAddress("192.0.2.2:53")) {
if (type == QType::NS) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
addRRSIG(keys, res->d_records, auth, 300);
addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSReso
urceRecord::ADDITIONAL, 3600);
addRRSIG(keys, res->d_records, auth, 300);
}
else {
setLWResult(res, RCode::NoError, true, false, true);
addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourc
eRecord::ANSWER, 3600);
addRRSIG(keys, res->d_records, auth, 300);
}
return 1;
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 2);
BOOST_CHECK_EQUAL(queriesCount, 8);
/* again, to test the cache */
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 2);
BOOST_CHECK_EQUAL(queriesCount, 8);
/* this time we ask for the NS that should be in the cache, to check
the validation status */
ret.clear();
res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 2);
BOOST_CHECK_EQUAL(queriesCount, 9);
}
BOOST_AUTO_TEST_CASE(test_dnssec_insecure_a_then_ns) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
setDNSSECValidation(sr, DNSSECMode::ValidateAll);
primeHints();
const DNSName target("powerdns.com.");
const ComboAddress targetAddr("192.0.2.42");
testkeysset_t keys;
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dsAnchors.clear();
generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA25
6, keys, luaconfsCopy.dsAnchors);
generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA
256, keys);
g_luaconfs.setState(luaconfsCopy);
size_t queriesCount = 0;
sr->setAsyncCallback([target,targetAddr,&queriesCount,keys](const ComboAddress
& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Le
vel, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<con
st ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWRes
ult* res) {
queriesCount++;
DNSName auth = domain;
if (domain == target) {
auth = DNSName("powerdns.com.");
}
if (type == QType::DS || type == QType::DNSKEY) {
return genericDSAndDNSKEYHandler(res, domain, auth, type, keys);
}
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResource
Record::AUTHORITY, 3600);
addDS(DNSName("com."), 300, res->d_records, keys);
addRRSIG(keys, res->d_records, DNSName("."), 300);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSReso
urceRecord::ADDITIONAL, 3600);
return 1;
}
if (ip == ComboAddress("192.0.2.1:53")) {
if (domain == DNSName("com.")) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
addRRSIG(keys, res->d_records, domain, 300);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSRe
sourceRecord::ADDITIONAL, 3600);
addRRSIG(keys, res->d_records, domain, 300);
}
else {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, auth, QType::NS, "ns1.powerdns.com.", DNSResourceRe
cord::AUTHORITY, 3600);
/* no DS */
addNSECRecordToLW(domain, DNSName("z.powerdns.com."), { QType::NS }, 6
00, res->d_records);
addRRSIG(keys, res->d_records, DNSName("com."), 300);
addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSReso
urceRecord::ADDITIONAL, 3600);
}
return 1;
}
if (ip == ComboAddress("192.0.2.2:53")) {
if (type == QType::NS) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSReso
urceRecord::ADDITIONAL, 3600);
}
else {
setLWResult(res, RCode::NoError, true, false, true);
addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourc
eRecord::ANSWER, 3600);
}
return 1;
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK_EQUAL(queriesCount, 7);
/* again, to test the cache */
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK_EQUAL(queriesCount, 7);
/* this time we ask for the NS that should be in the cache, to check
the validation status */
ret.clear();
res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK_EQUAL(queriesCount, 8);
}
BOOST_AUTO_TEST_CASE(test_dnssec_secure_with_nta) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
setDNSSECValidation(sr, DNSSECMode::ValidateAll);
primeHints();
const DNSName target("powerdns.com.");
const ComboAddress targetAddr("192.0.2.42");
testkeysset_t keys;
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dsAnchors.clear();
generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA25
6, keys, luaconfsCopy.dsAnchors);
generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA
256, keys);
generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKe
eper::SHA256, keys);
/* Add a NTA for "powerdns.com" */
luaconfsCopy.negAnchors[target] = "NTA for PowerDNS.com";
g_luaconfs.setState(luaconfsCopy);
size_t queriesCount = 0;
sr->setAsyncCallback([target,targetAddr,&queriesCount,keys](const ComboAddress
& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Le
vel, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<con
st ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWRes
ult* res) {
queriesCount++;
DNSName auth = domain;
if (domain == target) {
auth = DNSName("powerdns.com.");
}
if (type == QType::DS || type == QType::DNSKEY) {
return genericDSAndDNSKEYHandler(res, domain, auth, type, keys);
}
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResource
Record::AUTHORITY, 3600);
addDS(DNSName("com."), 300, res->d_records, keys);
addRRSIG(keys, res->d_records, DNSName("."), 300);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSReso
urceRecord::ADDITIONAL, 3600);
return 1;
}
if (ip == ComboAddress("192.0.2.1:53")) {
if (domain == DNSName("com.")) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
addRRSIG(keys, res->d_records, domain, 300);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSRe
sourceRecord::ADDITIONAL, 3600);
addRRSIG(keys, res->d_records, domain, 300);
}
else {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, auth, QType::NS, "ns1.powerdns.com.", DNSResourceRe
cord::AUTHORITY, 3600);
addDS(auth, 300, res->d_records, keys);
addRRSIG(keys, res->d_records, DNSName("com."), 300);
addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSReso
urceRecord::ADDITIONAL, 3600);
}
return 1;
}
if (ip == ComboAddress("192.0.2.2:53")) {
if (type == QType::NS) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
addRRSIG(keys, res->d_records, auth, 300);
addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSReso
urceRecord::ADDITIONAL, 3600);
addRRSIG(keys, res->d_records, auth, 300);
}
else {
setLWResult(res, RCode::NoError, true, false, true);
addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourc
eRecord::ANSWER, 3600);
addRRSIG(keys, res->d_records, auth, 300);
}
return 1;
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
/* Should be insecure because of the NTA */
BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 2);
BOOST_CHECK_EQUAL(queriesCount, 5);
/* again, to test the cache */
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
/* Should be insecure because of the NTA */
BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 2);
BOOST_CHECK_EQUAL(queriesCount, 5);
}
BOOST_AUTO_TEST_CASE(test_dnssec_bogus_with_nta) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
setDNSSECValidation(sr, DNSSECMode::ValidateAll);
primeHints();
const DNSName target("powerdns.com.");
const ComboAddress targetAddr("192.0.2.42");
testkeysset_t keys;
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dsAnchors.clear();
generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA25
6, keys, luaconfsCopy.dsAnchors);
generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA
256, keys);
generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKe
eper::SHA256, keys);
/* Add a NTA for "powerdns.com" */
luaconfsCopy.negAnchors[target] = "NTA for PowerDNS.com";
g_luaconfs.setState(luaconfsCopy);
size_t queriesCount = 0;
sr->setAsyncCallback([target,targetAddr,&queriesCount,keys](const ComboAddress
& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Le
vel, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<con
st ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWRes
ult* res) {
queriesCount++;
if (type == QType::DS || type == QType::DNSKEY) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pi
eter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord
::AUTHORITY, 3600);
return 1;
}
else {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResour
ceRecord::AUTHORITY, 3600);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSRe
sourceRecord::ADDITIONAL, 3600);
return 1;
}
else if (ip == ComboAddress("192.0.2.1:53")) {
if (domain == DNSName("com.")) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNS
ResourceRecord::ADDITIONAL, 3600);
}
else {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.", DNSResour
ceRecord::AUTHORITY, 3600);
addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSRe
sourceRecord::ADDITIONAL, 3600);
}
return 1;
}
else if (ip == ComboAddress("192.0.2.2:53")) {
if (type == QType::NS) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSRe
sourceRecord::ADDITIONAL, 3600);
}
else {
setLWResult(res, RCode::NoError, true, false, true);
addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResou
rceRecord::ANSWER, 3600);
}
return 1;
}
}
return 0;
});
/* There is TA for root but no DS/DNSKEY/RRSIG, should be Bogus, but.. */
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
/* Should be insecure because of the NTA */
BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK_EQUAL(queriesCount, 4);
/* again, to test the cache */
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK_EQUAL(queriesCount, 4);
}
BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
setDNSSECValidation(sr, DNSSECMode::ValidateAll);
primeHints();
const DNSName target("powerdns.com.");
testkeysset_t keys;
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dsAnchors.clear();
generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA25
6, keys, luaconfsCopy.dsAnchors);
generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA
256, keys);
generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKe
eper::SHA256, keys);
g_luaconfs.setState(luaconfsCopy);
size_t queriesCount = 0;
sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const
DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct
timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveC
ontext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
if (type == QType::DS || type == QType::DNSKEY) {
return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
}
else {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResour
ceRecord::AUTHORITY, 3600);
addDS(DNSName("com."), 300, res->d_records, keys);
addRRSIG(keys, res->d_records, DNSName("."), 300);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSRe
sourceRecord::ADDITIONAL, 3600);
return 1;
}
else if (ip == ComboAddress("192.0.2.1:53")) {
if (domain == DNSName("com.")) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
addRRSIG(keys, res->d_records, domain, 300);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNS
ResourceRecord::ADDITIONAL, 3600);
addRRSIG(keys, res->d_records, domain, 300);
}
else {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.", DNSResour
ceRecord::AUTHORITY, 3600);
addDS(domain, 300, res->d_records, keys);
addRRSIG(keys, res->d_records, DNSName("com."), 300);
addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSRe
sourceRecord::ADDITIONAL, 3600);
}
return 1;
}
else if (ip == ComboAddress("192.0.2.2:53")) {
if (type == QType::NS) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSRe
sourceRecord::ADDITIONAL, 3600);
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
}
else {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com
. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRe
cord::AUTHORITY, 3600);
addRRSIG(keys, res->d_records, domain, 300);
addNSECRecordToLW(domain, DNSName("z.powerdns.com."), { QType::NS, Q
Type::DNSKEY }, 600, res->d_records);
addRRSIG(keys, res->d_records, domain, 300);
}
return 1;
}
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 4);
BOOST_CHECK_EQUAL(queriesCount, 8);
/* again, to test the cache */
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 4);
BOOST_CHECK_EQUAL(queriesCount, 8);
}
BOOST_AUTO_TEST_CASE(test_dnssec_validation_nxdomain_nsec) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
setDNSSECValidation(sr, DNSSECMode::ValidateAll);
primeHints();
const DNSName target("nx.powerdns.com.");
testkeysset_t keys;
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dsAnchors.clear();
generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA25
6, keys, luaconfsCopy.dsAnchors);
generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA
256, keys);
generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKe
eper::SHA256, keys);
g_luaconfs.setState(luaconfsCopy);
size_t queriesCount = 0;
sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const
DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct
timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveC
ontext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
DNSName auth = domain;
if (domain == target) {
auth = DNSName("powerdns.com.");
}
if (type == QType::DS || type == QType::DNSKEY) {
if (type == QType::DS && domain == target) {
setLWResult(res, RCode::NXDomain, true, false, true);
addRecordToLW(res, DNSName("powerdns.com."), QType::SOA, "pdns-public-
ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600
", DNSResourceRecord::AUTHORITY, 3600);
addRRSIG(keys, res->d_records, auth, 300);
addNSECRecordToLW(DNSName("nw.powerdns.com."), DNSName("ny.powerdns.co
m."), { QType::RRSIG, QType::NSEC }, 600, res->d_records);
addRRSIG(keys, res->d_records, auth, 300);
return 1;
}
else {
return genericDSAndDNSKEYHandler(res, domain, auth, type, keys);
}
}
else {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResour
ceRecord::AUTHORITY, 3600);
addDS(DNSName("com."), 300, res->d_records, keys);
addRRSIG(keys, res->d_records, DNSName("."), 300);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSRe
sourceRecord::ADDITIONAL, 3600);
return 1;
}
else if (ip == ComboAddress("192.0.2.1:53")) {
if (domain == DNSName("com.")) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
addRRSIG(keys, res->d_records, domain, 300);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNS
ResourceRecord::ADDITIONAL, 3600);
addRRSIG(keys, res->d_records, domain, 300);
}
else {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, auth, QType::NS, "ns1.powerdns.com.", DNSResource
Record::AUTHORITY, 3600);
addDS(auth, 300, res->d_records, keys);
addRRSIG(keys, res->d_records, DNSName("com."), 300);
addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSRe
sourceRecord::ADDITIONAL, 3600);
}
return 1;
}
else if (ip == ComboAddress("192.0.2.2:53")) {
if (type == QType::NS) {
setLWResult(res, 0, true, false, true);
if (domain == DNSName("powerdns.com.")) {
addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNS
ResourceRecord::ADDITIONAL, 3600);
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
}
else {
addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.c
om. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResource
Record::AUTHORITY, 3600);
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
addNSECRecordToLW(DNSName("nx.powerdns.com."), DNSName("nz.powerdn
s.com."), { QType::A, QType::NSEC, QType::RRSIG }, 600, res->d_records);
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
}
}
else {
setLWResult(res, RCode::NXDomain, true, false, true);
addRecordToLW(res, DNSName("powerdns.com."), QType::SOA, "pdns-publi
c-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 36
00", DNSResourceRecord::AUTHORITY, 3600);
addRRSIG(keys, res->d_records, auth, 300);
addNSECRecordToLW(DNSName("nw.powerdns.com."), DNSName("ny.powerdns.
com."), { QType::RRSIG, QType::NSEC }, 600, res->d_records);
addRRSIG(keys, res->d_records, auth, 300);
/* add wildcard denial */
addNSECRecordToLW(DNSName("powerdns.com."), DNSName("a.powerdns.com.
"), { QType::RRSIG, QType::NSEC }, 600, res->d_records);
addRRSIG(keys, res->d_records, auth, 300);
}
return 1;
}
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NXDomain);
BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 6);
BOOST_CHECK_EQUAL(queriesCount, 9);
/* again, to test the cache */
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NXDomain);
BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 6);
BOOST_CHECK_EQUAL(queriesCount, 9);
}
BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec_wildcard) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
setDNSSECValidation(sr, DNSSECMode::ValidateAll);
primeHints();
const DNSName target("www.powerdns.com.");
testkeysset_t keys;
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dsAnchors.clear();
generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA25
6, keys, luaconfsCopy.dsAnchors);
generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA
256, keys);
generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKe
eper::SHA256, keys);
g_luaconfs.setState(luaconfsCopy);
size_t queriesCount = 0;
sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const
DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct
timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveC
ontext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
if (type == QType::DS || type == QType::DNSKEY) {
if (type == QType::DS && domain == target) {
setLWResult(res, RCode::NoError, true, false, true);
addRecordToLW(res, DNSName("powerdns.com."), QType::SOA, "pdns-public-
ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600
", DNSResourceRecord::AUTHORITY, 3600);
addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300);
addNSECRecordToLW(DNSName("www.powerdns.com."), DNSName("wwz.powerdns.
com."), { QType::A, QType::NSEC, QType::RRSIG }, 600, res->d_records);
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
return 1;
}
else {
return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
}
}
else {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResour
ceRecord::AUTHORITY, 3600);
addDS(DNSName("com."), 300, res->d_records, keys);
addRRSIG(keys, res->d_records, DNSName("."), 300);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSRe
sourceRecord::ADDITIONAL, 3600);
return 1;
}
else if (ip == ComboAddress("192.0.2.1:53")) {
if (domain == DNSName("com.")) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
addRRSIG(keys, res->d_records, domain, 300);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNS
ResourceRecord::ADDITIONAL, 3600);
addRRSIG(keys, res->d_records, domain, 300);
}
else {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "powerdns.com.", QType::NS, "ns1.powerdns.com.",
DNSResourceRecord::AUTHORITY, 3600);
addDS(DNSName("powerdns.com."), 300, res->d_records, keys);
addRRSIG(keys, res->d_records, DNSName("com."), 300);
addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSRe
sourceRecord::ADDITIONAL, 3600);
}
return 1;
}
else if (ip == ComboAddress("192.0.2.2:53")) {
setLWResult(res, 0, true, false, true);
if (type == QType::NS) {
if (domain == DNSName("powerdns.com.")) {
addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNS
ResourceRecord::ADDITIONAL, 3600);
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
}
else {
addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.c
om. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResource
Record::AUTHORITY, 3600);
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
addNSECRecordToLW(DNSName("www.powerdns.com."), DNSName("wwz.power
dns.com."), { QType::A, QType::NSEC, QType::RRSIG }, 600, res->d_records);
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
}
}
else {
addRecordToLW(res, domain, QType::A, "192.0.2.42");
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300, false,
boost::none, DNSName("*.powerdns.com"));
/* we need to add the proof that this name does not exist, so the wi
ldcard may apply */
addNSECRecordToLW(DNSName("a.powerdns.com."), DNSName("wwz.powerdns.
com."), { QType::A, QType::NSEC, QType::RRSIG }, 600, res->d_records);
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
}
return 1;
}
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 4);
BOOST_CHECK_EQUAL(queriesCount, 9);
/* again, to test the cache */
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 4);
BOOST_CHECK_EQUAL(queriesCount, 9);
}
BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec_nodata_nowildcard) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
setDNSSECValidation(sr, DNSSECMode::ValidateAll);
primeHints();
const DNSName target("www.com.");
testkeysset_t keys;
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dsAnchors.clear();
generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA25
6, keys, luaconfsCopy.dsAnchors);
generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA
256, keys);
g_luaconfs.setState(luaconfsCopy);
size_t queriesCount = 0;
sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const
DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct
timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveC
ontext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
if (type == QType::DS || type == QType::DNSKEY) {
if (type == QType::DS && domain == target) {
DNSName auth("com.");
setLWResult(res, 0, true, false, true);
addRecordToLW(res, auth, QType::SOA, "foo. bar. 2017032800 1800 900 60
4800 86400", DNSResourceRecord::AUTHORITY, 86400);
addRRSIG(keys, res->d_records, auth, 300);
/* add a NSEC denying the DS AND the existence of a cut (no NS) */
addNSECRecordToLW(domain, DNSName("z") + domain, { QType::NSEC }, 600,
res->d_records);
addRRSIG(keys, res->d_records, auth, 300);
return 1;
}
return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
}
else {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResour
ceRecord::AUTHORITY, 3600);
addDS(DNSName("com."), 300, res->d_records, keys);
addRRSIG(keys, res->d_records, DNSName("."), 300);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSRe
sourceRecord::ADDITIONAL, 3600);
return 1;
}
else if (ip == ComboAddress("192.0.2.1:53")) {
setLWResult(res, 0, true, false, true);
/* no data */
addRecordToLW(res, DNSName("com."), QType::SOA, "com. com. 2017032301
10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
addRRSIG(keys, res->d_records, DNSName("com."), 300);
/* no record for this name */
addNSECRecordToLW(DNSName("wwv.com."), DNSName("wwx.com."), { QType::N
SEC, QType::RRSIG }, 600, res->d_records);
addRRSIG(keys, res->d_records, DNSName("com."), 300);
/* a wildcard matches but has no record for this type */
addNSECRecordToLW(DNSName("*.com."), DNSName("com."), { QType::AAAA, Q
Type::NSEC, QType::RRSIG }, 600, res->d_records);
addRRSIG(keys, res->d_records, DNSName("com"), 300, false, boost::none
, DNSName("*.com"));
return 1;
}
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 6);
BOOST_CHECK_EQUAL(queriesCount, 6);
/* again, to test the cache */
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 6);
BOOST_CHECK_EQUAL(queriesCount, 6);
}
BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec3_nodata_nowildcard) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
setDNSSECValidation(sr, DNSSECMode::ValidateAll);
primeHints();
const DNSName target("www.com.");
testkeysset_t keys;
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dsAnchors.clear();
generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA25
6, keys, luaconfsCopy.dsAnchors);
generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA
256, keys);
g_luaconfs.setState(luaconfsCopy);
size_t queriesCount = 0;
sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const
DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct
timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveC
ontext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
if (type == QType::DS || type == QType::DNSKEY) {
if (type == QType::DS && domain == target) {
DNSName auth("com.");
setLWResult(res, 0, true, false, true);
addRecordToLW(res, auth, QType::SOA, "foo. bar. 2017032800 1800 900 60
4800 86400", DNSResourceRecord::AUTHORITY, 86400);
addRRSIG(keys, res->d_records, auth, 300);
/* add a NSEC3 denying the DS AND the existence of a cut (no NS) */
/* first the closest encloser */
addNSEC3UnhashedRecordToLW(DNSName("com."), auth, "whatever", { QType:
:A, QType::TXT, QType::RRSIG, QType::NSEC }, 600, res->d_records);
addRRSIG(keys, res->d_records, auth, 300);
/* then the next closer */
addNSEC3NarrowRecordToLW(domain, DNSName("com."), { QType::RRSIG, QTyp
e::NSEC }, 600, res->d_records);
addRRSIG(keys, res->d_records, auth, 300);
/* a wildcard matches but has no record for this type */
addNSEC3UnhashedRecordToLW(DNSName("*.com."), DNSName("com."), "whatev
er", { QType::AAAA, QType::NSEC, QType::RRSIG }, 600, res->d_records);
addRRSIG(keys, res->d_records, DNSName("com"), 300, false, boost::none
, DNSName("*.com"));
return 1;
}
return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
}
else {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResour
ceRecord::AUTHORITY, 3600);
addDS(DNSName("com."), 300, res->d_records, keys);
addRRSIG(keys, res->d_records, DNSName("."), 300);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSRe
sourceRecord::ADDITIONAL, 3600);
return 1;
}
else if (ip == ComboAddress("192.0.2.1:53")) {
setLWResult(res, 0, true, false, true);
/* no data */
addRecordToLW(res, DNSName("com."), QType::SOA, "com. com. 2017032301
10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
addRRSIG(keys, res->d_records, DNSName("com."), 300);
/* no record for this name */
/* first the closest encloser */
addNSEC3UnhashedRecordToLW(DNSName("com."), DNSName("com."), "whatever
", { QType::A, QType::TXT, QType::RRSIG, QType::NSEC }, 600, res->d_records);
addRRSIG(keys, res->d_records, DNSName("com."), 300);
/* then the next closer */
addNSEC3NarrowRecordToLW(domain, DNSName("com."), { QType::RRSIG, QTyp
e::NSEC }, 600, res->d_records);
addRRSIG(keys, res->d_records, DNSName("com."), 300);
/* a wildcard matches but has no record for this type */
addNSEC3UnhashedRecordToLW(DNSName("*.com."), DNSName("com."), "whatev
er", { QType::AAAA, QType::NSEC, QType::RRSIG }, 600, res->d_records);
addRRSIG(keys, res->d_records, DNSName("com"), 300, false, boost::none
, DNSName("*.com"));
return 1;
}
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 8);
BOOST_CHECK_EQUAL(queriesCount, 6);
/* again, to test the cache */
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 8);
BOOST_CHECK_EQUAL(queriesCount, 6);
}
BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec3_nodata_nowildcard_too_many_ite
rations) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
setDNSSECValidation(sr, DNSSECMode::ValidateAll);
primeHints();
const DNSName target("www.com.");
testkeysset_t keys;
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dsAnchors.clear();
generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA25
6, keys, luaconfsCopy.dsAnchors);
generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA
256, keys);
g_luaconfs.setState(luaconfsCopy);
size_t queriesCount = 0;
sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const
DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct
timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveC
ontext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
if (type == QType::DS || type == QType::DNSKEY) {
if (type == QType::DS && domain == target) {
DNSName auth("com.");
setLWResult(res, 0, true, false, true);
addRecordToLW(res, auth, QType::SOA, "foo. bar. 2017032800 1800 900 60
4800 86400", DNSResourceRecord::AUTHORITY, 86400);
addRRSIG(keys, res->d_records, auth, 300);
/* add a NSEC3 denying the DS AND the existence of a cut (no NS) */
/* first the closest encloser */
addNSEC3UnhashedRecordToLW(DNSName("com."), auth, "whatever", { QType:
:A, QType::TXT, QType::RRSIG, QType::NSEC }, 600, res->d_records, g_maxNSEC3Iter
ations + 100);
addRRSIG(keys, res->d_records, auth, 300);
/* then the next closer */
addNSEC3NarrowRecordToLW(domain, DNSName("com."), { QType::RRSIG, QTyp
e::NSEC }, 600, res->d_records, g_maxNSEC3Iterations + 100);
addRRSIG(keys, res->d_records, auth, 300);
/* a wildcard matches but has no record for this type */
addNSEC3UnhashedRecordToLW(DNSName("*.com."), DNSName("com."), "whatev
er", { QType::AAAA, QType::NSEC, QType::RRSIG }, 600, res->d_records, g_maxNSEC3
Iterations + 100);
addRRSIG(keys, res->d_records, DNSName("com"), 300, false, boost::none
, DNSName("*.com"));
return 1;
}
return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
}
else {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResour
ceRecord::AUTHORITY, 3600);
addDS(DNSName("com."), 300, res->d_records, keys);
addRRSIG(keys, res->d_records, DNSName("."), 300);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSRe
sourceRecord::ADDITIONAL, 3600);
return 1;
}
else if (ip == ComboAddress("192.0.2.1:53")) {
setLWResult(res, 0, true, false, true);
/* no data */
addRecordToLW(res, DNSName("com."), QType::SOA, "com. com. 2017032301
10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
addRRSIG(keys, res->d_records, DNSName("com."), 300);
/* no record for this name */
/* first the closest encloser */
addNSEC3UnhashedRecordToLW(DNSName("com."), DNSName("com."), "whatever
", { QType::A, QType::TXT, QType::RRSIG, QType::NSEC }, 600, res->d_records, g_m
axNSEC3Iterations + 100);
addRRSIG(keys, res->d_records, DNSName("com."), 300);
/* then the next closer */
addNSEC3NarrowRecordToLW(domain, DNSName("com."), { QType::RRSIG, QTyp
e::NSEC }, 600, res->d_records, g_maxNSEC3Iterations + 100);
addRRSIG(keys, res->d_records, DNSName("com."), 300);
/* a wildcard matches but has no record for this type */
addNSEC3UnhashedRecordToLW(DNSName("*.com."), DNSName("com."), "whatev
er", { QType::AAAA, QType::NSEC, QType::RRSIG }, 600, res->d_records, g_maxNSEC3
Iterations + 100);
addRRSIG(keys, res->d_records, DNSName("com"), 300, false, boost::none
, DNSName("*.com"));
return 1;
}
}
return 0;
});
/* we are generating NSEC3 with more iterations than we allow, so we should go
Insecure */
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 8);
BOOST_CHECK_EQUAL(queriesCount, 6);
/* again, to test the cache */
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 8);
BOOST_CHECK_EQUAL(queriesCount, 6);
}
BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec3_wildcard) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
setDNSSECValidation(sr, DNSSECMode::ValidateAll);
primeHints();
const DNSName target("www.powerdns.com.");
testkeysset_t keys;
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dsAnchors.clear();
generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA25
6, keys, luaconfsCopy.dsAnchors);
generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA
256, keys);
generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKe
eper::SHA256, keys);
g_luaconfs.setState(luaconfsCopy);
size_t queriesCount = 0;
sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const
DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct
timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveC
ontext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
if (type == QType::DS || type == QType::DNSKEY) {
if (type == QType::DS && domain == target) {
setLWResult(res, RCode::NoError, true, false, true);
addRecordToLW(res, DNSName("powerdns.com."), QType::SOA, "pdns-public-
ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600
", DNSResourceRecord::AUTHORITY, 3600);
addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300);
addNSECRecordToLW(DNSName("www.powerdns.com."), DNSName("wwz.powerdns.
com."), { QType::A, QType::NSEC, QType::RRSIG }, 600, res->d_records);
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
return 1;
}
else {
return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
}
}
else {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResour
ceRecord::AUTHORITY, 3600);
addDS(DNSName("com."), 300, res->d_records, keys);
addRRSIG(keys, res->d_records, DNSName("."), 300);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSRe
sourceRecord::ADDITIONAL, 3600);
return 1;
}
else if (ip == ComboAddress("192.0.2.1:53")) {
if (domain == DNSName("com.")) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
addRRSIG(keys, res->d_records, domain, 300);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNS
ResourceRecord::ADDITIONAL, 3600);
addRRSIG(keys, res->d_records, domain, 300);
}
else {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "powerdns.com.", QType::NS, "ns1.powerdns.com.",
DNSResourceRecord::AUTHORITY, 3600);
addDS(DNSName("powerdns.com."), 300, res->d_records, keys);
addRRSIG(keys, res->d_records, DNSName("com."), 300);
addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSRe
sourceRecord::ADDITIONAL, 3600);
}
return 1;
}
else if (ip == ComboAddress("192.0.2.2:53")) {
setLWResult(res, 0, true, false, true);
if (type == QType::NS) {
if (domain == DNSName("powerdns.com.")) {
addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNS
ResourceRecord::ADDITIONAL, 3600);
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
}
else {
addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.c
om. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResource
Record::AUTHORITY, 3600);
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
addNSECRecordToLW(DNSName("www.powerdns.com."), DNSName("wwz.power
dns.com."), { QType::A, QType::NSEC, QType::RRSIG }, 600, res->d_records);
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
}
}
else {
addRecordToLW(res, domain, QType::A, "192.0.2.42");
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300, false,
boost::none, DNSName("*.powerdns.com"));
/* we need to add the proof that this name does not exist, so the wi
ldcard may apply */
/* first the closest encloser */
addNSEC3UnhashedRecordToLW(DNSName("powerdns.com."), DNSName("powerd
ns.com."), "whatever", { QType::A, QType::TXT, QType::RRSIG, QType::NSEC }, 600,
res->d_records);
addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300);
/* then the next closer */
addNSEC3NarrowRecordToLW(DNSName("www.powerdns.com."), DNSName("powe
rdns.com."), { QType::A, QType::TXT, QType::RRSIG, QType::NSEC }, 600, res->d_re
cords);
addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300);
}
return 1;
}
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 6);
BOOST_CHECK_EQUAL(queriesCount, 9);
/* again, to test the cache */
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 6);
BOOST_CHECK_EQUAL(queriesCount, 9);
}
BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec3_wildcard_too_many_iterations)
{
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
setDNSSECValidation(sr, DNSSECMode::ValidateAll);
primeHints();
const DNSName target("www.powerdns.com.");
testkeysset_t keys;
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dsAnchors.clear();
generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA25
6, keys, luaconfsCopy.dsAnchors);
generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA
256, keys);
generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKe
eper::SHA256, keys);
g_luaconfs.setState(luaconfsCopy);
size_t queriesCount = 0;
sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const
DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct
timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveC
ontext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
if (type == QType::DS || type == QType::DNSKEY) {
if (type == QType::DS && domain == target) {
setLWResult(res, RCode::NoError, true, false, true);
addRecordToLW(res, DNSName("powerdns.com."), QType::SOA, "pdns-public-
ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600
", DNSResourceRecord::AUTHORITY, 3600);
addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300);
addNSECRecordToLW(DNSName("www.powerdns.com."), DNSName("wwz.powerdns.
com."), { QType::A, QType::NSEC, QType::RRSIG }, 600, res->d_records);
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
return 1;
}
else {
return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
}
}
else {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResour
ceRecord::AUTHORITY, 3600);
addDS(DNSName("com."), 300, res->d_records, keys);
addRRSIG(keys, res->d_records, DNSName("."), 300);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSRe
sourceRecord::ADDITIONAL, 3600);
return 1;
}
else if (ip == ComboAddress("192.0.2.1:53")) {
if (domain == DNSName("com.")) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
addRRSIG(keys, res->d_records, domain, 300);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNS
ResourceRecord::ADDITIONAL, 3600);
addRRSIG(keys, res->d_records, domain, 300);
}
else {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "powerdns.com.", QType::NS, "ns1.powerdns.com.",
DNSResourceRecord::AUTHORITY, 3600);
addDS(DNSName("powerdns.com."), 300, res->d_records, keys);
addRRSIG(keys, res->d_records, DNSName("com."), 300);
addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSRe
sourceRecord::ADDITIONAL, 3600);
}
return 1;
}
else if (ip == ComboAddress("192.0.2.2:53")) {
setLWResult(res, 0, true, false, true);
if (type == QType::NS) {
if (domain == DNSName("powerdns.com.")) {
addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNS
ResourceRecord::ADDITIONAL, 3600);
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
}
else {
addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.c
om. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResource
Record::AUTHORITY, 3600);
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
addNSECRecordToLW(DNSName("www.powerdns.com."), DNSName("wwz.power
dns.com."), { QType::A, QType::NSEC, QType::RRSIG }, 600, res->d_records);
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
}
}
else {
addRecordToLW(res, domain, QType::A, "192.0.2.42");
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300, false,
boost::none, DNSName("*.powerdns.com"));
/* we need to add the proof that this name does not exist, so the wi
ldcard may apply */
/* first the closest encloser */
addNSEC3UnhashedRecordToLW(DNSName("powerdns.com."), DNSName("powerd
ns.com."), "whatever", { QType::A, QType::TXT, QType::RRSIG, QType::NSEC }, 600,
res->d_records);
addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300);
/* then the next closer */
addNSEC3NarrowRecordToLW(DNSName("www.powerdns.com."), DNSName("powe
rdns.com."), { QType::A, QType::TXT, QType::RRSIG, QType::NSEC }, 600, res->d_re
cords, g_maxNSEC3Iterations + 100);
addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300);
}
return 1;
}
}
return 0;
});
/* the NSEC3 providing the denial of existence proof for the next closer has t
oo many iterations,
we should end up Insecure */
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 6);
BOOST_CHECK_EQUAL(queriesCount, 9);
/* again, to test the cache */
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 6);
BOOST_CHECK_EQUAL(queriesCount, 9);
}
BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec_wildcard_missing) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
setDNSSECValidation(sr, DNSSECMode::ValidateAll);
primeHints();
const DNSName target("www.powerdns.com.");
testkeysset_t keys;
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dsAnchors.clear();
generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA25
6, keys, luaconfsCopy.dsAnchors);
generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA
256, keys);
generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKe
eper::SHA256, keys);
g_luaconfs.setState(luaconfsCopy);
size_t queriesCount = 0;
sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const
DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct
timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveC
ontext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
if (type == QType::DS || type == QType::DNSKEY) {
if (type == QType::DS && domain == target) {
setLWResult(res, RCode::NoError, true, false, true);
addRecordToLW(res, DNSName("powerdns.com."), QType::SOA, "pdns-public-
ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600
", DNSResourceRecord::AUTHORITY, 3600);
addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300);
addNSECRecordToLW(DNSName("www.powerdns.com."), DNSName("wwz.powerdns.
com."), { QType::A, QType::NSEC, QType::RRSIG }, 600, res->d_records);
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
return 1;
}
else {
return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
}
}
else {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResour
ceRecord::AUTHORITY, 3600);
addDS(DNSName("com."), 300, res->d_records, keys);
addRRSIG(keys, res->d_records, DNSName("."), 300);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSRe
sourceRecord::ADDITIONAL, 3600);
return 1;
}
else if (ip == ComboAddress("192.0.2.1:53")) {
if (domain == DNSName("com.")) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
addRRSIG(keys, res->d_records, domain, 300);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNS
ResourceRecord::ADDITIONAL, 3600);
addRRSIG(keys, res->d_records, domain, 300);
}
else {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "powerdns.com.", QType::NS, "ns1.powerdns.com.",
DNSResourceRecord::AUTHORITY, 3600);
addDS(DNSName("powerdns.com."), 300, res->d_records, keys);
addRRSIG(keys, res->d_records, DNSName("com."), 300);
addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSRe
sourceRecord::ADDITIONAL, 3600);
}
return 1;
}
else if (ip == ComboAddress("192.0.2.2:53")) {
setLWResult(res, 0, true, false, true);
if (type == QType::NS) {
if (domain == DNSName("powerdns.com.")) {
addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNS
ResourceRecord::ADDITIONAL, 3600);
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
}
else {
addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.c
om. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResource
Record::AUTHORITY, 3600);
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
addNSECRecordToLW(DNSName("www.powerdns.com."), DNSName("wwz.power
dns.com."), { QType::A, QType::NSEC, QType::RRSIG }, 600, res->d_records);
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
}
}
else {
addRecordToLW(res, domain, QType::A, "192.0.2.42");
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300, false,
boost::none, DNSName("*.powerdns.com"));
}
return 1;
}
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 2);
BOOST_CHECK_EQUAL(queriesCount, 9);
/* again, to test the cache */
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 2);
BOOST_CHECK_EQUAL(queriesCount, 9);
}
BOOST_AUTO_TEST_CASE(test_dnssec_no_ds_on_referral_secure) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
setDNSSECValidation(sr, DNSSECMode::ValidateAll);
primeHints();
const DNSName target("www.powerdns.com.");
testkeysset_t keys;
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dsAnchors.clear();
generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA25
6, keys, luaconfsCopy.dsAnchors);
generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA
256, keys);
generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKe
eper::SHA256, keys);
g_luaconfs.setState(luaconfsCopy);
size_t queriesCount = 0;
size_t dsQueriesCount = 0;
sr->setAsyncCallback([target,&queriesCount,&dsQueriesCount,keys](const ComboAd
dress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int ED
NS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optiona
l<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger,
LWResult* res) {
queriesCount++;
if (type == QType::DS) {
DNSName auth(domain);
auth.chopOff();
dsQueriesCount++;
setLWResult(res, 0, true, false, true);
addDS(domain, 300, res->d_records, keys, DNSResourceRecord::ANSWER);
addRRSIG(keys, res->d_records, auth, 300);
return 1;
}
else if (type == QType::DNSKEY) {
setLWResult(res, 0, true, false, true);
addDNSKEY(keys, domain, 300, res->d_records);
addRRSIG(keys, res->d_records, domain, 300);
return 1;
}
else {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResour
ceRecord::AUTHORITY, 3600);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSRe
sourceRecord::ADDITIONAL, 3600);
/* No DS on referral, and no denial of the DS either */
return 1;
}
else if (ip == ComboAddress("192.0.2.1:53")) {
if (domain == DNSName("com.")) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
addRRSIG(keys, res->d_records, domain, 300);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNS
ResourceRecord::ADDITIONAL, 3600);
addRRSIG(keys, res->d_records, domain, 300);
}
else {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "powerdns.com.", QType::NS, "ns1.powerdns.com.",
DNSResourceRecord::AUTHORITY, 3600);
addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSRe
sourceRecord::ADDITIONAL, 3600);
/* No DS on referral, and no denial of the DS either */
}
return 1;
}
else if (ip == ComboAddress("192.0.2.2:53")) {
setLWResult(res, 0, true, false, true);
if (type == QType::NS) {
if (domain == DNSName("powerdns.com.")) {
addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNS
ResourceRecord::ADDITIONAL, 3600);
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
}
else {
addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.c
om. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResource
Record::AUTHORITY, 3600);
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
addNSECRecordToLW(DNSName("www.powerdns.com."), DNSName("wwz.power
dns.com."), { QType::A, QType::NSEC, QType::RRSIG }, 600, res->d_records);
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
}
}
else {
addRecordToLW(res, domain, QType::A, "192.0.2.42");
addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
}
return 1;
}
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 2);
BOOST_CHECK_EQUAL(queriesCount, 9);
BOOST_CHECK_EQUAL(dsQueriesCount, 3);
/* again, to test the cache */
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 2);
BOOST_CHECK_EQUAL(queriesCount, 9);
BOOST_CHECK_EQUAL(dsQueriesCount, 3);
}
BOOST_AUTO_TEST_CASE(test_dnssec_ds_sign_loop) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
setDNSSECValidation(sr, DNSSECMode::ValidateAll);
primeHints();
const DNSName target("www.powerdns.com.");
testkeysset_t keys;
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dsAnchors.clear();
generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA25
6, keys, luaconfsCopy.dsAnchors);
generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA
256, keys);
generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKe
eper::SHA256, keys);
generateKeyMaterial(DNSName("www.powerdns.com."), DNSSECKeeper::ECDSA256, DNSS
ECKeeper::SHA256, keys);
g_luaconfs.setState(luaconfsCopy);
size_t queriesCount = 0;
sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const
DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct
timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveC
ontext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
if (type == QType::DS) {
DNSName auth(domain);
auth.chopOff();
setLWResult(res, 0, true, false, true);
if (domain == target) {
addRecordToLW(res, domain, QType::SOA, "ns1.powerdns.com. blah. 201703
2800 1800 900 604800 86400", DNSResourceRecord::AUTHORITY, 86400);
addRRSIG(keys, res->d_records, target, 300);
}
else {
addDS(domain, 300, res->d_records, keys, DNSResourceRecord::ANSWER);
addRRSIG(keys, res->d_records, auth, 300);
}
return 1;
}
else if (type == QType::DNSKEY) {
setLWResult(res, 0, true, false, true);
addDNSKEY(keys, domain, 300, res->d_records);
addRRSIG(keys, res->d_records, domain, 300);
return 1;
}
else {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResour
ceRecord::AUTHORITY, 3600);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSRe
sourceRecord::ADDITIONAL, 3600);
addDS(DNSName("com."), 300, res->d_records, keys);
addRRSIG(keys, res->d_records, DNSName("."), 300);
return 1;
}
else if (ip == ComboAddress("192.0.2.1:53")) {
if (domain == DNSName("com.")) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
addRRSIG(keys, res->d_records, domain, 300);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNS
ResourceRecord::ADDITIONAL, 3600);
addRRSIG(keys, res->d_records, domain, 300);
}
else {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "powerdns.com.", QType::NS, "ns1.powerdns.com.",
DNSResourceRecord::AUTHORITY, 3600);
/* no DS */
addNSECRecordToLW(domain, DNSName("z.powerdns.com."), { QType::NS },
600, res->d_records);
addRRSIG(keys, res->d_records, DNSName("com."), 300);
addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSRe
sourceRecord::ADDITIONAL, 3600);
}
return 1;
}
else if (ip == ComboAddress("192.0.2.2:53")) {
if (type == QType::NS) {
if (domain == DNSName("powerdns.com.")) {
setLWResult(res, RCode::Refused, false, false, true);
}
else {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
addRRSIG(keys, res->d_records, domain, 300);
addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNS
ResourceRecord::ADDITIONAL, 3600);
addRRSIG(keys, res->d_records, domain, 300);
}
}
else {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::A, "192.0.2.42");
addRRSIG(keys, res->d_records, DNSName("www.powerdns.com"), 300);
}
return 1;
}
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 2);
BOOST_CHECK_EQUAL(queriesCount, 9);
/* again, to test the cache */
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 2);
BOOST_CHECK_EQUAL(queriesCount, 9);
}
BOOST_AUTO_TEST_CASE(test_dnssec_dnskey_signed_child) {
/* check that we don't accept a signer below us */
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
setDNSSECValidation(sr, DNSSECMode::ValidateAll);
primeHints();
const DNSName target("www.powerdns.com.");
testkeysset_t keys;
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dsAnchors.clear();
generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA25
6, keys, luaconfsCopy.dsAnchors);
generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA
256, keys);
generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKe
eper::SHA256, keys);
generateKeyMaterial(DNSName("www.powerdns.com."), DNSSECKeeper::ECDSA256, DNSS
ECKeeper::SHA256, keys);
generateKeyMaterial(DNSName("sub.www.powerdns.com."), DNSSECKeeper::ECDSA256,
DNSSECKeeper::SHA256, keys);
g_luaconfs.setState(luaconfsCopy);
size_t queriesCount = 0;
sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const
DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct
timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveC
ontext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
if (type == QType::DS) {
DNSName auth(domain);
auth.chopOff();
setLWResult(res, 0, true, false, true);
if (domain == target) {
addRecordToLW(res, domain, QType::SOA, "ns1.powerdns.com. blah. 201703
2800 1800 900 604800 86400", DNSResourceRecord::AUTHORITY, 86400);
addRRSIG(keys, res->d_records, target, 300);
}
else {
addDS(domain, 300, res->d_records, keys, DNSResourceRecord::ANSWER);
addRRSIG(keys, res->d_records, auth, 300);
}
return 1;
}
else if (type == QType::DNSKEY) {
setLWResult(res, 0, true, false, true);
addDNSKEY(keys, domain, 300, res->d_records);
if (domain == DNSName("www.powerdns.com.")) {
addRRSIG(keys, res->d_records, DNSName("sub.www.powerdns.com."), 300);
}
else {
addRRSIG(keys, res->d_records, domain, 300);
}
return 1;
}
else {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResour
ceRecord::AUTHORITY, 3600);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSRe
sourceRecord::ADDITIONAL, 3600);
addDS(DNSName("com."), 300, res->d_records, keys);
addRRSIG(keys, res->d_records, DNSName("."), 300);
return 1;
}
else if (ip == ComboAddress("192.0.2.1:53")) {
if (domain == DNSName("com.")) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
addRRSIG(keys, res->d_records, domain, 300);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNS
ResourceRecord::ADDITIONAL, 3600);
addRRSIG(keys, res->d_records, domain, 300);
}
else {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "powerdns.com.", QType::NS, "ns1.powerdns.com.",
DNSResourceRecord::AUTHORITY, 3600);
addDS(DNSName("powerdns.com."), 300, res->d_records, keys);
addRRSIG(keys, res->d_records, DNSName("com."), 300);
addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSRe
sourceRecord::ADDITIONAL, 3600);
}
return 1;
}
else if (ip == ComboAddress("192.0.2.2:53")) {
if (type == QType::NS) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
addRRSIG(keys, res->d_records, domain, 300);
addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSRe
sourceRecord::ADDITIONAL, 3600);
addRRSIG(keys, res->d_records, domain, 300);
}
else {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::A, "192.0.2.42");
addRRSIG(keys, res->d_records, domain, 300);
}
return 1;
}
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 2);
BOOST_CHECK_EQUAL(queriesCount, 9);
/* again, to test the cache */
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 2);
BOOST_CHECK_EQUAL(queriesCount, 9);
}
BOOST_AUTO_TEST_CASE(test_dnssec_no_ds_on_referral_insecure) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
setDNSSECValidation(sr, DNSSECMode::ValidateAll);
primeHints();
const DNSName target("www.powerdns.com.");
testkeysset_t keys;
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dsAnchors.clear();
generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA25
6, keys, luaconfsCopy.dsAnchors);
generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA
256, keys);
g_luaconfs.setState(luaconfsCopy);
size_t queriesCount = 0;
size_t dsQueriesCount = 0;
sr->setAsyncCallback([target,&queriesCount,&dsQueriesCount,keys](const ComboAd
dress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int ED
NS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optiona
l<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger,
LWResult* res) {
queriesCount++;
if (type == QType::DS) {
DNSName auth(domain);
auth.chopOff();
dsQueriesCount++;
setLWResult(res, 0, true, false, true);
if (domain == DNSName("com.")) {
addDS(domain, 300, res->d_records, keys, DNSResourceRecord::ANSWER);
}
else {
addRecordToLW(res, "com.", QType::SOA, "a.gtld-servers.com. hostmaster
com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
addRRSIG(keys, res->d_records, DNSName("com."), 300);
addNSECRecordToLW(domain, DNSName("powerdnt.com."), { QType::NS }, 600
, res->d_records);
}
addRRSIG(keys, res->d_records, auth, 300);
return 1;
}
else if (type == QType::DNSKEY) {
setLWResult(res, 0, true, false, true);
addDNSKEY(keys, domain, 300, res->d_records);
addRRSIG(keys, res->d_records, domain, 300);
return 1;
}
else {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResour
ceRecord::AUTHORITY, 3600);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSRe
sourceRecord::ADDITIONAL, 3600);
/* No DS on referral, and no denial of the DS either */
return 1;
}
else if (ip == ComboAddress("192.0.2.1:53")) {
if (domain == DNSName("com.")) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
addRRSIG(keys, res->d_records, domain, 300);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNS
ResourceRecord::ADDITIONAL, 3600);
addRRSIG(keys, res->d_records, domain, 300);
}
else {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "powerdns.com.", QType::NS, "ns1.powerdns.com.",
DNSResourceRecord::AUTHORITY, 3600);
addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSRe
sourceRecord::ADDITIONAL, 3600);
/* No DS on referral, and no denial of the DS either */
}
return 1;
}
else if (ip == ComboAddress("192.0.2.2:53")) {
setLWResult(res, 0, true, false, true);
if (type == QType::NS) {
if (domain == DNSName("powerdns.com.")) {
addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNS
ResourceRecord::ADDITIONAL, 3600);
}
else {
addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.c
om. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResource
Record::AUTHORITY, 3600);
}
}
else {
addRecordToLW(res, domain, QType::A, "192.0.2.42");
}
return 1;
}
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK_EQUAL(queriesCount, 7);
BOOST_CHECK_EQUAL(dsQueriesCount, 2);
/* again, to test the cache */
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK_EQUAL(queriesCount, 7);
BOOST_CHECK_EQUAL(dsQueriesCount, 2);
}
BOOST_AUTO_TEST_CASE(test_dnssec_validation_bogus_unsigned_nsec) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
setDNSSECValidation(sr, DNSSECMode::ValidateAll);
primeHints();
const DNSName target("powerdns.com.");
testkeysset_t keys;
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dsAnchors.clear();
generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA25
6, keys, luaconfsCopy.dsAnchors);
generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA
256, keys);
generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKe
eper::SHA256, keys);
g_luaconfs.setState(luaconfsCopy);
size_t queriesCount = 0;
sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const
DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct
timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveC
ontext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
if (type == QType::DS || type == QType::DNSKEY) {
return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
}
else {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResour
ceRecord::AUTHORITY, 3600);
addDS(DNSName("com."), 300, res->d_records, keys);
addRRSIG(keys, res->d_records, DNSName("."), 300);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSRe
sourceRecord::ADDITIONAL, 3600);
return 1;
}
else if (ip == ComboAddress("192.0.2.1:53")) {
if (domain == DNSName("com.")) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, DNSName("com."), QType::NS, "a.gtld-servers.com."
);
addRRSIG(keys, res->d_records, DNSName("com."), 300);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNS
ResourceRecord::ADDITIONAL, 3600);
}
else {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.", DNSResour
ceRecord::AUTHORITY, 3600);
addDS(domain, 300, res->d_records, keys);
addRRSIG(keys, res->d_records, DNSName("com."), 300);
addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSRe
sourceRecord::ADDITIONAL, 3600);
}
return 1;
}
else if (ip == ComboAddress("192.0.2.2:53")) {
setLWResult(res, 0, true, false, true);
if (type == QType::NS) {
addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
addRRSIG(keys, res->d_records, domain, 300);
}
else {
addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com
. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRe
cord::AUTHORITY, 3600);
addRRSIG(keys, res->d_records, domain, 300);
addNSECRecordToLW(domain, DNSName("z.powerdns.com."), { QType::NS, Q
Type::DNSKEY }, 600, res->d_records);
/* NO RRSIG for the NSEC record! */
}
return 1;
}
}
return 0;
});
/* NSEC record without the corresponding RRSIG in a secure zone, should be Bog
us! */
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
BOOST_CHECK_EQUAL(ret.size(), 3);
BOOST_CHECK_EQUAL(queriesCount, 8);
/* again, to test the cache */
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 3);
BOOST_CHECK_EQUAL(queriesCount, 8);
}
BOOST_AUTO_TEST_CASE(test_dnssec_validation_bogus_no_nsec) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
setDNSSECValidation(sr, DNSSECMode::ValidateAll);
primeHints();
const DNSName target("powerdns.com.");
testkeysset_t keys;
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dsAnchors.clear();
generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA25
6, keys, luaconfsCopy.dsAnchors);
generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA
256, keys);
generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKe
eper::SHA256, keys);
g_luaconfs.setState(luaconfsCopy);
size_t queriesCount = 0;
sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const
DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct
timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveC
ontext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
if (type == QType::DS || type == QType::DNSKEY) {
return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
}
else {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResour
ceRecord::AUTHORITY, 3600);
addDS(DNSName("com."), 300, res->d_records, keys);
addRRSIG(keys, res->d_records, DNSName("."), 300);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSRe
sourceRecord::ADDITIONAL, 3600);
return 1;
}
else if (ip == ComboAddress("192.0.2.1:53")) {
if (domain == DNSName("com.")) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, DNSName("com."), QType::NS, "a.gtld-servers.com."
);
addRRSIG(keys, res->d_records, DNSName("com."), 300);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNS
ResourceRecord::ADDITIONAL, 3600);
}
else {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.", DNSResour
ceRecord::AUTHORITY, 3600);
addDS(domain, 300, res->d_records, keys);
addRRSIG(keys, res->d_records, DNSName("com."), 300);
addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSRe
sourceRecord::ADDITIONAL, 3600);
}
return 1;
}
else if (ip == ComboAddress("192.0.2.2:53")) {
setLWResult(res, 0, true, false, true);
if (type == QType::NS) {
addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
addRRSIG(keys, res->d_records, domain, 300);
}
else {
addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com
. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRe
cord::AUTHORITY, 3600);
addRRSIG(keys, res->d_records, domain, 300);
/* NO NSEC record! */
}
return 1;
}
}
return 0;
});
/* no NSEC record in a secure zone, should be Bogus! */
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
BOOST_CHECK_EQUAL(ret.size(), 2);
BOOST_CHECK_EQUAL(queriesCount, 8);
/* again, to test the cache */
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 2);
BOOST_CHECK_EQUAL(queriesCount, 8);
}
BOOST_AUTO_TEST_CASE(test_dnssec_secure_to_insecure) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
setDNSSECValidation(sr, DNSSECMode::ValidateAll);
primeHints();
const DNSName target("powerdns.com.");
const ComboAddress targetAddr("192.0.2.42");
testkeysset_t keys;
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dsAnchors.clear();
generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA25
6, keys, luaconfsCopy.dsAnchors);
generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA
256, keys);
g_luaconfs.setState(luaconfsCopy);
size_t queriesCount = 0;
sr->setAsyncCallback([target,targetAddr,&queriesCount,keys](const ComboAddress
& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Le
vel, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<con
st ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWRes
ult* res) {
queriesCount++;
if (type == QType::DS) {
if (domain == target) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com.
pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceReco
rd::AUTHORITY, 3600);
addNSECRecordToLW(domain, DNSName("z.powerdns.com."), { QType::NS }, 6
00, res->d_records);
addRRSIG(keys, res->d_records, DNSName("com."), 300);
return 1;
} else {
return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
}
}
else if (type == QType::DNSKEY) {
if (domain == g_rootdnsname || domain == DNSName("com.")) {
setLWResult(res, 0, true, false, true);
addDNSKEY(keys, domain, 300, res->d_records);
addRRSIG(keys, res->d_records, domain, 300);
return 1;
}
else {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com.
pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceReco
rd::AUTHORITY, 3600);
return 1;
}
}
else {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResour
ceRecord::AUTHORITY, 3600);
addDS(DNSName("com."), 300, res->d_records, keys);
addRRSIG(keys, res->d_records, DNSName("."), 300);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSRe
sourceRecord::ADDITIONAL, 3600);
return 1;
}
else if (ip == ComboAddress("192.0.2.1:53")) {
if (domain == DNSName("com.")) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, DNSName("com."), QType::NS, "a.gtld-servers.com."
);
addRRSIG(keys, res->d_records, DNSName("com."), 300);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNS
ResourceRecord::ADDITIONAL, 3600);
}
else {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.", DNSResour
ceRecord::AUTHORITY, 3600);
/* no DS */
addNSECRecordToLW(domain, DNSName("z.powerdns.com."), { QType::NS },
600, res->d_records);
addRRSIG(keys, res->d_records, DNSName("com."), 300);
addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSRe
sourceRecord::ADDITIONAL, 3600);
}
return 1;
}
else if (ip == ComboAddress("192.0.2.2:53")) {
setLWResult(res, 0, true, false, true);
if (type == QType::NS) {
addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
}
else {
addRecordToLW(res, domain, QType::A, targetAddr.toString());
}
return 1;
}
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK(ret[0].d_type == QType::A);
/* 4 NS: com at ., com at com, powerdns.com at com, powerdns.com at powerdns.c
om
4 DNSKEY: ., com (not for powerdns.com because DS denial in referral)
1 query for A */
BOOST_CHECK_EQUAL(queriesCount, 7);
/* again, to test the cache */
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 1);
BOOST_CHECK(ret[0].d_type == QType::A);
BOOST_CHECK_EQUAL(queriesCount, 7);
}
BOOST_AUTO_TEST_CASE(test_dnssec_secure_direct_ds) {
/*
Direct DS query:
- parent is secure, zone is secure: DS should be secure
*/
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
setDNSSECValidation(sr, DNSSECMode::ValidateAll);
primeHints();
const DNSName target("powerdns.com.");
testkeysset_t keys;
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dsAnchors.clear();
generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA25
6, keys, luaconfsCopy.dsAnchors);
generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA
256, keys);
generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKe
eper::SHA256, keys);
g_luaconfs.setState(luaconfsCopy);
size_t queriesCount = 0;
sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const
DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct
timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveC
ontext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
if (type == QType::DS || type == QType::DNSKEY) {
return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
}
else {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResour
ceRecord::AUTHORITY, 3600);
addDS(DNSName("com."), 300, res->d_records, keys);
addRRSIG(keys, res->d_records, DNSName("."), 300);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSRe
sourceRecord::ADDITIONAL, 3600);
return 1;
}
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::DS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 2);
for (const auto& record : ret) {
BOOST_CHECK(record.d_type == QType::DS || record.d_type == QType::RRSIG);
}
BOOST_CHECK_EQUAL(queriesCount, 4);
/* again, to test the cache */
ret.clear();
res = sr->beginResolve(target, QType(QType::DS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 2);
for (const auto& record : ret) {
BOOST_CHECK(record.d_type == QType::DS || record.d_type == QType::RRSIG);
}
BOOST_CHECK_EQUAL(queriesCount, 4);
}
BOOST_AUTO_TEST_CASE(test_dnssec_insecure_direct_ds) {
/*
Direct DS query:
- parent is secure, zone is insecure: DS denial should be secure
*/
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
setDNSSECValidation(sr, DNSSECMode::ValidateAll);
primeHints();
const DNSName target("powerdns.com.");
testkeysset_t keys;
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dsAnchors.clear();
generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA25
6, keys, luaconfsCopy.dsAnchors);
generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA
256, keys);
g_luaconfs.setState(luaconfsCopy);
size_t queriesCount = 0;
sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const
DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct
timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveC
ontext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
queriesCount++;
if (type == QType::DS || type == QType::DNSKEY) {
return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
}
else {
if (isRootServer(ip)) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResour
ceRecord::AUTHORITY, 3600);
addDS(DNSName("com."), 300, res->d_records, keys);
addRRSIG(keys, res->d_records, DNSName("."), 300);
addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSRe
sourceRecord::ADDITIONAL, 3600);
return 1;
}
}
return 0;
});
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::DS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 4);
for (const auto& record : ret) {
BOOST_CHECK(record.d_type == QType::SOA || record.d_type == QType::NSEC || r
ecord.d_type == QType::RRSIG);
}
BOOST_CHECK_EQUAL(queriesCount, 4);
/* again, to test the cache */
ret.clear();
res = sr->beginResolve(target, QType(QType::DS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 4);
for (const auto& record : ret) {
BOOST_CHECK(record.d_type == QType::SOA || record.d_type == QType::NSEC || r
ecord.d_type == QType::RRSIG);
}
BOOST_CHECK_EQUAL(queriesCount, 4);
}
BOOST_AUTO_TEST_CASE(test_dnssec_secure_to_insecure_skipped_cut) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
setDNSSECValidation(sr, DNSSECMode::ValidateAll);
primeHints();
const DNSName target("www.sub.powerdns.com.");
const ComboAddress targetAddr("192.0.2.42");
testkeysset_t keys;
auto luaconfsCopy = g_luaconfs.getCopy();
luaconfsCopy.dsAnchors.clear();
generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA25
6, keys, luaconfsCopy.dsAnchors);
generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA
256, keys);
generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKe
eper::SHA256, keys);
g_luaconfs.setState(luaconfsCopy);
size_t queriesCount = 0;
sr->setAsyncCallback([target,targetAddr,&queriesCount,keys](const ComboAddress
& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Le
vel, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<con
st ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWRes
ult* res) {
queriesCount++;
if (type == QType::DS) {
if (domain == DNSName("sub.powerdns.com.")) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com.
pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceReco
rd::AUTHORITY, 3600);
addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300);
addNSECRecordToLW(domain, DNSName("z.powerdns.com."), { QType::NS }, 6
00, res->d_records);
addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300);
return 1;
}
else if (domain == DNSName("www.sub.powerdns.com.")) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, DNSName("sub.powerdns.com."), QType::SOA, "pdns-pub
lic-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800
3600", DNSResourceRecord::AUTHORITY, 3600);
return 1;