File ns-ampl-4.1.15.diff of Package pdns-recursor.openSUSE_Backports_SLE-15-SP1_Update
Index: pdns-recursor-4.1.12/pdns_recursor.cc
===================================================================
--- pdns-recursor-4.1.12.orig/pdns_recursor.cc
+++ pdns-recursor-4.1.12/pdns_recursor.cc
@@ -3134,6 +3134,7 @@ static int serviceMain(int argc, char*ar
SyncRes::s_serverdownthrottletime=::arg().asNum("server-down-throttle-time");
SyncRes::s_serverID=::arg()["server-id"];
SyncRes::s_maxqperq=::arg().asNum("max-qperq");
+ SyncRes::s_maxnsaddressqperq=::arg().asNum("max-ns-address-qperq");
SyncRes::s_maxtotusec=1000*::arg().asNum("max-total-msec");
SyncRes::s_maxdepth=::arg().asNum("max-recursion-depth");
SyncRes::s_rootNXTrust = ::arg().mustDo( "root-nx-trust");
@@ -3632,6 +3633,7 @@ int main(int argc, char **argv)
::arg().set("edns-outgoing-bufsize", "Outgoing EDNS buffer size")="1680";
::arg().set("minimum-ttl-override", "Set under adverse conditions, a minimum TTL")="0";
::arg().set("max-qperq", "Maximum outgoing queries per query")="50";
+ ::arg().set("max-ns-address-qperq", "Maximum outgoing NS address queries per query")="10";
::arg().set("max-total-msec", "Maximum total wall-clock time per query in milliseconds, 0 for unlimited")="7000";
::arg().set("max-recursion-depth", "Maximum number of internal recursion calls per query, 0 for unlimited")="40";
::arg().set("max-udp-queries-per-round", "Maximum number of UDP queries processed per recvmsg() round, before returning back to normal processing")="10000";
Index: pdns-recursor-4.1.12/test-syncres_cc.cc
===================================================================
--- pdns-recursor-4.1.12.orig/test-syncres_cc.cc
+++ pdns-recursor-4.1.12/test-syncres_cc.cc
@@ -131,7 +131,8 @@ static void init(bool debug=false)
t_RC = std::unique_ptr<MemRecursorCache>(new MemRecursorCache());
SyncRes::s_maxqperq = 50;
- SyncRes::s_maxtotusec = 1000*7000;
+ SyncRes::s_maxnsaddressqperq = 10;
+ SyncRes::s_maxtotusec = 1000 * 7000;
SyncRes::s_maxdepth = 40;
SyncRes::s_maxnegttl = 3600;
SyncRes::s_maxcachettl = 86400;
@@ -10444,6 +10445,48 @@ BOOST_AUTO_TEST_CASE(test_getDSRecords_m
}
#endif // HAVE_BOTAN110
+BOOST_AUTO_TEST_CASE(test_completely_flawed_big_nsset)
+{
+ std::unique_ptr<SyncRes> sr;
+ initSR(sr);
+
+ 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<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res, bool* chained) {
+ queriesCount++;
+
+ if (isRootServer(ip) && domain == target) {
+ setLWResult(res, 0, false, false, true);
+ // 20 NS records
+ for (int i = 0; i < 20; i++) {
+ string n = string("pdns-public-ns") + std::to_string(i) + string(".powerdns.com.");
+ addRecordToLW(res, domain, QType::NS, n, DNSResourceRecord::AUTHORITY, 172800);
+ }
+ return 1;
+ }
+ else if (domain.toString().length() > 14 && domain.toString().substr(0, 14) == "pdns-public-ns") {
+ 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<DNSRecord> ret;
+ try {
+ sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK(0);
+ } catch (const ImmediateServFailException& ex) {
+ BOOST_CHECK_EQUAL(ret.size(), 0U);
+ // one query to get NSs, then A and AAAA for each NS, 5th NS hits the limit
+ // limit is reduced to 5, because zone publishes many (20) NS
+ BOOST_CHECK_EQUAL(queriesCount, 11);
+ }
+}
+
/*
// cerr<<"asyncresolve called to ask "<<ip.toStringWithPort()<<" about "<<domain.toString()<<" / "<<QType(type).getName()<<" over "<<(doTCP ? "TCP" : "UDP")<<" (rd: "<<sendRDQuery<<", EDNS0 level: "<<EDNS0Level<<")"<<endl;
Index: pdns-recursor-4.1.12/syncres.cc
===================================================================
--- pdns-recursor-4.1.12.orig/syncres.cc
+++ pdns-recursor-4.1.12/syncres.cc
@@ -49,6 +49,7 @@ SyncRes::LogMode SyncRes::s_lm;
unsigned int SyncRes::s_maxnegttl;
unsigned int SyncRes::s_maxcachettl;
unsigned int SyncRes::s_maxqperq;
+unsigned int SyncRes::s_maxnsaddressqperq;
unsigned int SyncRes::s_maxtotusec;
unsigned int SyncRes::s_maxdepth;
unsigned int SyncRes::s_minimumTTL;
@@ -662,7 +663,7 @@ struct speedOrderCA
/** This function explicitly goes out for A or AAAA addresses
*/
-vector<ComboAddress> SyncRes::getAddrs(const DNSName &qname, unsigned int depth, set<GetBestNSAnswer>& beenthere, bool cacheOnly)
+vector<ComboAddress> SyncRes::getAddrs(const DNSName &qname, unsigned int depth, set<GetBestNSAnswer>& beenthere, bool cacheOnly, unsigned int& addressQueriesForNS)
{
typedef vector<DNSRecord> res_t;
res_t res;
@@ -674,6 +675,7 @@ vector<ComboAddress> SyncRes::getAddrs(c
bool oldCacheOnly = d_cacheonly;
bool oldRequireAuthData = d_requireAuthData;
bool oldValidationRequested = d_DNSSECValidationRequested;
+ const unsigned int startqueries = d_outqueries;
d_requireAuthData = false;
d_DNSSECValidationRequested = false;
d_cacheonly = cacheOnly;
@@ -723,6 +725,10 @@ vector<ComboAddress> SyncRes::getAddrs(c
}
}
+ if (ret.empty() && d_outqueries > startqueries) {
+ // We did 1 or more outgoing queries to resolve this NS name but returned empty handed
+ addressQueriesForNS++;
+ }
d_requireAuthData = oldRequireAuthData;
d_DNSSECValidationRequested = oldValidationRequested;
d_cacheonly = oldCacheOnly;
@@ -1429,13 +1435,13 @@ bool SyncRes::nameserverIPBlockedByRPZ(c
return false;
}
-vector<ComboAddress> SyncRes::retrieveAddressesForNS(const std::string& prefix, const DNSName& qname, vector<DNSName >::const_iterator& tns, const unsigned int depth, set<GetBestNSAnswer>& beenthere, const vector<DNSName >& rnameservers, NsSet& nameservers, bool& sendRDQuery, bool& pierceDontQuery, bool& flawedNSSet, bool cacheOnly)
+vector<ComboAddress> SyncRes::retrieveAddressesForNS(const std::string& prefix, const DNSName& qname, vector<DNSName >::const_iterator& tns, const unsigned int depth, set<GetBestNSAnswer>& beenthere, const vector<DNSName >& rnameservers, NsSet& nameservers, bool& sendRDQuery, bool& pierceDontQuery, bool& flawedNSSet, bool cacheOnly, unsigned int& retrieveAddressesForNS)
{
vector<ComboAddress> result;
if(!tns->empty()) {
LOG(prefix<<qname<<": Trying to resolve NS '"<<*tns<< "' ("<<1+tns-rnameservers.begin()<<"/"<<(unsigned int)rnameservers.size()<<")"<<endl);
- result = getAddrs(*tns, depth+2, beenthere, cacheOnly);
+ result = getAddrs(*tns, depth+2, beenthere, cacheOnly, retrieveAddressesForNS);
pierceDontQuery=false;
}
else {
@@ -2671,10 +2677,24 @@ int SyncRes::doResolveAt(NsSet &nameserv
LOG(endl);
+ unsigned int addressQueriesForNS = 0;
for(;;) { // we may get more specific nameservers
vector<DNSName > rnameservers = shuffleInSpeedOrder(nameservers, doLog() ? (prefix+qname.toString()+": ") : string() );
+ // We allow s_maxnsaddressqperq (default 10) queries with empty responses when resolving NS names.
+ // If a zone publishes many (more than s_maxnsaddressqperq) NS records, we allow less.
+ // This is to "punish" zones that publish many non-resolving NS names.
+ // We always allow 5 NS name resolving attempts with empty results.
+ unsigned int nsLimit = s_maxnsaddressqperq;
+ if (rnameservers.size() > nsLimit) {
+ int newLimit = static_cast<int>(nsLimit) - (rnameservers.size() - nsLimit);
+ nsLimit = std::max(5, newLimit);
+ }
+
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());
+ }
if(tns==rnameservers.cend()) {
LOG(prefix<<qname<<": Failed to resolve via any of the "<<(unsigned int)rnameservers.size()<<" offered NS at level '"<<auth<<"'"<<endl);
if(!auth.isRoot() && flawedNSSet) {
@@ -2726,7 +2746,7 @@ int SyncRes::doResolveAt(NsSet &nameserv
}
else {
/* if tns is empty, retrieveAddressesForNS() knows we have hardcoded servers (i.e. "forwards") */
- remoteIPs = retrieveAddressesForNS(prefix, qname, tns, depth, beenthere, rnameservers, nameservers, sendRDQuery, pierceDontQuery, flawedNSSet, cacheOnly);
+ remoteIPs = retrieveAddressesForNS(prefix, qname, tns, depth, beenthere, rnameservers, nameservers, sendRDQuery, pierceDontQuery, flawedNSSet, cacheOnly, addressQueriesForNS);
if(remoteIPs.empty()) {
LOG(prefix<<qname<<": Failed to get IP for NS "<<*tns<<", trying next if available"<<endl);
Index: pdns-recursor-4.1.12/syncres.hh
===================================================================
--- pdns-recursor-4.1.12.orig/syncres.hh
+++ pdns-recursor-4.1.12/syncres.hh
@@ -677,6 +677,7 @@ public:
static string s_serverID;
static unsigned int s_minimumTTL;
static unsigned int s_maxqperq;
+ static unsigned int s_maxnsaddressqperq;
static unsigned int s_maxtotusec;
static unsigned int s_maxdepth;
static unsigned int s_maxnegttl;
@@ -746,13 +747,13 @@ private:
inline vector<DNSName> shuffleInSpeedOrder(NsSet &nameservers, const string &prefix);
bool moreSpecificThan(const DNSName& a, const DNSName &b) const;
- vector<ComboAddress> getAddrs(const DNSName &qname, unsigned int depth, set<GetBestNSAnswer>& beenthere, bool cacheOnly);
+ vector<ComboAddress> getAddrs(const DNSName &qname, unsigned int depth, set<GetBestNSAnswer>& beenthere, bool cacheOnly, unsigned int& addressQueriesForNS);
bool nameserversBlockedByRPZ(const DNSFilterEngine& dfe, const NsSet& nameservers);
bool nameserverIPBlockedByRPZ(const DNSFilterEngine& dfe, const ComboAddress&);
bool throttledOrBlocked(const std::string& prefix, const ComboAddress& remoteIP, const DNSName& qname, const QType& qtype, bool pierceDontQuery);
- vector<ComboAddress> retrieveAddressesForNS(const std::string& prefix, const DNSName& qname, vector<DNSName >::const_iterator& tns, const unsigned int depth, set<GetBestNSAnswer>& beenthere, const vector<DNSName >& rnameservers, NsSet& nameservers, bool& sendRDQuery, bool& pierceDontQuery, bool& flawedNSSet, bool cacheOnly);
+ vector<ComboAddress> retrieveAddressesForNS(const std::string& prefix, const DNSName& qname, vector<DNSName >::const_iterator& tns, const unsigned int depth, set<GetBestNSAnswer>& beenthere, const vector<DNSName >& rnameservers, NsSet& nameservers, bool& sendRDQuery, bool& pierceDontQuery, bool& flawedNSSet, bool cacheOnly, unsigned int& addressQueriesForNS);
RCode::rcodes_ updateCacheFromRecords(unsigned int depth, LWResult& lwr, const DNSName& qname, const QType& qtype, const DNSName& auth, bool wasForwarded, const boost::optional<Netmask>, vState& state, bool& needWildcardProof, unsigned int& wildcardLabelsCount, bool sendRDQuery);
bool processRecords(const std::string& prefix, const DNSName& qname, const QType& qtype, const DNSName& auth, LWResult& lwr, const bool sendRDQuery, vector<DNSRecord>& ret, set<DNSName>& nsset, DNSName& newtarget, DNSName& newauth, bool& realreferral, bool& negindic, vState& state, const bool needWildcardProof, const unsigned int wildcardLabelsCount);