From 658d9e407dfd3ac4e6267e71c306559ded261761 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Tue, 10 Jan 2017 13:12:17 +0100 Subject: [PATCH] rec: Add `max-recursion-depth` to limit the number of internal recursion Default to 40, was unlimited. (cherry picked from commit 7c3398aabe2e9dd8c5c3e8b3572455abfa3037be) --- docs/markdown/recursor/settings.md | 10 +++++++++- pdns/pdns_recursor.cc | 2 ++ pdns/syncres.cc | 20 ++++++++++++-------- pdns/syncres.hh | 17 +++++++++-------- 4 files changed, 32 insertions(+), 17 deletions(-) diff --git a/docs/markdown/recursor/settings.md b/docs/markdown/recursor/settings.md index 73e09ff30..9e67c4efa 100644 --- a/docs/markdown/recursor/settings.md +++ b/docs/markdown/recursor/settings.md @@ -599,6 +599,14 @@ In practice, caches can become saturated with hundreds of thousands of hosts which are tried only once. This setting, which defaults to 3600 seconds, puts a maximum on the amount of time negative entries are cached. +## `max-recursion-depth` +* Integer +* Default: 40 (since 4.1.0), unlimited (before 4.1.0) + +Total maximum number of internal recursion calls the server may use to answer +a single query. 0 means unlimited. The value of `stack-size` should be increased +together with this one to prevent the stack from overflowing. + ## `max-tcp-clients` * Integer * Default: 128 @@ -616,7 +624,7 @@ Maximum number of simultaneous incoming TCP connections allowed per client * Integer * Default: 7000 -Total maximum number of miliseconds of wallclock time the servermay use to answer +Total maximum number of miliseconds of wallclock time the server may use to answer a single query. ## `minimum-ttl-override` diff --git a/pdns/pdns_recursor.cc b/pdns/pdns_recursor.cc index 306b0955d..7cbe7cd20 100644 --- a/pdns/pdns_recursor.cc +++ b/pdns/pdns_recursor.cc @@ -2686,6 +2686,7 @@ int serviceMain(int argc, char*argv[]) SyncRes::s_serverID=::arg()["server-id"]; SyncRes::s_maxqperq=::arg().asNum("max-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"); if(SyncRes::s_serverID.empty()) { char tmp[128]; @@ -3042,6 +3043,7 @@ int main(int argc, char **argv) ::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-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("include-dir","Include *.conf files from this directory")=""; ::arg().set("security-poll-suffix","Domain name from which to query security update notifications")="secpoll.powerdns.com."; diff --git a/pdns/syncres.cc b/pdns/syncres.cc index 2db2e69ef..f121cfac2 100644 --- a/pdns/syncres.cc +++ b/pdns/syncres.cc @@ -74,6 +74,7 @@ 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; @@ -182,7 +183,7 @@ int SyncRes::beginResolve(const DNSName &qname, const QType &qtype, uint16_t qcl } //! This is the 'out of band resolver', in other words, the authoritative server -bool SyncRes::doOOBResolve(const DNSName &qname, const QType &qtype, vector&ret, int depth, int& res) +bool SyncRes::doOOBResolve(const DNSName &qname, const QType &qtype, vector&ret, unsigned int depth, int& res) { string prefix; if(doLog()) { @@ -402,7 +403,7 @@ int SyncRes::asyncresolveWrapper(const ComboAddress& ip, bool ednsMANDATORY, con return ret; } -int SyncRes::doResolve(const DNSName &qname, const QType &qtype, vector&ret, int depth, set& beenthere) +int SyncRes::doResolve(const DNSName &qname, const QType &qtype, vector&ret, unsigned int depth, set& beenthere) { string prefix; if(doLog()) { @@ -412,6 +413,9 @@ int SyncRes::doResolve(const DNSName &qname, const QType &qtype, vector s_maxdepth) + throw ImmediateServFailException("More than "+std::to_string(s_maxdepth)+" (max-recursion-depth) levels of recursion needed while resolving "+qname.toLogString()); + int res=0; if(!(d_nocache && qtype.getCode()==QType::NS && qname.isRoot())) { if(d_cacheonly) { // very limited OOB support @@ -489,7 +493,7 @@ static bool ipv6First(const ComboAddress& a, const ComboAddress& b) /** This function explicitly goes out for A or AAAA addresses */ -vector SyncRes::getAddrs(const DNSName &qname, int depth, set& beenthere) +vector SyncRes::getAddrs(const DNSName &qname, unsigned int depth, set& beenthere) { typedef vector res_t; res_t res; @@ -564,7 +568,7 @@ vector SyncRes::getAddrs(const DNSName &qname, int depth, set& bestns, bool* flawedNSSet, int depth, set& beenthere) +void SyncRes::getBestNSFromCache(const DNSName &qname, const QType& qtype, vector& bestns, bool* flawedNSSet, unsigned int depth, set& beenthere) { string prefix; DNSName subdomain(qname); @@ -652,7 +656,7 @@ SyncRes::domainmap_t::const_iterator SyncRes::getBestAuthZone(DNSName* qname) } /** doesn't actually do the work, leaves that to getBestNSFromCache */ -DNSName SyncRes::getBestNSNamesFromCache(const DNSName &qname, const QType& qtype, NsSet& nsset, bool* flawedNSSet, int depth, set&beenthere) +DNSName SyncRes::getBestNSNamesFromCache(const DNSName &qname, const QType& qtype, NsSet& nsset, bool* flawedNSSet, unsigned int depth, set&beenthere) { DNSName subdomain(qname); DNSName authdomain(qname); @@ -683,7 +687,7 @@ DNSName SyncRes::getBestNSNamesFromCache(const DNSName &qname, const QType& qtyp return subdomain; } -bool SyncRes::doCNAMECacheCheck(const DNSName &qname, const QType &qtype, vector& ret, int depth, int &res) +bool SyncRes::doCNAMECacheCheck(const DNSName &qname, const QType &qtype, vector& ret, unsigned int depth, int &res) { string prefix; if(doLog()) { @@ -742,7 +746,7 @@ static const DNSName getLastLabel(const DNSName& qname) } -bool SyncRes::doCacheCheck(const DNSName &qname, const QType &qtype, vector&ret, int depth, int &res) +bool SyncRes::doCacheCheck(const DNSName &qname, const QType &qtype, vector&ret, unsigned int depth, int &res) { bool giveNegative=false; @@ -959,7 +963,7 @@ static void addNXNSECS(vector&ret, const vector& records) */ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, const DNSName &qname, const QType &qtype, vector&ret, - int depth, set&beenthere) + unsigned int depth, set&beenthere) { string prefix; if(doLog()) { diff --git a/pdns/syncres.hh b/pdns/syncres.hh index e8520a99f..a1151b116 100644 --- a/pdns/syncres.hh +++ b/pdns/syncres.hh @@ -363,6 +363,7 @@ public: static bool s_doIPv6; static unsigned int s_maxqperq; static unsigned int s_maxtotusec; + static unsigned int s_maxdepth; std::unordered_map d_discardedPolicies; DNSFilterEngine::Policy d_appliedPolicy; unsigned int d_outqueries; @@ -508,18 +509,18 @@ public: private: struct GetBestNSAnswer; int doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, const DNSName &qname, const QType &qtype, vector&ret, - int depth, set&beenthere); - int doResolve(const DNSName &qname, const QType &qtype, vector&ret, int depth, set& beenthere); - bool doOOBResolve(const DNSName &qname, const QType &qtype, vector&ret, int depth, int &res); + unsigned int depth, set&beenthere); + int doResolve(const DNSName &qname, const QType &qtype, vector&ret, unsigned int depth, set& beenthere); + bool doOOBResolve(const DNSName &qname, const QType &qtype, vector&ret, unsigned int depth, int &res); domainmap_t::const_iterator getBestAuthZone(DNSName* qname); - bool doCNAMECacheCheck(const DNSName &qname, const QType &qtype, vector&ret, int depth, int &res); - bool doCacheCheck(const DNSName &qname, const QType &qtype, vector&ret, int depth, int &res); - void getBestNSFromCache(const DNSName &qname, const QType &qtype, vector&bestns, bool* flawedNSSet, int depth, set& beenthere); - DNSName getBestNSNamesFromCache(const DNSName &qname, const QType &qtype, NsSet& nsset, bool* flawedNSSet, int depth, set&beenthere); + bool doCNAMECacheCheck(const DNSName &qname, const QType &qtype, vector&ret, unsigned int depth, int &res); + bool doCacheCheck(const DNSName &qname, const QType &qtype, vector&ret, unsigned int depth, int &res); + void getBestNSFromCache(const DNSName &qname, const QType &qtype, vector&bestns, bool* flawedNSSet, unsigned int depth, set& beenthere); + DNSName getBestNSNamesFromCache(const DNSName &qname, const QType &qtype, NsSet& nsset, bool* flawedNSSet, unsigned int depth, set&beenthere); inline vector shuffleInSpeedOrder(NsSet &nameservers, const string &prefix); bool moreSpecificThan(const DNSName& a, const DNSName &b); - vector getAddrs(const DNSName &qname, int depth, set& beenthere); + vector getAddrs(const DNSName &qname, unsigned int depth, set& beenthere); private: ostringstream d_trace; shared_ptr d_pdl; -- 2.40.0