From: Otto Moerbeek Date: Tue, 16 Apr 2019 13:25:46 +0000 (+0200) Subject: Qname minimizaton. X-Git-Tag: dnsdist-1.4.0-rc1~115^2~3 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=116d12880e9fe32980ae9c3964437adb9e971f8f;p=pdns Qname minimizaton. --- diff --git a/pdns/pdns_recursor.cc b/pdns/pdns_recursor.cc index 491ae26bd..6710556f0 100644 --- a/pdns/pdns_recursor.cc +++ b/pdns/pdns_recursor.cc @@ -3846,6 +3846,8 @@ static int serviceMain(int argc, char*argv[]) SyncRes::s_ecsipv6cachelimit = ::arg().asNum("ecs-ipv6-cache-bits"); SyncRes::s_ecscachelimitttl = ::arg().asNum("ecs-cache-limit-ttl"); + SyncRes::s_qnameminimization = ::arg().mustDo("qname-minimization"); + if (!::arg().isEmpty("ecs-scope-zero-address")) { ComboAddress scopeZero(::arg()["ecs-scope-zero-address"]); SyncRes::setECSScopeZeroAddress(Netmask(scopeZero, scopeZero.isIPv4() ? 32 : 128)); @@ -4544,6 +4546,7 @@ int main(int argc, char **argv) ::arg().set("rng", "Specify random number generator to use. Valid values are auto,sodium,openssl,getrandom,arc4random,urandom.")="auto"; ::arg().set("public-suffix-list-file", "Path to the Public Suffix List file, if any")=""; ::arg().set("distribution-load-factor", "The load factor used when PowerDNS is distributing queries to worker threads")="0.0"; + ::arg().setSwitch("qname-minimization", "Use Query Name Minimization")="no"; #ifdef NOD_ENABLED ::arg().set("new-domain-tracking", "Track newly observed domains (i.e. never seen before).")="no"; ::arg().set("new-domain-log", "Log newly observed domains.")="yes"; diff --git a/pdns/recursordist/docs/settings.rst b/pdns/recursordist/docs/settings.rst index a867200c4..45cc199e3 100644 --- a/pdns/recursordist/docs/settings.rst +++ b/pdns/recursordist/docs/settings.rst @@ -1164,6 +1164,17 @@ Whether to compute the latency of responses in protobuf messages using the times Path to the Public Suffix List file, if any. If set, PowerDNS will try to load the Public Suffix List from this file instead of using the built-in list. The PSL is used to group the queries by relevant domain names when displaying the top queries. +.. _setting-qname-minimization: + +``qname-minimization`` +---------------------- +.. versionadded:: 4.3.0 +- Boolean +- Default: no + +Enable Query Name Minimization. This is a experimental feature, implementing a relaxed form of Query Name Mimimization as +described in RFC 7816. + .. _setting-query-local-address: ``query-local-address`` diff --git a/pdns/recursordist/test-syncres_cc.cc b/pdns/recursordist/test-syncres_cc.cc index aabfe33fe..b95b56e0f 100644 --- a/pdns/recursordist/test-syncres_cc.cc +++ b/pdns/recursordist/test-syncres_cc.cc @@ -165,7 +165,7 @@ void initSR(bool debug) ::arg().set("version-string", "string reported on version.pdns or version.bind")="PowerDNS Unit Tests"; ::arg().set("rng")="auto"; ::arg().set("entropy-source")="/dev/urandom"; - ::arg().setSwitch("qname-minimisation", "Use Query Name Minimisation") = "no"; + ::arg().setSwitch("qname-minimization", "Use Query Name Minimization") = "no"; } void initSR(std::unique_ptr& sr, bool dnssec, bool debug, time_t fakeNow) @@ -199,11 +199,6 @@ void setDNSSECValidation(std::unique_ptr& sr, const DNSSECMode& mode) g_dnssecmode = mode; } -void setDoQNameMinimisation(void) -{ - ::arg().setSwitch("qname-minimisation", "") = "yes"; -} - void setLWResult(LWResult* res, int rcode, bool aa, bool tc, bool edns, bool validpacket) { res->d_rcode = rcode; @@ -461,7 +456,7 @@ int genericDSAndDNSKEYHandler(LWResult* res, const DNSName& domain, DNSName auth return 0; } -int basicRecordsForQnameMinimisation(LWResult* res, const DNSName& domain, int type) { +int basicRecordsForQnameMinimization(LWResult* res, const DNSName& domain, int type) { if (domain == DNSName(".") && type == QType::A) { setLWResult(res, 0, true); addRecordToLW(res, DNSName("."), QType::SOA, "a.root-servers.net. nstld.verisign-grs.com. 2019042400 1800 900 604800 86400", DNSResourceRecord::AUTHORITY); diff --git a/pdns/recursordist/test-syncres_cc.hh b/pdns/recursordist/test-syncres_cc.hh index d568a43c5..93d7e6f43 100644 --- a/pdns/recursordist/test-syncres_cc.hh +++ b/pdns/recursordist/test-syncres_cc.hh @@ -41,8 +41,6 @@ void initSR(std::unique_ptr& sr, bool dnssec=false, bool debug=false, t void setDNSSECValidation(std::unique_ptr& sr, const DNSSECMode& mode); -void setDoQNameMinimisation(void); - void setLWResult(LWResult* res, int rcode, bool aa=false, bool tc=false, bool edns=false, bool validpacket=true); void addRecordToLW(LWResult* res, const DNSName& name, uint16_t type, const std::string& content, DNSResourceRecord::Place place=DNSResourceRecord::ANSWER, uint32_t ttl=60); @@ -77,5 +75,5 @@ void generateKeyMaterial(const DNSName& name, unsigned int algo, uint8_t digest, int genericDSAndDNSKEYHandler(LWResult* res, const DNSName& domain, DNSName auth, int type, const testkeysset_t& keys, bool proveCut=true); -int basicRecordsForQnameMinimisation(LWResult* res, const DNSName& domain, int type); +int basicRecordsForQnameMinimization(LWResult* res, const DNSName& domain, int type); diff --git a/pdns/recursordist/test-syncres_cc3.cc b/pdns/recursordist/test-syncres_cc3.cc index 361016ce3..9a5ea155e 100644 --- a/pdns/recursordist/test-syncres_cc3.cc +++ b/pdns/recursordist/test-syncres_cc3.cc @@ -112,7 +112,7 @@ static void test_no_data_f(bool qmin) { std::unique_ptr sr; initSR(sr); if (qmin) - setDoQNameMinimisation(); + sr->setQNameMinimization(); primeHints(); @@ -138,8 +138,6 @@ BOOST_AUTO_TEST_CASE(test_no_data) { } BOOST_AUTO_TEST_CASE(test_no_data_qmin) { - // DISABLED UNTIL QNAME MINIMIZATION IS THERE - return; test_no_data_f(true); } diff --git a/pdns/syncres.cc b/pdns/syncres.cc index 1c5daf550..5269e2721 100644 --- a/pdns/syncres.cc +++ b/pdns/syncres.cc @@ -87,6 +87,7 @@ bool SyncRes::s_doIPv6; bool SyncRes::s_nopacketcache; bool SyncRes::s_rootNXTrust; bool SyncRes::s_noEDNS; +bool SyncRes::s_qnameminimization; #define LOG(x) if(d_lm == Log) { g_log <&ret, unsigned int depth, set& beenthere, vState& state) { + + if (!getQNameMinimization()) { + return doResolveNoQNameMinimization(qname, qtype, ret, depth, beenthere, state); + } + + // The qname minimization algorithm is a simplified version of the one in RFC 7816 (bis). + // It could be simplified because the cache maintenance (both positive and negative) + // is already done by doResolveNoQNameMinimization(). + // + // Sketch of algorithm: + // Check cache + // If result found: done + // Otherwise determine closes ancestor from cache data + // Repeat querying A, adding more labels of the original qname + // If we get a delegation continue at ancestor determination + // Until we have the full name. + // + // The algorithm starts with adding a single label per iteration, and + // moves to three labels per iteration after three iterations. + + DNSName child; + string prefix = d_prefix; + prefix.append(depth, ' '); + prefix.append(string("QM ") + qname.toString() + "|" + qtype.getName()); + + QLOG("doResolve"); + + // Look in cache only + vector retq; + bool old = setCacheOnly(true); + bool fromCache = false; + int res = doResolveNoQNameMinimization(qname, qtype, retq, depth + 1, beenthere, state, &fromCache); + setCacheOnly(old); + if (fromCache) { + QLOG("Step0 Found in cache"); + ret.insert(ret.end(), retq.begin(), retq.end()); + return res; + } + QLOG("Step0 Not cached"); + + const unsigned int qnamelen = qname.countLabels(); + + for (unsigned int i = 0; i <= qnamelen; ) { + + // Step 1 + vector bestns; + // the two retries allow getBestNSFromCache&co to reprime the root + // hints, in case they ever go missing + for (int tries = 0; tries < 2 && bestns.empty(); ++tries) { + bool flawedNSSet = false; + set beenthereIgnored; + getBestNSFromCache(qname, qtype, bestns, &flawedNSSet, depth + 1, beenthereIgnored); + } + DNSName ancestor; + if (bestns.size() > 0) { + ancestor = bestns[0].d_name; + QLOG("Step1 Ancestor from cache is " << ancestor.toString()); + } else { + QLOG("Step1 No ancestor found return ServFail"); + return RCode::ServFail; + } + + child = ancestor; + + unsigned int targetlen = std::min(child.countLabels() + (i > 3 ? 3 : 1), qnamelen); + + for (; i <= qnamelen; i++) { + // Step 2 + while (child.countLabels() < targetlen) { + child.prependRawLabel(qname.getRawLabel(qnamelen - child.countLabels() - 1)); + } + targetlen += i > 3 ? 3 : 1; + targetlen = std::min(targetlen, qnamelen); + + QLOG("Step2 New child"); + + // Step 3 resolve + if (child == qname) { + QLOG("Step3 Going to do final resolve"); + res = doResolveNoQNameMinimization(qname, qtype, ret, depth + 1, beenthere, state); + QLOG("Step3 Final resolve: " << RCode::to_s(res) << "/" << ret.size() << endl); + return res; + } + + // Step 6 + QLOG("Step4 Resolve A for child"); + retq.resize(0); + StopAtDelegation stopAtDelegation = Stop; + res = doResolveNoQNameMinimization(child, QType::A, retq, depth + 1, beenthere, state, NULL, &stopAtDelegation); + QLOG("Step4 Resolve A result is " << RCode::to_s(res) << "/" << retq.size() << "/" << stopAtDelegation); + if (stopAtDelegation == Stopped) { + QLOG("Delegation seen, continue at step 1"); + break; + } + if (res != RCode::NoError) { + // Case 5: unexpected answer + QLOG("Step5: other rcode, last effort final resolve"); + setQNameMinimization(false); + res = doResolveNoQNameMinimization(qname, qtype, ret, depth + 1, beenthere, state); + QLOG("Step5 End resolve: " << RCode::to_s(res) << "/" << ret.size() << endl); + return res; + } + } + } + + // Should not be reached + QLOG("Max iterations reached, return ServFail"); + return RCode::ServFail; +} + /*! This function will check the cache and go out to the internet if the answer is not in cache * * \param qname The name we need an answer for @@ -549,7 +663,7 @@ int SyncRes::asyncresolveWrapper(const ComboAddress& ip, bool ednsMANDATORY, con * \param beenthere * \return DNS RCODE or -1 (Error) or -2 (RPZ hit) */ -int SyncRes::doResolve(const DNSName &qname, const QType &qtype, vector&ret, unsigned int depth, set& beenthere, vState& state) +int SyncRes::doResolveNoQNameMinimization(const DNSName &qname, const QType &qtype, vector&ret, unsigned int depth, set& beenthere, vState& state, bool *fromCache, StopAtDelegation *stopAtDelegation) { string prefix; if(doLog()) { @@ -577,6 +691,8 @@ int SyncRes::doResolve(const DNSName &qname, const QType &qtype, vectorsecond.isAuth()) { ret.clear(); d_wasOutOfBand = doOOBResolve(qname, qtype, ret, depth, res); + if (fromCache) + *fromCache = d_wasOutOfBand; return res; } else { @@ -590,7 +706,9 @@ int SyncRes::doResolve(const DNSName &qname, const QType &qtype, vector SyncRes::getAddrs(const DNSName &qname, unsigned int depth, typedef vector ret_t; ret_t ret; - bool oldCacheOnly = d_cacheonly; + bool oldCacheOnly = setCacheOnly(cacheOnly); bool oldRequireAuthData = d_requireAuthData; bool oldValidationRequested = d_DNSSECValidationRequested; d_requireAuthData = false; d_DNSSECValidationRequested = false; - d_cacheonly = cacheOnly; vState newState = Indeterminate; res_t resv4; @@ -745,7 +865,7 @@ vector SyncRes::getAddrs(const DNSName &qname, unsigned int depth, d_requireAuthData = oldRequireAuthData; d_DNSSECValidationRequested = oldValidationRequested; - d_cacheonly = oldCacheOnly; + setCacheOnly(oldCacheOnly); /* we need to remove from the nsSpeeds collection the existing IPs for this nameserver that are no longer in the set, even if there @@ -3140,7 +3260,7 @@ bool SyncRes::processAnswer(unsigned int depth, LWResult& lwr, const DNSName& qn */ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, const DNSName &qname, const QType &qtype, vector&ret, - unsigned int depth, set&beenthere, vState& state) + unsigned int depth, set&beenthere, vState& state, StopAtDelegation* stopAtDelegation) { auto luaconfsLocal = g_luaconfs.getLocal(); string prefix; @@ -3207,6 +3327,10 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con return rcode; } if (gotNewServers) { + if (stopAtDelegation && *stopAtDelegation == Stop) { + *stopAtDelegation = Stopped; + return rcode; + } break; } } @@ -3272,6 +3396,10 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con return rcode; } if (gotNewServers) { + if (stopAtDelegation && *stopAtDelegation == Stop) { + *stopAtDelegation = Stopped; + return rcode; + } break; } /* was lame */ diff --git a/pdns/syncres.hh b/pdns/syncres.hh index 248e1bc08..d0edf346d 100644 --- a/pdns/syncres.hh +++ b/pdns/syncres.hh @@ -598,9 +598,16 @@ public: return d_lm != LogNone; } - void setCacheOnly(bool state=true) + bool setCacheOnly(bool state = true) { - d_cacheonly=state; + bool old = d_cacheonly; + d_cacheonly = state; + return old; + } + + void setQNameMinimization(bool state=true) + { + d_qNameMinimization=state; } void setDoEDNS0(bool state=true) @@ -643,6 +650,11 @@ public: return d_trace.str(); } + bool getQNameMinimization() const + { + return d_qNameMinimization; + } + void setLuaEngine(shared_ptr pdl) { d_pdl = pdl; @@ -740,6 +752,7 @@ public: static bool s_noEDNS; static bool s_rootNXTrust; static bool s_nopacketcache; + static bool s_qnameminimization; std::unordered_map d_discardedPolicies; DNSFilterEngine::Policy d_appliedPolicy; @@ -777,13 +790,15 @@ private: }; typedef std::map zonesStates_t; + enum StopAtDelegation { DontStop, Stop, Stopped }; int doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, const DNSName &qname, const QType &qtype, vector&ret, - unsigned int depth, set&beenthere, vState& state); + 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 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); + int doResolveNoQNameMinimization(const DNSName &qname, const QType &qtype, vector&ret, unsigned int depth, set& beenthere, vState& state, bool* fromCache = NULL, StopAtDelegation* stopAtDelegation = NULL); bool doOOBResolve(const AuthDomain& domain, const DNSName &qname, const QType &qtype, vector&ret, int& res); bool doOOBResolve(const DNSName &qname, const QType &qtype, vector&ret, unsigned int depth, int &res); domainmap_t::const_iterator getBestAuthZone(DNSName* qname) const; @@ -862,6 +877,7 @@ private: bool d_wantsRPZ{true}; bool d_wasOutOfBand{false}; bool d_wasVariable{false}; + bool d_qNameMinimization{false}; LogMode d_lm; };