From 3337c2f75f655f8986d3e96657e363e7e12ed830 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Tue, 11 Apr 2017 14:36:39 +0200 Subject: [PATCH] rec: Cleanup the auth and forward zones a bit --- pdns/pdns_recursor.cc | 8 +- pdns/recursordist/test-syncres_cc.cc | 593 +++++++++++++++++++++++++++ pdns/reczones.cc | 14 +- pdns/syncres.cc | 208 +++++----- pdns/syncres.hh | 31 +- 5 files changed, 748 insertions(+), 106 deletions(-) diff --git a/pdns/pdns_recursor.cc b/pdns/pdns_recursor.cc index 90d30ea35..26908b4cb 100644 --- a/pdns/pdns_recursor.cc +++ b/pdns/pdns_recursor.cc @@ -105,7 +105,7 @@ static thread_local std::unique_ptr t_tcpClientCounts; thread_local std::unique_ptr MT; // the big MTasker thread_local std::unique_ptr t_RC; thread_local std::unique_ptr t_packetCache; -thread_local FDMultiplexer* t_fdm; +thread_local FDMultiplexer* t_fdm{nullptr}; thread_local std::unique_ptr t_remotes, t_servfailremotes, t_largeanswerremotes; thread_local std::unique_ptr > > t_queryring, t_servfailqueryring; thread_local std::shared_ptr t_allowFrom; @@ -2043,9 +2043,9 @@ static void doStats(void) static void houseKeeping(void *) { - static __thread time_t last_stat, last_rootupdate, last_prune, last_secpoll; - static __thread int cleanCounter=0; - static __thread bool s_running; // houseKeeping can get suspended in secpoll, and be restarted, which makes us do duplicate work + static thread_local time_t last_stat, last_rootupdate, last_prune, last_secpoll; + static thread_local int cleanCounter=0; + static thread_local bool s_running; // houseKeeping can get suspended in secpoll, and be restarted, which makes us do duplicate work try { if(s_running) return; diff --git a/pdns/recursordist/test-syncres_cc.cc b/pdns/recursordist/test-syncres_cc.cc index d0d51ded5..e2338783b 100644 --- a/pdns/recursordist/test-syncres_cc.cc +++ b/pdns/recursordist/test-syncres_cc.cc @@ -414,6 +414,40 @@ BOOST_AUTO_TEST_CASE(test_tc_fallback_to_tcp) { BOOST_CHECK_EQUAL(res, 0); } +BOOST_AUTO_TEST_CASE(test_tc_over_tcp) { + std::unique_ptr sr; + init(); + initSR(sr, true, false); + + 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& srcmask, boost::optional context, std::shared_ptr 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 ret; + int res = sr->beginResolve(DNSName("powerdns.com."), QType(QType::A), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, 0); + BOOST_CHECK_EQUAL(tcpQueriesCount, 2); +} + BOOST_AUTO_TEST_CASE(test_all_nss_down) { std::unique_ptr sr; init(); @@ -1562,6 +1596,42 @@ BOOST_AUTO_TEST_CASE(test_flawed_nsset) { BOOST_CHECK_EQUAL(ret.size(), 1); } +BOOST_AUTO_TEST_CASE(test_completely_flawed_nsset) { + std::unique_ptr sr; + init(); + initSR(sr, true, false); + + primeHints(); + + const DNSName target("powerdns.com."); + size_t queriesCount = 0; + + sr->setAsyncCallback([&queriesCount,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) { + + queriesCount++; + + if (isRootServer(ip) && domain == target) { + setLWResult(res, 0, true, false, true); + addRecordToLW(res, domain, QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800); + addRecordToLW(res, domain, QType::NS, "pdns-public-ns3.powerdns.com.", DNSResourceRecord::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 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 sr; init(); @@ -2482,6 +2552,529 @@ BOOST_AUTO_TEST_CASE(test_auth_zone_delegation_oob) { BOOST_CHECK(sr->wasOutOfBand()); } +BOOST_AUTO_TEST_CASE(test_auth_zone) { + std::unique_ptr sr; + init(); + initSR(sr, true, false); + + 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("pdns-public-ns1.powerdns.com. 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(addr); + ad.d_records.insert(dr); + + auto map = std::make_shared(); + (*map)[target] = ad; + SyncRes::setDomainMap(map); + + 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++; + setLWResult(res, 0, true, false, true); + addRecordToLW(res, domain, QType::A, "192.0.2.42"); + return 1; + }); + + vector ret; + int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, 0); + BOOST_CHECK_EQUAL(ret.size(), 1); + BOOST_CHECK(ret[0].d_type == QType::A); + BOOST_CHECK_EQUAL(getRR(ret[0])->getCA().toString(), addr.toString()); + BOOST_CHECK_EQUAL(queriesCount, 0); +} + +BOOST_AUTO_TEST_CASE(test_auth_zone_cname_lead_to_oob) { + std::unique_ptr sr; + init(); + initSR(sr, true, false); + + 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("pdns-public-ns1.powerdns.com. 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(addr); + ad.d_records.insert(dr); + + auto map = std::make_shared(); + (*map)[authZone] = ad; + SyncRes::setDomainMap(map); + + sr->setAsyncCallback([&queriesCount,target,authZone](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 == target) { + setLWResult(res, 0, true, false, true); + addRecordToLW(res, target, QType::CNAME, authZone.toString(), DNSResourceRecord::ANSWER, 3600); + return 1; + } + + return 0; + }); + + vector ret; + int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, 0); + BOOST_CHECK_EQUAL(ret.size(), 2); + BOOST_CHECK(ret[0].d_type == QType::CNAME); + BOOST_CHECK_EQUAL(getRR(ret[0])->getTarget().toString(), authZone.toString()); + BOOST_CHECK(ret[1].d_type == QType::A); + BOOST_CHECK_EQUAL(getRR(ret[1])->getCA().toString(), addr.toString()); + BOOST_CHECK_EQUAL(queriesCount, 1); +} + +BOOST_AUTO_TEST_CASE(test_auth_zone_oob_lead_to_outgoing_queryb) { + std::unique_ptr sr; + init(); + initSR(sr, true, false); + + 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("pdns-public-ns1.powerdns.com. 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(externalCNAME); + ad.d_records.insert(dr); + + auto map = std::make_shared(); + (*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& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* res) { + + queriesCount++; + + if (domain == externalCNAME) { + setLWResult(res, 0, true, false, true); + addRecordToLW(res, externalCNAME, QType::A, addr.toString(), DNSResourceRecord::ANSWER, 3600); + return 1; + } + + return 0; + }); + + vector ret; + int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, 0); + BOOST_CHECK_EQUAL(ret.size(), 2); + BOOST_CHECK(ret[0].d_type == QType::CNAME); + BOOST_CHECK_EQUAL(getRR(ret[0])->getTarget().toString(), externalCNAME.toString()); + BOOST_CHECK(ret[1].d_type == QType::A); + BOOST_CHECK_EQUAL(getRR(ret[1])->getCA().toString(), addr.toString()); + BOOST_CHECK_EQUAL(queriesCount, 1); +} + +BOOST_AUTO_TEST_CASE(test_auth_zone_nodata) { + std::unique_ptr sr; + init(); + initSR(sr, true, false); + + 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(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("pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600"); + ad.d_records.insert(dr); + + auto map = std::make_shared(); + (*map)[authZone] = ad; + SyncRes::setDomainMap(map); + + 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++; + + return 0; + }); + + vector ret; + int res = sr->beginResolve(target, QType(QType::AAAA), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, 0); + 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 sr; + init(); + initSR(sr, true, false); + + 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("pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600"); + ad.d_records.insert(dr); + + auto map = std::make_shared(); + (*map)[authZone] = ad; + SyncRes::setDomainMap(map); + + 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++; + + return 0; + }); + + vector 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 sr; + init(); + 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("pdns-public-ns1.powerdns.com. 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(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(nsAddr); + ad.d_records.insert(dr); + + auto map = std::make_shared(); + (*map)[authZone] = ad; + SyncRes::setDomainMap(map); + + sr->setAsyncCallback([&queriesCount,target,targetAddr,nsAddr](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 (ip == ComboAddress(nsAddr.toString(), 53) && domain == target) { + setLWResult(res, 0, true, false, true); + addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600); + return 1; + } + + 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(ret[0].d_type == QType::A); + BOOST_CHECK_EQUAL(queriesCount, 1); +} + +BOOST_AUTO_TEST_CASE(test_auth_zone_delegation_point) { + std::unique_ptr sr; + init(); + initSR(sr, true, false); + + 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("pdns-public-ns1.powerdns.com. 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(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(nsAddr); + ad.d_records.insert(dr); + + auto map = std::make_shared(); + (*map)[authZone] = ad; + SyncRes::setDomainMap(map); + + sr->setAsyncCallback([&queriesCount,nsAddr,target,targetAddr](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 (ip == ComboAddress(nsAddr.toString(), 53) && domain == target) { + setLWResult(res, 0, true, false, true); + addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600); + return 1; + } + + 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(ret[0].d_type == QType::A); + BOOST_CHECK_EQUAL(queriesCount, 1); +} + +BOOST_AUTO_TEST_CASE(test_auth_zone_wildcard) { + std::unique_ptr sr; + init(); + initSR(sr, true, false); + + 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("pdns-public-ns1.powerdns.com. 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(targetAddr); + ad.d_records.insert(dr); + + auto map = std::make_shared(); + (*map)[authZone] = ad; + SyncRes::setDomainMap(map); + + 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++; + + 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(ret[0].d_type == QType::A); + BOOST_CHECK_EQUAL(queriesCount, 0); +} + +BOOST_AUTO_TEST_CASE(test_auth_zone_wildcard_nodata) { + std::unique_ptr sr; + init(); + initSR(sr, true, false); + + 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("pdns-public-ns1.powerdns.com. 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(targetAddr); + ad.d_records.insert(dr); + + auto map = std::make_shared(); + (*map)[authZone] = ad; + SyncRes::setDomainMap(map); + + 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++; + + return 0; + }); + + vector ret; + int res = sr->beginResolve(target, QType(QType::AAAA), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, 0); + 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 sr; + init(); + initSR(sr, true, false); + + 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("pdns-public-ns1.powerdns.com. 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(addr); + ad.d_records.insert(dr); + + auto map = std::make_shared(); + (*map)[target] = ad; + SyncRes::setDomainMap(map); + + 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++; + 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 ret; + int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, 0); + BOOST_CHECK_EQUAL(ret.size(), 1); + BOOST_CHECK(ret[0].d_type == QType::A); + BOOST_CHECK_EQUAL(getRR(ret[0])->getCA().toString(), addr.toString()); + BOOST_CHECK_EQUAL(queriesCount, 0); +} + /* // cerr<<"asyncresolve called to ask "< newMap, const } else { L< newMap, cons else { if(ipparts.size()==4) L< parseAuthAndForwards() ad.d_rdForward = true; } } - - (*newMap)[DNSName(headers.first)]=ad; + + ad.d_name = DNSName(headers.first); + (*newMap)[ad.d_name]=ad; } } @@ -502,7 +505,8 @@ std::shared_ptr parseAuthAndForwards() throw PDNSException("Conversion error parsing line "+std::to_string(linenum)+" of " +::arg()["forward-zones-file"]); } - (*newMap)[DNSName(domain)]=ad; + ad.d_name = DNSName(domain); + (*newMap)[ad.d_name]=ad; } L<size() - before<<" forwarding instructions from file '"<<::arg()["forward-zones-file"]<<"'"< SyncRes::s_delegationOnly; +std::unique_ptr SyncRes::s_dontQuery{nullptr}; +NetmaskGroup SyncRes::s_ednssubnets; +SuffixMatchNode SyncRes::s_ednsdomains; +string SyncRes::s_serverID; +SyncRes::LogMode SyncRes::s_lm; + unsigned int SyncRes::s_maxnegttl; unsigned int SyncRes::s_maxcachettl; +unsigned int SyncRes::s_maxqperq; +unsigned int SyncRes::s_maxtotusec; +unsigned int SyncRes::s_maxdepth; +unsigned int SyncRes::s_minimumTTL; unsigned int SyncRes::s_packetcachettl; unsigned int SyncRes::s_packetcacheservfailttl; unsigned int SyncRes::s_serverdownmaxfails; @@ -54,24 +65,13 @@ std::atomic SyncRes::s_nodelegated; std::atomic SyncRes::s_unreachables; uint8_t SyncRes::s_ecsipv4limit; uint8_t SyncRes::s_ecsipv6limit; -unsigned int SyncRes::s_minimumTTL; bool SyncRes::s_doIPv6; bool SyncRes::s_nopacketcache; bool SyncRes::s_rootNXTrust; -unsigned int SyncRes::s_maxqperq; -unsigned int SyncRes::s_maxtotusec; -unsigned int SyncRes::s_maxdepth; -string SyncRes::s_serverID; -SyncRes::LogMode SyncRes::s_lm; -std::unordered_set SyncRes::s_delegationOnly; -std::unique_ptr SyncRes::s_dontQuery{nullptr}; -NetmaskGroup SyncRes::s_ednssubnets; -SuffixMatchNode SyncRes::s_ednsdomains; +bool SyncRes::s_noEDNS; #define LOG(x) if(d_lm == Log) { L <&ret, unsigned int depth, int& res) +void SyncRes::AuthDomain::addSOA(std::vector& records) const { - string prefix; - if(doLog()) { - prefix=d_prefix; - prefix.append(depth, ' '); + SyncRes::AuthDomain::records_t::const_iterator ziter = d_records.find(boost::make_tuple(getName(), QType::SOA)); + if (ziter != d_records.end()) { + DNSRecord dr = *ziter; + dr.d_place = DNSResourceRecord::AUTHORITY; + records.push_back(dr); + } + else { + // cerr<& records) const +{ + int result = RCode::NoError; + records.clear(); - domainmap_t::const_iterator iter=getBestAuthZone(&authdomain); - if(iter==t_sstorage.domainmap->end()) { - LOG(prefix< range; - - range=iter->second.d_records.equal_range(tie(qname)); // partial lookup - - ret.clear(); - AuthDomain::records_t::const_iterator ziter; - bool somedata=false; - for(ziter=range.first; ziter!=range.second; ++ziter) { - somedata=true; - if(qtype.getCode()==QType::ANY || ziter->d_type==qtype.getCode() || ziter->d_type==QType::CNAME) // let rest of nameserver do the legwork on this one - ret.push_back(*ziter); - else if(ziter->d_type == QType::NS && ziter->d_name.countLabels() > authdomain.countLabels()) { // we hit a delegation point! - DNSRecord dr=*ziter; + // partial lookup + std::pair range = d_records.equal_range(tie(qname)); + + SyncRes::AuthDomain::records_t::const_iterator ziter; + bool somedata = false; + + for(ziter = range.first; ziter != range.second; ++ziter) { + somedata = true; + + if(qtype == QType::ANY || ziter->d_type == qtype || ziter->d_type == QType::CNAME) { + // let rest of nameserver do the legwork on this one + records.push_back(*ziter); + } + else if (ziter->d_type == QType::NS && ziter->d_name.countLabels() > getName().countLabels()) { + // we hit a delegation point! + DNSRecord dr = *ziter; dr.d_place=DNSResourceRecord::AUTHORITY; - ret.push_back(dr); + records.push_back(dr); } } - if(!ret.empty()) { - LOG(prefix<second.d_records.find(boost::make_tuple(authdomain, QType::SOA)); - if(ziter!=iter->second.d_records.end()) { - DNSRecord dr=*ziter; - dr.d_place=DNSResourceRecord::AUTHORITY; - ret.push_back(dr); - } - else - LOG(prefix<first && wcarddomain.chopOff()) { - LOG(prefix<second.d_records.equal_range(boost::make_tuple(g_wildcarddnsname+wcarddomain)); - if(range.first==range.second) + while(wcarddomain != getName() && wcarddomain.chopOff()) { + // cerr<first) { - range=iter->second.d_records.equal_range(boost::make_tuple(nsdomain,QType::NS)); - if(range.first==range.second) + while (nsdomain.chopOff() && nsdomain != getName()) { + range = d_records.equal_range(boost::make_tuple(nsdomain,QType::NS)); + if(range.first == range.second) continue; - for(ziter=range.first; ziter!=range.second; ++ziter) { - DNSRecord dr=*ziter; - dr.d_place=DNSResourceRecord::AUTHORITY; - ret.push_back(dr); + for(ziter = range.first; ziter != range.second; ++ziter) { + DNSRecord dr = *ziter; + dr.d_place = DNSResourceRecord::AUTHORITY; + records.push_back(dr); } } - if(ret.empty()) { - LOG(prefix<second.d_records.find(boost::make_tuple(authdomain, QType::SOA)); - if(ziter!=iter->second.d_records.end()) { - DNSRecord dr=*ziter; - dr.d_place=DNSResourceRecord::AUTHORITY; - ret.push_back(dr); - } - else { - LOG(prefix<&ret, int& res) const +{ + res = domain.getRecords(qname, qtype.getCode(), ret); return true; } +bool SyncRes::doOOBResolve(const DNSName &qname, const QType &qtype, vector&ret, unsigned int depth, int& res) +{ + string prefix; + if(doLog()) { + prefix=d_prefix; + prefix.append(depth, ' '); + } + + DNSName authdomain(qname); + domainmap_t::const_iterator iter=getBestAuthZone(&authdomain); + if(iter==t_sstorage.domainmap->end() || !iter->second.isAuth()) { + LOG(prefix<second, qname, qtype, ret, res); +} + void SyncRes::doEDNSDumpAndClose(int fd) { FILE* fp=fdopen(fd, "w"); @@ -486,13 +506,13 @@ int SyncRes::doResolve(const DNSName &qname, const QType &qtype, vectorend()) { - const vector& servers = iter->second.d_servers; - if(servers.empty()) { + if(iter->second.isAuth()) { ret.clear(); d_wasOutOfBand = doOOBResolve(qname, qtype, ret, depth, res); return res; } else { + const vector& servers = iter->second.d_servers; const ComboAddress remoteIP = servers.front(); LOG(prefix<end()) { - if( iter->second.d_servers.empty() ) + if( iter->second.isAuth() ) // this gets picked up in doResolveAt, the empty DNSName, combined with the // empty vector means 'we are auth for this zone' nsset.insert({DNSName(), {{}, false}}); @@ -740,7 +760,7 @@ DNSName SyncRes::getBestNSNamesFromCache(const DNSName &qname, const QType& qtyp // Again, picked up in doResolveAt. An empty DNSName, combined with a // non-empty vector of ComboAddresses means 'this is a forwarded domain' // This is actually picked up in retrieveAddressesForNS called from doResolveAt. - nsset.insert({DNSName(), {iter->second.d_servers, iter->second.d_rdForward}}); + nsset.insert({DNSName(), {iter->second.d_servers, iter->second.shouldRecurse() }}); } return authdomain; } diff --git a/pdns/syncres.hh b/pdns/syncres.hh index c8a3a9b3c..e069eafc1 100644 --- a/pdns/syncres.hh +++ b/pdns/syncres.hh @@ -328,10 +328,9 @@ public: typedef map nsspeeds_t; typedef map ednsstatus_t; - struct AuthDomain + class AuthDomain { - vector d_servers; - bool d_rdForward; + public: typedef multi_index_container < DNSRecord, indexed_by < @@ -344,7 +343,32 @@ public: > > > records_t; + records_t d_records; + vector d_servers; + DNSName d_name; + bool d_rdForward{false}; + + int getRecords(const DNSName& qname, uint16_t qtype, std::vector& records) const; + bool isAuth() const + { + return d_servers.empty(); + } + bool isForward() const + { + return !isAuth(); + } + bool shouldRecurse() const + { + return d_rdForward; + } + const DNSName& getName() const + { + return d_name; + } + + private: + void addSOA(std::vector& records) const; }; typedef map domainmap_t; @@ -676,6 +700,7 @@ private: bool processAnswer(unsigned int depth, LWResult& lwr, const DNSName& qname, const QType& qtype, DNSName& auth, bool wasForwarded, const boost::optional ednsmask, bool sendRDQuery, NsSet &nameservers, std::vector& ret, const DNSFilterEngine& dfe, bool* gotNewServers, int* rcode); int doResolve(const DNSName &qname, const QType &qtype, vector&ret, unsigned int depth, set& beenthere); + bool doOOBResolve(const AuthDomain& domain, const DNSName &qname, const QType &qtype, vector&ret, int& res) const; bool doOOBResolve(const DNSName &qname, const QType &qtype, vector&ret, unsigned int depth, int &res); domainmap_t::const_iterator getBestAuthZone(DNSName* qname) const; bool doCNAMECacheCheck(const DNSName &qname, const QType &qtype, vector&ret, unsigned int depth, int &res); -- 2.49.0