From: Pieter Lexis Date: Tue, 2 Jun 2020 12:55:08 +0000 (+0200) Subject: rec: Disable outgoing v4 when QLA has no v4 addresses X-Git-Tag: rec-4.4.0-beta1~56^2~11 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7b1d1a7d4fa6526f8629c9ad008668f4aa3edbbc;p=thirdparty%2Fpdns.git rec: Disable outgoing v4 when QLA has no v4 addresses --- diff --git a/pdns/pdns_recursor.cc b/pdns/pdns_recursor.cc index 73bcc4f719..0fcb702b6a 100644 --- a/pdns/pdns_recursor.cc +++ b/pdns/pdns_recursor.cc @@ -4158,6 +4158,15 @@ static int serviceMain(int argc, char*argv[]) exit(99); } + if(pdns::isQueryLocalAddressFamilyEnabled(AF_INET)) { + SyncRes::s_doIPv4=true; + g_log< + +#include "test-syncres_cc.hh" + +BOOST_AUTO_TEST_SUITE(syncres_cc10) +BOOST_AUTO_TEST_CASE(test_outgoing_v4_only) +{ + std::unique_ptr sr; + initSR(sr); + SyncRes::s_doIPv6 = false; + primeHints(); + bool v6Hit = false; + bool v4Hit = false; + int queries = 0; + + const DNSName target("powerdns.com."); + sr->setAsyncCallback([target, &v4Hit, &v6Hit, &queries](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, LWResult* res, bool* chained) { + queries++; + if (isRootServer(ip)) { + setLWResult(res, 0, false, false, true); + + if (domain == DNSName("powerdns.com.")) { + addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800); + addRecordToLW(res, "ns1.powerdns.com.", QType::AAAA, "2001:DB8:1::53", DNSResourceRecord::ADDITIONAL, 3600); + addRecordToLW(res, "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, false); + v4Hit = true; + if (domain == DNSName("powerdns.com.")) { + addRecordToLW(res, domain, QType::A, "192.0.2.2"); + } + return 1; + } + else if (ip == ComboAddress("[2001:DB8:1::53]:53")) { + setLWResult(res, 0, true, false, false); + v6Hit = true; + if (domain == DNSName("powerdns.com.")) { + addRecordToLW(res, domain, QType::A, "192.0.2.2"); + } + return 1; + } + return 0; + }); + + vector ret; + int rcode; + rcode = sr->beginResolve(target, QType(QType::A), QClass::IN, ret); + BOOST_REQUIRE_EQUAL(queries, 2); + BOOST_REQUIRE_EQUAL(v4Hit, true); + BOOST_REQUIRE_EQUAL(v6Hit, false); + BOOST_CHECK_EQUAL(rcode, RCode::NoError); + BOOST_CHECK_EQUAL(ret.size(), 1); +} + +BOOST_AUTO_TEST_CASE(test_outgoing_v4_only_no_A_in_delegation) +{ + // The name is not resolvable, as there's no A glue for an in-bailiwick NS + std::unique_ptr sr; + initSR(sr); + SyncRes::s_doIPv6 = false; + primeHints(); + int queries = 0; + + const DNSName target("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& srcmask, boost::optional context, LWResult* res, bool* chained) { + queries++; + if (isRootServer(ip)) { + setLWResult(res, 0, false, false, true); + + if (domain == DNSName("powerdns.com.")) { + addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800); + addRecordToLW(res, "ns1.powerdns.com.", QType::AAAA, "2001:DB8:1::53", DNSResourceRecord::ADDITIONAL, 3600); + } + return 1; + } + else if (ip == ComboAddress("[2001:DB8:1::53]:53")) { + setLWResult(res, 0, true, false, false); + if (domain == DNSName("powerdns.com.")) { + addRecordToLW(res, domain, QType::A, "192.0.2.2"); + } + return 1; + } + return 0; + }); + + vector ret; + int rcode; + rcode = sr->beginResolve(target, QType(QType::A), QClass::IN, ret); + BOOST_REQUIRE_EQUAL(queries, 14); // We keep trying all parent nameservers, this is wrong! + BOOST_CHECK_EQUAL(rcode, RCode::ServFail); + BOOST_CHECK_EQUAL(ret.size(), 0); +} + +BOOST_AUTO_TEST_CASE(test_outgoing_v6_only_no_AAAA_in_delegation) +{ + std::unique_ptr sr; + initSR(sr); + SyncRes::s_doIPv4 = false; + SyncRes::s_doIPv6 = true; + primeHints(); + int queries = 0; + + const DNSName target("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& srcmask, boost::optional context, LWResult* res, bool* chained) { + cout< ret; + int rcode; + rcode = sr->beginResolve(target, QType(QType::A), QClass::IN, ret); + BOOST_REQUIRE_EQUAL(queries, 14); // The recursor tries all parent nameservers... this needs to be fixed + BOOST_CHECK_EQUAL(rcode, RCode::ServFail); + BOOST_CHECK_EQUAL(ret.size(), 0); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/pdns/syncres.cc b/pdns/syncres.cc index 19eaa418a4..fe40aeea4b 100644 --- a/pdns/syncres.cc +++ b/pdns/syncres.cc @@ -85,6 +85,7 @@ uint8_t SyncRes::s_ecsipv6limit; uint8_t SyncRes::s_ecsipv4cachelimit; uint8_t SyncRes::s_ecsipv6cachelimit; +bool SyncRes::s_doIPv4; bool SyncRes::s_doIPv6; bool SyncRes::s_nopacketcache; bool SyncRes::s_rootNXTrust; @@ -934,7 +935,7 @@ vector SyncRes::getAddrs(const DNSName &qname, unsigned int depth, vState newState = Indeterminate; res_t resv4; // If IPv4 ever becomes second class, we should revisit this - if (doResolve(qname, QType::A, resv4, depth+1, beenthere, newState) == 0) { // this consults cache, OR goes out + if (s_doIPv4 && doResolve(qname, QType::A, resv4, depth+1, beenthere, newState) == 0) { // this consults cache, OR goes out for (auto const &i : resv4) { if (i.d_type == QType::A) { if (auto rec = getRR(i)) { @@ -943,7 +944,7 @@ vector SyncRes::getAddrs(const DNSName &qname, unsigned int depth, } } } - if (s_doIPv6) { + if (s_doIPv6) { // s_doIPv6 **IMPLIES** pdns::isQueryLocalAddressFamilyEnabled(AF_INET6) returned true if (ret.empty()) { // We did not find IPv4 addresses, try to get IPv6 ones newState = Indeterminate; @@ -1046,10 +1047,16 @@ void SyncRes::getBestNSFromCache(const DNSName &qname, const QType& qtype, vecto for(auto k=ns.cbegin();k!=ns.cend(); ++k) { if(k->d_ttl > (unsigned int)d_now.tv_sec ) { vector aset; + QType nsqt{QType::ADDR}; + if (s_doIPv4 && !s_doIPv6) { + nsqt = QType::A; + } else if (!s_doIPv4 && s_doIPv6) { + nsqt = QType::AAAA; + } const DNSRecord& dr=*k; auto nrr = getRR(dr); - if(nrr && (!nrr->getNS().isPartOf(subdomain) || s_RC->get(d_now.tv_sec, nrr->getNS(), s_doIPv6 ? QType(QType::ADDR) : QType(QType::A), + if(nrr && (!nrr->getNS().isPartOf(subdomain) || s_RC->get(d_now.tv_sec, nrr->getNS(), nsqt, false, doLog() ? &aset : 0, d_cacheRemote, d_routingTag) > 5)) { bestns.push_back(dr); LOG(prefix< '"<getNS()<<"'"<