From: Otto Moerbeek Date: Mon, 7 Feb 2022 14:56:03 +0000 (+0100) Subject: Add recursion, the records found can be subject to expansion X-Git-Tag: rec-4.7.0-alpha1~8^2~6 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=76e368a301da7f59a7d1d1a847849d4cf3a466e7;p=thirdparty%2Fpdns.git Add recursion, the records found can be subject to expansion --- diff --git a/pdns/syncres.cc b/pdns/syncres.cc index 0ea779c416..ee9e91732f 100644 --- a/pdns/syncres.cc +++ b/pdns/syncres.cc @@ -150,25 +150,24 @@ static const std::map, SyncRes::AddtionalMode>> {QType::NAPTR, {{QType::A, QType::AAAA, QType::SRV}, SyncRes::AddtionalMode::ResolveImmediately}} }; -void SyncRes::getAdditionals(const DNSName& qname, QType qtype, AddtionalMode mode, std::set& additionals, unsigned int depth) +void SyncRes::resolveAdditionals(const DNSName& qname, QType qtype, AddtionalMode mode, std::vector& additionals, unsigned int depth) { vector addRecords; vState state = vState::Indeterminate; switch (mode) { case AddtionalMode::ResolveImmediately: { - set lbeenthere; - int res = doResolve(qname, qtype, addRecords, depth, lbeenthere, state); + set beenthere; + int res = doResolve(qname, qtype, addRecords, depth, beenthere, state); if (res != 0) { return; } if (vStateIsBogus(state)) { return; } - for (auto rec : addRecords) { + for (const auto& rec : addRecords) { if (rec.d_place == DNSResourceRecord::ANSWER) { - rec.d_place = DNSResourceRecord::ADDITIONAL; - additionals.insert(rec); + additionals.push_back(rec); } } break; @@ -182,23 +181,100 @@ void SyncRes::getAdditionals(const DNSName& qname, QType qtype, AddtionalMode mo if (vStateIsBogus(state)) { return; } - for (auto rec : addRecords) { + for (auto& rec : addRecords) { if (rec.d_place == DNSResourceRecord::ANSWER) { - rec.d_place = DNSResourceRecord::ADDITIONAL; rec.d_ttl -= d_now.tv_sec ; - additionals.insert(rec); + additionals.push_back(rec); } } break; } case AddtionalMode::ResolveDeferred: // Not yet implemented + // Look in cache for authoritative answer, if available return it + // If not, look in nergache and submit if not there as well. The logic should be the same as + // #11294, which is in review atm. break; case AddtionalMode::Ignore: break; } } +// The main (recursive) function to add additonals +// qtype: the original query type to expand +// start: records to start from +// This function uses to state sets to avoid infinite recursion +// depth is the main recursion depth +// additionaldepth is the depth for addAdditionals itself +void SyncRes::addAdditionals(QType qtype, const vector&start, vector&additionals, std::set>& uniqueCalls, std::set>& uniqueResults, unsigned int depth, unsigned additionaldepth) +{ + if (additionaldepth >= 5 || start.empty()) { + return; + } + const auto it = additionalTypes.find(qtype); + if (it == additionalTypes.end()) { + return; + } + std::unordered_set addnames; + for (const auto& rec : start) { + if (rec.d_place == DNSResourceRecord::ANSWER) { + // currently, this funtion only knows about names, we could also take the target types that are dependent on + // reord contents into account + // e.g. for NAPTR records, go only for SRV for flag value "s", or A/AAAA for flag value "a" + allowAdditionalEntry(addnames, rec); + } + } + + auto mode = it->second.second; + for (const auto& targettype : it->second.first) { + for (const auto& addname : addnames) { + if ((targettype == QType::A && !s_doIPv4) || (targettype == QType::AAAA && !s_doIPv6)) { + continue; + } + std::vector records; + if (uniqueCalls.count(std::pair(addname, targettype)) == 0) { + uniqueCalls.emplace(addname, targettype); + resolveAdditionals(addname, targettype, mode, records, depth); + } + if (!records.empty()) { + for (auto r = records.begin(); r != records.end(); ) { + if (uniqueResults.count(std::pair(r->d_name, QType(r->d_type))) > 0) { + // A bit expensive for vectors, but they are small + r = records.erase(r); + } else { + ++r; + } + } + for (const auto& r : records) { + additionals.push_back(r); + uniqueResults.emplace(r.d_name, r.d_type); + } + addAdditionals(targettype, records, additionals, uniqueCalls, uniqueResults, depth, additionaldepth + 1); + } + } + } +} + +// The entry point for other code +void SyncRes::addAdditionals(QType qtype, vector&ret, unsigned int depth) +{ + // The additional records of interest + std::vector additionals; + + // We only call resolve for a specific name/type combo once + std::set> uniqueCalls; + + // Collect multiple name/qtype from a single resolve but do not add a new set from new resolve calls + std::set> uniqueResults; + + addAdditionals(qtype, ret, additionals, uniqueCalls, uniqueResults, depth, 0); + + for (auto& rec : additionals) { + rec.d_place = DNSResourceRecord::ADDITIONAL; + ret.push_back(rec); + } +} + /** everything begins here - this is the entry point just after receiving a packet */ int SyncRes::beginResolve(const DNSName &qname, const QType qtype, QClass qclass, vector&ret, unsigned int depth) { @@ -248,28 +324,8 @@ int SyncRes::beginResolve(const DNSName &qname, const QType qtype, QClass qclass } } - const auto it = additionalTypes.find(qtype); - if (it != additionalTypes.end()) { - std::unordered_set addnames; - for (const auto& rec : ret) { - if (rec.d_place == DNSResourceRecord::ANSWER) { - allowAdditionalEntry(addnames, rec); - } - } - std::set additionals; - auto mode = it->second.second; - for (const auto& targettype : it->second.first) { - for (const auto& addname : addnames) { - vector addRecords; - if ((targettype == QType::A && !s_doIPv4) || (targettype == QType::AAAA && !s_doIPv6)) { - continue; - } - getAdditionals(addname, targettype, mode, additionals, depth); - } - } - for (const auto& rec : additionals) { - ret.push_back(rec); - } + if (qclass == QClass::IN && additionalTypes.find(qtype) != additionalTypes.end()) { + addAdditionals(qtype, ret, depth); } d_eventTrace.add(RecEventTrace::SyncRes, res, false); return res; diff --git a/pdns/syncres.hh b/pdns/syncres.hh index 1cb0b9d5f8..89cd86ccc9 100644 --- a/pdns/syncres.hh +++ b/pdns/syncres.hh @@ -268,7 +268,15 @@ public: typedef std::function& srcmask, boost::optional context, LWResult *lwr, bool* chained)> asyncresolve_t; enum class HardenNXD { No, DNSSEC, Yes }; - + + enum class AddtionalMode : uint8_t { + Ignore, + CacheOnly, + CacheOnlyRequireAuth, + ResolveImmediately, + ResolveDeferred + }; + //! This represents a number of decaying Ewmas, used to store performance per nameserver-name. /** Modelled to work mostly like the underlying DecayingEwma */ struct DecayingEwmaCollection @@ -598,15 +606,6 @@ public: explicit SyncRes(const struct timeval& now); - enum class AddtionalMode : uint8_t { - Ignore, - CacheOnly, - CacheOnlyRequireAuth, - ResolveImmediately, - ResolveDeferred - }; - - void getAdditionals(const DNSName& qname, QType qtype, AddtionalMode, std::set& additionals, unsigned int depth); int beginResolve(const DNSName &qname, QType qtype, QClass qclass, vector&ret, unsigned int depth = 0); void setId(int id) @@ -847,6 +846,10 @@ private: typedef std::map zonesStates_t; enum StopAtDelegation { DontStop, Stop, Stopped }; + void resolveAdditionals(const DNSName& qname, QType qtype, AddtionalMode, std::vector& additionals, unsigned int depth); + void addAdditionals(QType qtype, const vector&start, vector&addditionals, std::set>& uniqueCalls, std::set>& uniqueResults, unsigned int depth, unsigned int adddepth); + void addAdditionals(QType qtype, vector&ret, unsigned int depth); + bool doDoTtoAuth(const DNSName& ns) const; int doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, const DNSName &qname, QType qtype, vector&ret, unsigned int depth, set&beenthere, vState& state, StopAtDelegation* stopAtDelegation);