From: Otto Moerbeek Date: Tue, 2 Dec 2025 14:25:09 +0000 (+0100) Subject: rec: if the IPs of the auths of a zone resolve to duplicate IPs, skip the dups X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c02c021edce938d13ed6678080ccb6c51ea53b37;p=thirdparty%2Fpdns.git rec: if the IPs of the auths of a zone resolve to duplicate IPs, skip the dups Signed-off-by: Otto Moerbeek --- diff --git a/pdns/recursordist/syncres.cc b/pdns/recursordist/syncres.cc index 7cec0c96d8..91a7456e93 100644 --- a/pdns/recursordist/syncres.cc +++ b/pdns/recursordist/syncres.cc @@ -5891,6 +5891,8 @@ int SyncRes::doResolveAt(NsSet& nameservers, DNSName auth, bool flawedNSSet, con LOG("Applying nsLimit " << nsLimit << endl); } + // If multiple NS records resolve to the same IP, we don't want to ask again, so keep track + std::set visitedAddresses; for (auto tns = rnameservers.cbegin();; ++tns) { if (addressQueriesForNS >= nsLimit) { throw ImmediateServFailException(std::to_string(nsLimit) + " (adjusted max-ns-address-qperq) or more queries with empty results for NS addresses sent resolving " + qname.toLogString()); @@ -5991,6 +5993,11 @@ int SyncRes::doResolveAt(NsSet& nameservers, DNSName auth, bool flawedNSSet, con } for (remoteIP = remoteIPs.begin(); remoteIP != remoteIPs.end(); ++remoteIP) { + auto inserted = visitedAddresses.insert(*remoteIP).second; + if (!wasForwarded && !inserted) { + LOG(prefix << qname << ": Already visited " << remoteIP->toStringWithPort() << ", asking '" << qname << "|" << qtype << "'; skipping" << endl); + continue; + } LOG(prefix << qname << ": Trying IP " << remoteIP->toStringWithPort() << ", asking '" << qname << "|" << qtype << "'" << endl); if (throttledOrBlocked(prefix, *remoteIP, qname, qtype, pierceDontQuery)) { diff --git a/pdns/recursordist/test-syncres_cc1.cc b/pdns/recursordist/test-syncres_cc1.cc index bca758dcfc..e0922f5c52 100644 --- a/pdns/recursordist/test-syncres_cc1.cc +++ b/pdns/recursordist/test-syncres_cc1.cc @@ -815,6 +815,58 @@ BOOST_AUTO_TEST_CASE(test_forward_ns_send_servfail) } } +BOOST_AUTO_TEST_CASE(test_forward_ns_send_servfail_same) +{ + std::unique_ptr sr; + initSR(sr); + + primeHints(); + + std::set downServers; + size_t queriesCount = 0; + + const DNSName target("www.refused."); + + SyncRes::AuthDomain ad; + // It is documented that having duplicate forwarders should result into a retry on servfail + const std::vector forwardedNSs{ComboAddress("192.0.2.42:53"), ComboAddress("192.0.2.42:53")}; + ad.d_rdForward = false; + ad.d_servers = forwardedNSs; + (*SyncRes::t_sstorage.domainmap)[DNSName("refused.")] = ad; + + sr->setAsyncCallback([&](const ComboAddress& address, const DNSName& /* domain */, int /* type */, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, std::optional& /* srcmask */, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) { + if (isRootServer(address)) { + 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(address); + + 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(), 0U); + BOOST_CHECK_EQUAL(downServers.size(), 1U); + BOOST_CHECK_EQUAL(queriesCount, 2U); + + const auto& server = forwardedNSs.at(0); + 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;