From: Remi Gacogne Date: Thu, 15 Oct 2020 13:05:01 +0000 (+0200) Subject: rec: Throttle servers sending invalid data and rcodes X-Git-Tag: auth-4.4.0-alpha2~21^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ccc6e89bed0b2f89eb5f507625e434dff9ee1781;p=thirdparty%2Fpdns.git rec: Throttle servers sending invalid data and rcodes --- diff --git a/pdns/recursordist/test-syncres_cc1.cc b/pdns/recursordist/test-syncres_cc1.cc index 7228646011..6c298bf8cf 100644 --- a/pdns/recursordist/test-syncres_cc1.cc +++ b/pdns/recursordist/test-syncres_cc1.cc @@ -414,6 +414,242 @@ BOOST_AUTO_TEST_CASE(test_all_nss_network_error) } } +BOOST_AUTO_TEST_CASE(test_all_nss_send_tc_then_garbage_over_tcp) { + std::unique_ptr sr; + initSR(sr); + + primeHints(); + + std::set downServers; + + 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, LWResult* res, bool* chained) { + + if (isRootServer(ip)) { + setLWResult(res, 0, false, false, true); + addRecordToLW(res, "lock-up.", 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 LWResult::Result::Success; + } + + if (!doTCP) { + setLWResult(res, 0, false, true, false); + return LWResult::Result::Success; + } + else { + downServers.insert(ip); + + setLWResult(res, RCode::FormErr, false, false, false); + res->d_validpacket = false; + return LWResult::Result::Success; + } + }); + + DNSName target("www.lock-up."); + + vector ret; + int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, RCode::ServFail); + BOOST_CHECK_EQUAL(ret.size(), 0U); + BOOST_CHECK_EQUAL(downServers.size(), 2U); + + for (const auto& server : downServers) { + BOOST_CHECK(SyncRes::isThrottled(time(nullptr), server, target, QType::A)); + BOOST_CHECK_EQUAL(SyncRes::getNSSpeed(DNSName("a.gtld-servers.net."), server), 1000000U); + } +} + +BOOST_AUTO_TEST_CASE(test_all_nss_send_garbage_over_udp) { + std::unique_ptr sr; + initSR(sr); + + primeHints(); + + std::set downServers; + size_t queriesCount = 0; + + sr->setAsyncCallback([&queriesCount, &downServers](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) { + + if (isRootServer(ip)) { + setLWResult(res, 0, false, false, true); + addRecordToLW(res, "lock-up.", 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 LWResult::Result::Success; + } + + ++queriesCount; + downServers.insert(ip); + + setLWResult(res, RCode::FormErr, false, false, false); + res->d_validpacket = false; + return LWResult::Result::Success; + }); + + DNSName target("www.lock-up."); + + vector ret; + int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, RCode::ServFail); + BOOST_CHECK_EQUAL(ret.size(), 0U); + BOOST_CHECK_EQUAL(downServers.size(), 2U); + /* two queries with EDNS, that's it */ + BOOST_CHECK_EQUAL(queriesCount, 2U); + + for (const auto& server : downServers) { + BOOST_CHECK(SyncRes::isThrottled(time(nullptr), server, target, QType::A)); + BOOST_CHECK_EQUAL(SyncRes::getNSSpeed(DNSName("a.gtld-servers.net."), server), 1000000U); + BOOST_CHECK_EQUAL(SyncRes::getEDNSStatus(server), SyncRes::EDNSStatus::EDNSIGNORANT); + } +} + +BOOST_AUTO_TEST_CASE(test_regular_ns_send_refused) { + std::unique_ptr sr; + initSR(sr); + + primeHints(); + + std::set downServers; + size_t queriesCount = 0; + + sr->setAsyncCallback([&queriesCount, &downServers](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) { + + if (isRootServer(ip)) { + setLWResult(res, 0, false, false, true); + addRecordToLW(res, "refused.", 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 LWResult::Result::Success; + } + + ++queriesCount; + downServers.insert(ip); + + setLWResult(res, RCode::Refused, false, false, true); + + return LWResult::Result::Success; + }); + + DNSName target("www.refused."); + + vector ret; + int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, RCode::ServFail); + BOOST_CHECK_EQUAL(ret.size(), 0U); + BOOST_CHECK_EQUAL(downServers.size(), 2U); + BOOST_CHECK_EQUAL(queriesCount, 2U); + + for (const auto& server : downServers) { + /* same as any other server */ + BOOST_CHECK(SyncRes::isThrottled(time(nullptr), server, target, QType::A)); + BOOST_CHECK_EQUAL(SyncRes::getNSSpeed(DNSName("a.gtld-servers.net."), server), 0U); + BOOST_CHECK_EQUAL(SyncRes::getEDNSStatus(server), SyncRes::EDNSStatus::EDNSOK); + } +} + +BOOST_AUTO_TEST_CASE(test_forward_ns_send_refused) { + std::unique_ptr sr; + initSR(sr); + + primeHints(); + + std::set downServers; + size_t queriesCount = 0; + + const DNSName target("www.refused."); + + SyncRes::AuthDomain ad; + const std::vector forwardedNSs { ComboAddress("192.0.2.42:53"), ComboAddress("192.0.2.43:53") }; + ad.d_rdForward = false; + ad.d_servers = forwardedNSs; + (*SyncRes::t_sstorage.domainmap)[target] = ad; + + sr->setAsyncCallback([&queriesCount, &downServers](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) { + + if (isRootServer(ip)) { + setLWResult(res, 0, false, false, true); + addRecordToLW(res, "refused.", 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 LWResult::Result::Success; + } + + ++queriesCount; + downServers.insert(ip); + + setLWResult(res, RCode::Refused, false, false, true); + + return LWResult::Result::Success; + }); + + 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); + BOOST_CHECK_EQUAL(downServers.size(), 2U); + BOOST_CHECK_EQUAL(queriesCount, 2U); + + for (const auto& server : forwardedNSs) { + BOOST_CHECK_EQUAL(downServers.count(server), 1U); + /* same as any other server */ + BOOST_CHECK(SyncRes::isThrottled(time(nullptr), server, target, QType::A)); + BOOST_CHECK_EQUAL(SyncRes::getNSSpeed(DNSName("a.gtld-servers.net."), server), 0U); + BOOST_CHECK_EQUAL(SyncRes::getEDNSStatus(server), SyncRes::EDNSStatus::EDNSOK); + } +} + +BOOST_AUTO_TEST_CASE(test_forward_ns_send_servfail) { + std::unique_ptr sr; + initSR(sr); + + primeHints(); + + std::set downServers; + size_t queriesCount = 0; + + const DNSName target("www.refused."); + + SyncRes::AuthDomain ad; + const std::vector forwardedNSs { ComboAddress("192.0.2.42:53"), ComboAddress("192.0.2.43:53") }; + ad.d_rdForward = false; + ad.d_servers = forwardedNSs; + (*SyncRes::t_sstorage.domainmap)[DNSName("refused.")] = ad; + + sr->setAsyncCallback([&queriesCount, &downServers](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) { + + if (isRootServer(ip)) { + setLWResult(res, 0, false, false, true); + addRecordToLW(res, "refused.", 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 LWResult::Result::Success; + } + + ++queriesCount; + downServers.insert(ip); + + setLWResult(res, RCode::ServFail, false, false, true); + + return LWResult::Result::Success; + }); + + 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); + BOOST_CHECK_EQUAL(downServers.size(), 2U); + BOOST_CHECK_EQUAL(queriesCount, 2U); + + for (const auto& server : forwardedNSs) { + BOOST_CHECK_EQUAL(downServers.count(server), 1U); + /* on servfail from a server we forward to we only increase the NS speed so + that a different server might be tried instead, but we don't throttle */ + BOOST_CHECK(!SyncRes::isThrottled(time(nullptr), server, target, QType::A)); + BOOST_CHECK_EQUAL(SyncRes::getNSSpeed(DNSName(server.toStringWithPort()), server), 1000000U); + BOOST_CHECK_EQUAL(SyncRes::getEDNSStatus(server), SyncRes::EDNSStatus::EDNSOK); + } +} + BOOST_AUTO_TEST_CASE(test_only_one_ns_up_resolving_itself_with_glue) { std::unique_ptr sr; diff --git a/pdns/syncres.cc b/pdns/syncres.cc index 172c20d021..3fb329d3f4 100644 --- a/pdns/syncres.cc +++ b/pdns/syncres.cc @@ -628,10 +628,11 @@ LWResult::Result SyncRes::asyncresolveWrapper(const ComboAddress& ip, bool ednsM t_sstorage.ednsstatus.setMode(ind, ednsstatus, EDNSStatus::EDNSOK); // cerr<<"We find that "<modeSetAt) + + if (oldmode != *mode || !ednsstatus->modeSetAt) { t_sstorage.ednsstatus.setTS(ind, ednsstatus, d_now.tv_sec); + } // cerr<<"Result: ret="<d_haveEDNS<<", new mode: "<& ednsmask, const DNSName& auth, bool const sendRDQuery, const DNSName& nsName, const ComboAddress& remoteIP, bool doTCP, bool* truncated) +bool SyncRes::doResolveAtThisIP(const std::string& prefix, const DNSName& qname, const QType& qtype, LWResult& lwr, boost::optional& ednsmask, const DNSName& auth, bool const sendRDQuery, const bool wasForwarded, const DNSName& nsName, const ComboAddress& remoteIP, bool doTCP, bool* truncated) { bool chained = false; LWResult::Result resolveret = LWResult::Result::Success; @@ -3622,14 +3623,41 @@ bool SyncRes::doResolveAtThisIP(const std::string& prefix, const DNSName& qname, return false; } - /* we got an answer */ - if(lwr.d_rcode==RCode::ServFail || lwr.d_rcode==RCode::Refused) { - LOG(prefix< 0) { @@ -3956,11 +3984,11 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con } bool truncated = false; - bool gotAnswer = doResolveAtThisIP(prefix, qname, qtype, lwr, ednsmask, auth, sendRDQuery, + bool gotAnswer = doResolveAtThisIP(prefix, qname, qtype, lwr, ednsmask, auth, sendRDQuery, wasForwarded, tns->first, *remoteIP, false, &truncated); if (gotAnswer && truncated ) { /* retry, over TCP this time */ - gotAnswer = doResolveAtThisIP(prefix, qname, qtype, lwr, ednsmask, auth, sendRDQuery, + gotAnswer = doResolveAtThisIP(prefix, qname, qtype, lwr, ednsmask, auth, sendRDQuery, wasForwarded, tns->first, *remoteIP, true, &truncated); } diff --git a/pdns/syncres.hh b/pdns/syncres.hh index 1880ba12bd..b38203bada 100644 --- a/pdns/syncres.hh +++ b/pdns/syncres.hh @@ -493,6 +493,10 @@ public: { t_sstorage.nsSpeeds.clear(); } + static float getNSSpeed(const DNSName& server, const ComboAddress& ca) + { + return t_sstorage.nsSpeeds[server].d_collection[ca].peek(); + } static EDNSStatus::EDNSMode getEDNSStatus(const ComboAddress& server) { const auto& it = t_sstorage.ednsstatus.find(server); @@ -804,7 +808,7 @@ private: int doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, const DNSName &qname, const QType &qtype, vector&ret, unsigned int depth, set&beenthere, vState& state, StopAtDelegation* stopAtDelegation); - bool doResolveAtThisIP(const std::string& prefix, const DNSName& qname, const QType& qtype, LWResult& lwr, boost::optional& ednsmask, const DNSName& auth, bool const sendRDQuery, const DNSName& nsName, const ComboAddress& remoteIP, bool doTCP, bool* truncated); + bool doResolveAtThisIP(const std::string& prefix, const DNSName& qname, const QType& qtype, LWResult& lwr, boost::optional& ednsmask, const DNSName& auth, bool const sendRDQuery, const bool wasForwarded, const DNSName& nsName, const ComboAddress& remoteIP, bool doTCP, bool* truncated); 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, vState& state); int doResolve(const DNSName &qname, const QType &qtype, vector&ret, unsigned int depth, set& beenthere, vState& state);