From 30ee601ad03f8aecde901e2bb3c08041667c849d Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Thu, 16 Mar 2017 17:13:36 +0100 Subject: [PATCH] rec: Add unit tests for `SyncRes` --- pdns/pdns_recursor.cc | 65 +-- pdns/recursordist/Makefile.am | 13 +- pdns/recursordist/test-syncres_cc.cc | 608 +++++++++++++++++++++++++++ pdns/root-addresses.hh | 58 +-- pdns/root-dnssec.hh | 4 +- pdns/secpoll-recursor.cc | 2 +- pdns/syncres.cc | 60 ++- pdns/syncres.hh | 98 ++++- pdns/validate-recursor.cc | 8 +- 9 files changed, 802 insertions(+), 114 deletions(-) create mode 100644 pdns/recursordist/test-syncres_cc.cc diff --git a/pdns/pdns_recursor.cc b/pdns/pdns_recursor.cc index a29a97186..01ae81906 100644 --- a/pdns/pdns_recursor.cc +++ b/pdns/pdns_recursor.cc @@ -232,6 +232,11 @@ unsigned int getRecursorThreadId() return t_id; } +int getMTaskerTID() +{ + return MT->getTid(); +} + static void handleTCPClientWritable(int fd, FDMultiplexer::funcparam_t& var); // -1 is error, 0 is timeout, 1 is success @@ -675,8 +680,8 @@ static void protobufLogResponse(const std::shared_ptr& logger, con static void handleRPZCustom(const DNSRecord& spoofed, const QType& qtype, SyncRes& sr, int& res, vector& ret) { if (spoofed.d_type == QType::CNAME) { - bool oldWantsRPZ = sr.d_wantsRPZ; - sr.d_wantsRPZ = false; + bool oldWantsRPZ = sr.getWantsRPZ(); + sr.setWantsRPZ(false); vector ans; res = sr.beginResolve(DNSName(spoofed.d_content->getZoneRepresentation()), qtype, 1, ans); for (const auto& rec : ans) { @@ -685,7 +690,7 @@ static void handleRPZCustom(const DNSRecord& spoofed, const QType& qtype, SyncRe } } // Reset the RPZ state of the SyncRes - sr.d_wantsRPZ = oldWantsRPZ; + sr.setWantsRPZ(oldWantsRPZ); } } @@ -757,7 +762,7 @@ static void startDoResolve(void *p) } if(g_dnssecmode != DNSSECMode::Off) { - sr.d_doDNSSEC=true; + sr.setDoDNSSEC(true); // Does the requestor want DNSSEC records? if(edo.d_Z & EDNSOpts::DNSSECOK) { @@ -769,12 +774,12 @@ static void startDoResolve(void *p) pw.getHeader()->cd=0; } #ifdef HAVE_PROTOBUF - sr.d_initialRequestId = dc->d_uuid; + sr.setInitialRequestId(dc->d_uuid); #endif if (g_useIncomingECS) { - sr.d_incomingECSFound = dc->d_ecsFound; + sr.setIncomingECSFound(dc->d_ecsFound); if (dc->d_ecsFound) { - sr.d_incomingECS = dc->d_ednssubnet; + sr.setIncomingECS(dc->d_ednssubnet); } } @@ -835,7 +840,7 @@ static void startDoResolve(void *p) // if there is a RecursorLua active, and it 'took' the query in preResolve, we don't launch beginResolve if(!t_pdl->get() || !(*t_pdl)->preresolve(dq, res)) { - sr.d_wantsRPZ = wantsRPZ; + sr.setWantsRPZ(wantsRPZ); if(wantsRPZ) { switch(appliedPolicy.d_kind) { case DNSFilterEngine::PolicyKind::NoAction: @@ -1221,7 +1226,7 @@ static void startDoResolve(void *p) } sr.d_outqueries ? t_RC->cacheMisses++ : t_RC->cacheHits++; - float spent=makeFloat(sr.d_now-dc->d_now); + float spent=makeFloat(sr.getNow()-dc->d_now); if(spent < 0.001) g_stats.answers0_1++; else if(spent < 0.010) @@ -2073,7 +2078,7 @@ static void houseKeeping(void *) } if(now.tv_sec - last_rootupdate > 7200) { - int res = getRootNS(); + int res = SyncRes::getRootNS(g_now, nullptr); if (!res) last_rootupdate=now.tv_sec; } @@ -3300,43 +3305,3 @@ int main(int argc, char **argv) return ret; } - -int getRootNS(void) { - SyncRes sr(g_now); - sr.setDoEDNS0(true); - sr.setNoCache(); - sr.d_doDNSSEC = (g_dnssecmode != DNSSECMode::Off); - - vector ret; - int res=-1; - try { - res=sr.beginResolve(g_rootdnsname, QType(QType::NS), 1, ret); - if (g_dnssecmode != DNSSECMode::Off && g_dnssecmode != DNSSECMode::ProcessNoValidate) { - ResolveContext ctx; - auto state = validateRecords(ctx, ret); - if (state == Bogus) - throw PDNSException("Got Bogus validation result for .|NS"); - } - return res; - } - catch(PDNSException& e) - { - L< g_delegationOnly; +RecursorStats g_stats; +GlobalStateHolder g_luaconfs; +NetmaskGroup* g_dontQuery{nullptr}; +__thread MemRecursorCache* t_RC{nullptr}; +SyncRes::domainmap_t* g_initialDomainMap{nullptr}; +unsigned int g_numThreads = 1; + +/* Fake some required functions we didn't want the trouble to + link with */ +ArgvMap &arg() +{ + static ArgvMap theArg; + return theArg; +} + +int getMTaskerTID() +{ + return 0; +} + +bool RecursorLua4::preoutquery(const ComboAddress& ns, const ComboAddress& requestor, const DNSName& query, const QType& qtype, bool isTcp, vector& res, int& ret) +{ + return false; +} + +int asyncresolve(const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* res) +{ + return 0; +} + +/* primeHints() is only here for now because it + was way too much trouble to link with the real one. + We should fix this, empty functions are one thing, but this is + bad. +*/ + +#include "root-addresses.hh" + +void primeHints(void) +{ + vector nsset; + if(!t_RC) + t_RC = new MemRecursorCache(); + + DNSRecord arr, aaaarr, nsrr; + nsrr.d_name=g_rootdnsname; + arr.d_type=QType::A; + aaaarr.d_type=QType::AAAA; + nsrr.d_type=QType::NS; + arr.d_ttl=aaaarr.d_ttl=nsrr.d_ttl=time(nullptr)+3600000; + + for(char c='a';c<='m';++c) { + static char templ[40]; + strncpy(templ,"a.root-servers.net.", sizeof(templ) - 1); + templ[sizeof(templ)-1] = '\0'; + *templ=c; + aaaarr.d_name=arr.d_name=DNSName(templ); + nsrr.d_content=std::make_shared(DNSName(templ)); + arr.d_content=std::make_shared(ComboAddress(rootIps4[c-'a'])); + vector aset; + aset.push_back(arr); + t_RC->replace(time(0), DNSName(templ), QType(QType::A), aset, vector>(), true); // auth, nuke it all + if (rootIps6[c-'a'] != NULL) { + aaaarr.d_content=std::make_shared(ComboAddress(rootIps6[c-'a'])); + + vector aaaaset; + aaaaset.push_back(aaaarr); + t_RC->replace(time(0), DNSName(templ), QType(QType::AAAA), aaaaset, vector>(), true); + } + + nsset.push_back(nsrr); + } + t_RC->replace(time(0), g_rootdnsname, QType(QType::NS), nsset, vector>(), false); // and stuff in the cache +} + +LuaConfigItems::LuaConfigItems() +{ + for (const auto &dsRecord : rootDSs) { + auto ds=unique_ptr(dynamic_cast(DSRecordContent::make(dsRecord))); + dsAnchors[g_rootdnsname].insert(*ds); + } +} + +/* Some helpers functions */ + +static void init(bool debug=false) +{ + if (debug) { + L.setName("test"); + L.setLoglevel((Logger::Urgency)(6)); // info and up + L.disableSyslog(true); + L.toConsole(Logger::Info); + } + + seedRandom("/dev/urandom"); + + if (g_dontQuery) + delete g_dontQuery; + g_dontQuery = new NetmaskGroup(); + + if (t_RC) + delete t_RC; + t_RC = new MemRecursorCache(); + + if (g_initialDomainMap) + delete g_initialDomainMap; + g_initialDomainMap = new SyncRes::domainmap_t(); // new threads needs this to be setup + + SyncRes::s_maxqperq = 50; + SyncRes::s_maxtotusec = 1000*7000; + SyncRes::s_maxdepth = 40; + SyncRes::s_maxnegttl = 3600; + SyncRes::s_maxcachettl = 86400; + SyncRes::s_packetcachettl = 3600; + SyncRes::s_packetcacheservfailttl = 60; + SyncRes::s_serverdownmaxfails = 64; + SyncRes::s_serverdownthrottletime = 60; + SyncRes::s_doIPv6 = true; + + ::arg().set("ecs-ipv4-bits", "24"); + ::arg().set("ecs-ipv6-bits", "56"); +} + +static void initSR(std::unique_ptr& sr, bool edns0, bool dnssec) +{ + struct timeval now; + Utility::gettimeofday(&now, 0); + sr = std::unique_ptr(new SyncRes(now)); + sr->setDoEDNS0(edns0); + sr->setDoDNSSEC(dnssec); + t_sstorage->domainmap = g_initialDomainMap; + t_sstorage->negcache.clear(); + t_sstorage->nsSpeeds.clear(); + t_sstorage->ednsstatus.clear(); + t_sstorage->throttle.clear(); + t_sstorage->fails.clear(); + t_sstorage->dnssecmap.clear(); +} + +static void setLWResult(LWResult* res, int rcode, bool aa=false, bool tc=false, bool edns=false) +{ + res->d_rcode = rcode; + res->d_aabit = aa; + res->d_tcbit = tc; + res->d_haveEDNS = edns; +} + +static void addRecordToLW(LWResult* res, const DNSName& name, uint16_t type, const std::string& content, DNSResourceRecord::Place place=DNSResourceRecord::ANSWER, uint32_t ttl=60) +{ + DNSRecord rec; + rec.d_place = place; + rec.d_name = name; + rec.d_type = type; + rec.d_ttl = ttl; + + if (type == QType::NS) { + rec.d_content = std::make_shared(DNSName(content)); + } + else if (type == QType::A) { + rec.d_content = std::make_shared(ComboAddress(content)); + } + else if (QType::AAAA) + { + rec.d_content = std::make_shared(ComboAddress(content)); + } + else { + rec.d_content = shared_ptr(DNSRecordContent::mastermake(type, QClass::IN, content)); + } + + res->d_records.push_back(rec); +} + +static void addRecordToLW(LWResult* res, const std::string& name, uint16_t type, const std::string& content, DNSResourceRecord::Place place=DNSResourceRecord::ANSWER, uint32_t ttl=60) +{ + addRecordToLW(res, DNSName(name), type, content, place, ttl); +} + +static bool isRootServer(const ComboAddress& ip) +{ + for (size_t idx = 0; idx < rootIps4Count; idx++) { + if (ip.toString() == rootIps4[idx]) { + return true; + } + } + + for (size_t idx = 0; idx < rootIps6Count; idx++) { + if (ip.toString() == rootIps6[idx]) { + return true; + } + } + return false; +} + +/* Real tests */ + +BOOST_AUTO_TEST_SUITE(syncres_cc) + +BOOST_AUTO_TEST_CASE(test_root_primed) { + std::unique_ptr sr; + init(); + initSR(sr, true, false); + + primeHints(); + + /* we are primed, we should be able to resolve NS . without any query */ + vector ret; + int res = sr->beginResolve(DNSName("."), QType(QType::NS), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, 0); + BOOST_CHECK_EQUAL(ret.size(), 13); +} + +BOOST_AUTO_TEST_CASE(test_root_not_primed) { + std::unique_ptr sr; + init(false); + initSR(sr, true, false); + + size_t queriesCount = 0; + + sr->setAsyncCallback([&queriesCount](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr 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.", DNSResourceRecord::ANSWER, 3600); + addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSResourceRecord::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 ret; + int res = sr->beginResolve(DNSName("."), QType(QType::NS), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, 0); + 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 sr; + init(); + initSR(sr, true, false); + std::set 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& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* res) { + + downServers.insert(ip); + return 0; + }); + + vector ret; + int res = sr->beginResolve(DNSName("."), QType(QType::NS), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, 2); + 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(t_sstorage->fails.value(server), 0); + } +} + +BOOST_AUTO_TEST_CASE(test_edns_formerr_fallback) { + std::unique_ptr sr; + init(); + initSR(sr, true, false); + + ComboAddress noEDNSServer; + size_t queriesWithEDNS = 0; + size_t queriesWithoutEDNS = 0; + + sr->setAsyncCallback([&queriesWithEDNS, &queriesWithoutEDNS, &noEDNSServer](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr outgoingLogger, 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 ret; + int res = sr->beginResolve(DNSName("powerdns.com."), QType(QType::A), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, 0); + BOOST_CHECK_EQUAL(ret.size(), 1); + BOOST_CHECK_EQUAL(queriesWithEDNS, 1); + BOOST_CHECK_EQUAL(queriesWithoutEDNS, 1); + BOOST_CHECK_EQUAL(t_sstorage->ednsstatus.size(), 1); + BOOST_CHECK_EQUAL(t_sstorage->ednsstatus[noEDNSServer].mode, SyncRes::EDNSStatus::NOEDNS); +} + +BOOST_AUTO_TEST_CASE(test_edns_notimp_fallback) { + std::unique_ptr sr; + init(); + initSR(sr, true, false); + + size_t queriesWithEDNS = 0; + size_t queriesWithoutEDNS = 0; + + sr->setAsyncCallback([&queriesWithEDNS, &queriesWithoutEDNS](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* 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 ret; + int res = sr->beginResolve(DNSName("powerdns.com."), QType(QType::A), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, 0); + 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 sr; + init(); + initSR(sr, true, false); + + sr->setAsyncCallback([](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr 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 TCP */ + vector ret; + int res = sr->beginResolve(DNSName("powerdns.com."), QType(QType::A), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, 0); +} + +BOOST_AUTO_TEST_CASE(test_all_nss_down) { + std::unique_ptr sr; + init(); + initSR(sr, true, false); + std::set downServers; + + primeHints(); + + sr->setAsyncCallback([&downServers](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* res) { + + if (isRootServer(ip)) { + setLWResult(res, 0, true, false, true); + addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800); + addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600); + addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600); + return 1; + } + else if (ip == ComboAddress("192.0.2.1:53") || ip == ComboAddress("[2001:DB8::1]:53")) { + setLWResult(res, 0, true, 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:DB8::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:DB8::3", DNSResourceRecord::ADDITIONAL, 172800); + return 1; + } + else { + downServers.insert(ip); + return 0; + } + }); + + vector ret; + int res = sr->beginResolve(DNSName("powerdns.com."), QType(QType::A), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, 2); + BOOST_CHECK_EQUAL(ret.size(), 0); + BOOST_CHECK_EQUAL(downServers.size(), 4); + + for (const auto& server : downServers) { + BOOST_CHECK_EQUAL(t_sstorage->fails.value(server), 1); + } +} + +BOOST_AUTO_TEST_CASE(test_glued_referral) { + std::unique_ptr sr; + init(); + initSR(sr, true, false); + + primeHints(); + + const DNSName target("powerdns.com."); + + sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr 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, true, false, true); + addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800); + addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600); + addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600); + return 1; + } + else if (ip == ComboAddress("192.0.2.1:53") || ip == ComboAddress("[2001:DB8::1]:53")) { + setLWResult(res, 0, true, 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:DB8::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:DB8::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:DB8::3]:53")) { + setLWResult(res, 0, true, false, true); + addRecordToLW(res, target, QType::A, "192.0.2.4"); + return 1; + } + else { + return 0; + } + }); + + vector 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_EQUAL(ret[0].d_type, QType::A); + BOOST_CHECK_EQUAL(ret[0].d_name, target); +} + +BOOST_AUTO_TEST_CASE(test_glueless_referral) { + std::unique_ptr sr; + init(); + initSR(sr, true, false); + + primeHints(); + + const DNSName target("powerdns.com."); + + sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* res) { + + if (isRootServer(ip)) { + setLWResult(res, 0, true, false, true); + + if (domain.isPartOf(DNSName("com."))) { + addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800); + } else if (domain.isPartOf(DNSName("org."))) { + addRecordToLW(res, "org.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800); + } + else { + setLWResult(res, RCode::NXDomain, false, false, true); + return 1; + } + + addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600); + addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600); + return 1; + } + else if (ip == ComboAddress("192.0.2.1:53") || ip == ComboAddress("[2001:DB8::1]:53")) { + if (domain == target) { + setLWResult(res, 0, true, false, true); + addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.org.", DNSResourceRecord::AUTHORITY, 172800); + addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns.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:DB8::3]:53")) { + setLWResult(res, 0, true, false, true); + addRecordToLW(res, target, QType::A, "192.0.2.4"); + return 1; + } + else { + return 0; + } + }); + + vector 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_EQUAL(ret[0].d_type, QType::A); + BOOST_CHECK_EQUAL(ret[0].d_name, target); +} + +/* + TODO: + +// cerr<<"asyncresolve called to ask "< ret; string version = "recursor-" +pkgv; diff --git a/pdns/syncres.cc b/pdns/syncres.cc index 22b5bafd8..f88900e41 100644 --- a/pdns/syncres.cc +++ b/pdns/syncres.cc @@ -111,8 +111,8 @@ static void accountAuthLatency(int usec, int family) SyncRes::SyncRes(const struct timeval& now) : d_outqueries(0), d_tcpoutqueries(0), d_throttledqueries(0), d_timeouts(0), d_unreachables(0), - d_totUsec(0), d_doDNSSEC(false), d_now(now), - d_cacheonly(false), d_nocache(false), d_doEDNS0(false), d_lm(s_lm) + d_totUsec(0), d_now(now), + d_cacheonly(false), d_nocache(false), d_doDNSSEC(false), d_doEDNS0(false), d_lm(s_lm) { if(!t_sstorage) { @@ -377,7 +377,7 @@ int SyncRes::asyncresolveWrapper(const ComboAddress& ip, bool ednsMANDATORY, con */ SyncRes::EDNSStatus* ednsstatus; - ednsstatus = &t_sstorage->ednsstatus[ip]; // does this include port? + ednsstatus = &t_sstorage->ednsstatus[ip]; // does this include port? YES if(ednsstatus->modeSetAt && ednsstatus->modeSetAt + 3600 < d_now.tv_sec) { *ednsstatus=SyncRes::EDNSStatus(); @@ -403,8 +403,13 @@ int SyncRes::asyncresolveWrapper(const ComboAddress& ip, bool ednsMANDATORY, con } else if(ednsMANDATORY || mode==EDNSStatus::UNKNOWN || mode==EDNSStatus::EDNSOK || mode==EDNSStatus::EDNSIGNORANT) EDNSLevel = 1; - - ret=asyncresolve(ip, domain, type, doTCP, sendRDQuery, EDNSLevel, now, srcmask, ctx, luaconfsLocal->outgoingProtobufServer, res); + + if (d_asyncResolve) { + ret = d_asyncResolve(ip, domain, type, doTCP, sendRDQuery, EDNSLevel, now, srcmask, ctx, luaconfsLocal->outgoingProtobufServer, res); + } + else { + ret=asyncresolve(ip, domain, type, doTCP, sendRDQuery, EDNSLevel, now, srcmask, ctx, luaconfsLocal->outgoingProtobufServer, res); + } if(ret < 0) { return ret; // transport error, nothing to learn here } @@ -685,7 +690,7 @@ void SyncRes::getBestNSFromCache(const DNSName &qname, const QType& qtype, vecto // We lost the root NS records primeHints(); LOG(prefix< ret; + int res=-1; + try { + res=sr.beginResolve(g_rootdnsname, QType(QType::NS), 1, ret); + if (g_dnssecmode != DNSSECMode::Off && g_dnssecmode != DNSSECMode::ProcessNoValidate) { + ResolveContext ctx; + auto state = validateRecords(ctx, ret); + if (state == Bogus) + throw PDNSException("Got Bogus validation result for .|NS"); + } + return res; + } + catch(PDNSException& e) + { + L<