]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Merge pull request #11492 from omoerbeek/rec-add-deferred
authorOtto Moerbeek <otto.moerbeek@open-xchange.com>
Fri, 8 Apr 2022 10:01:22 +0000 (12:01 +0200)
committerGitHub <noreply@github.com>
Fri, 8 Apr 2022 10:01:22 +0000 (12:01 +0200)
Rec: add deferred mode for additional records

pdns/recursordist/docs/lua-config/additionals.rst
pdns/syncres.cc
pdns/syncres.hh

index af97adf535fb6a1cbbd5431b5100e748c8251ba4..7d1395ce01f8e1b3b0c4e317f9c9577207d0ee39 100644 (file)
@@ -41,21 +41,32 @@ If needed, the Recursor will do an active resolve to retrieve these records.
 The modes available are:
 
 ``pdns.AdditionalMode.Ignore``
-  Do not do any additional processing for this qtype. This is equivalent to not calling :func:`addAllowedAdditionalQType` for the qtype.
+  Do not do any additional processing for this qtype.
+  This is equivalent to not calling :func:`addAllowedAdditionalQType` for the qtype.
 ``pdns.AdditionalMode.CacheOnly``
-  Look in the record cache for available records. Allow non-authoritative (learned from additional sections received from authoritative servers) records to be added.
+  Look in the record cache for available records.
+  Allow non-authoritative (learned from additional sections received from authoritative servers) records to be added.
 ``pdns.AdditionalMode.CacheOnlyRequireAuth``
-  Look in the record cache for available records. Only authoritative records will be added. These are records received from the nameservers for the specific domain.
+  Look in the record cache for available records.
+  Only authoritative records will be added. These are records received from the nameservers for the specific domain.
 ``pdns.AdditionalMode.ResolveImmediately``
-  Add records from the record cache (including DNSSEC records if relevant). If no record is found in the record cache, actively try to resolve the target name/qtype. This will delay the answer to the client.
+  Add records from the record cache (including DNSSEC records if relevant).
+  If no record is found in the record cache, actively try to resolve the target name/qtype.
+  This will delay the answer to the client.
 ``pdns.AdditionalMode.ResolveDeferred``
-  Add records from the record cache (including DNSSEC records if relevant). If no record is found in the record cache and the negative cache also has no entry, schedule a background task to resolve the target name/qtype. The next time the query is processed, the cache might hold the relevant information. This mode is not implemented yet.
+  Add records from the record cache (including DNSSEC records if relevant).
+  If no record is found in the record cache and the negative cache also has no entry, schedule a task to resolve the target name/qtype.
+  The next time the query is processed, the cache might hold the relevant information.
+  If a task is pushed, the answer that triggered it will be marked as variable and consequently not stored into the packet cache.
 
 If an additional record is not available at that time the query is stored into the packet cache the answer packet stored in the packet cache will not contain the additional record.
 Clients repeating the same question will get an answer from the packet cache if the question is still in the packet cache.
 These answers do not have the additional record, even if the record cache has learned it in the meantime .
 Clients will only see the additional record once the packet cache entry expires and the record cache is consulted again.
 The ``pdns.AdditionalMode.ResolveImmediately`` mode will not have this issue, at the cost of delaying the first query to resolve the additional records needed.
+The ``pdns.AdditionalMode.ResolveDeferred`` mode will only store answers in the packet cache if it determines that no deferred tasks are needed, i.e. either a positive or negative answer for potential additional records is available.
+If the additional records for an answer have low TTLs compared to the records in the answer section, tasks will be pushed often.
+Until all tasks for the answer have completed the packet cache will not contain the answer, making the packet cache less effective for this specific answer.
 
 Configuring additional record processing
 ----------------------------------------
index 8cd250cb3038f52c3f297ff51de1d10fca8bf910..b2dd49048917081062b1d4372750018f224e17d4 100644 (file)
@@ -377,7 +377,7 @@ SyncRes::SyncRes(const struct timeval& now) :  d_authzonequeries(0), d_outquerie
 
 static void allowAdditionalEntry(std::unordered_set<DNSName>& allowedAdditionals, const DNSRecord& rec);
 
-void SyncRes::resolveAdditionals(const DNSName& qname, QType qtype, AdditionalMode mode, std::vector<DNSRecord>& additionals, unsigned int depth)
+void SyncRes::resolveAdditionals(const DNSName& qname, QType qtype, AdditionalMode mode, std::vector<DNSRecord>& additionals, unsigned int depth, bool& additionalsNotInCache)
 {
   vector<DNSRecord> addRecords;
 
@@ -425,12 +425,43 @@ void SyncRes::resolveAdditionals(const DNSName& qname, QType qtype, AdditionalMo
     }
     break;
   }
-  case AdditionalMode::ResolveDeferred:
-    // FIXME: 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.
+  case AdditionalMode::ResolveDeferred: {
+    const bool oldCacheOnly = setCacheOnly(true);
+    set<GetBestNSAnswer> beenthere;
+    int res = doResolve(qname, qtype, addRecords, depth, beenthere, state);
+    setCacheOnly(oldCacheOnly);
+    if (res == 0 && addRecords.size() > 0) {
+      // We're conservative here. We do not add Bogus records in any circumstance, we add Indeterminates only if no
+      // validation is required.
+      if (vStateIsBogus(state)) {
+        return;
+      }
+      if (shouldValidate() && state != vState::Secure && state != vState::Insecure) {
+        return;
+      }
+      bool found = false;
+      for (auto& rec : addRecords) {
+        if (rec.d_place == DNSResourceRecord::ANSWER) {
+          found = true;
+          additionals.push_back(std::move(rec));
+        }
+      }
+      if (found) {
+        return;
+      }
+    }
+    // Not found in cache, check negcache and push task if also not in negcache
+    NegCache::NegCacheEntry ne;
+    bool inNegCache = g_negCache->get(qname, qtype, d_now, ne, false);
+    if (!inNegCache) {
+      // There are a few cases where an answer is neither stored in the record cache nor in the neg cache.
+      // An example is a SOA-less NODATA response. Rate limiting will kick in if those tasks are pushed too often.
+      // We might want to fix these cases (and always either store positive or negative) some day.
+      pushResolveTask(qname, qtype, d_now.tv_sec, d_now.tv_sec + 60);
+      additionalsNotInCache = true;
+    }
     break;
+  }
   case AdditionalMode::Ignore:
     break;
   }
@@ -442,7 +473,7 @@ void SyncRes::resolveAdditionals(const DNSName& qname, QType qtype, AdditionalMo
 // This function uses to state sets to avoid infinite recursion and allow depulication
 // depth is the main recursion depth
 // additionaldepth is the depth for addAdditionals itself
-void SyncRes::addAdditionals(QType qtype, const vector<DNSRecord>&start, vector<DNSRecord>&additionals, std::set<std::pair<DNSName, QType>>& uniqueCalls, std::set<std::tuple<DNSName, QType, QType>>& uniqueResults, unsigned int depth, unsigned additionaldepth)
+void SyncRes::addAdditionals(QType qtype, const vector<DNSRecord>&start, vector<DNSRecord>&additionals, std::set<std::pair<DNSName, QType>>& uniqueCalls, std::set<std::tuple<DNSName, QType, QType>>& uniqueResults, unsigned int depth, unsigned additionaldepth, bool& additionalsNotInCache)
 {
   if (additionaldepth >= 5 || start.empty()) {
     return;
@@ -474,7 +505,7 @@ void SyncRes::addAdditionals(QType qtype, const vector<DNSRecord>&start, vector<
       std::vector<DNSRecord> records;
       bool inserted = uniqueCalls.emplace(addname, targettype).second;
       if (inserted) {
-        resolveAdditionals(addname, targettype, mode, records, depth);
+        resolveAdditionals(addname, targettype, mode, records, depth, additionalsNotInCache);
       }
       if (!records.empty()) {
         for (auto r = records.begin(); r != records.end(); ) {
@@ -501,14 +532,14 @@ void SyncRes::addAdditionals(QType qtype, const vector<DNSRecord>&start, vector<
           }
           uniqueResults.emplace(r.d_name, r.d_type, covered);
         }
-        addAdditionals(targettype, records, additionals, uniqueCalls, uniqueResults, depth, additionaldepth + 1);
+        addAdditionals(targettype, records, additionals, uniqueCalls, uniqueResults, depth, additionaldepth + 1, additionalsNotInCache);
       }
     }
   }
 }
 
 // The entry point for other code
-void SyncRes::addAdditionals(QType qtype, vector<DNSRecord>&ret, unsigned int depth)
+bool SyncRes::addAdditionals(QType qtype, vector<DNSRecord>&ret, unsigned int depth)
 {
   // The additional records of interest
   std::vector<DNSRecord> additionals;
@@ -520,12 +551,14 @@ void SyncRes::addAdditionals(QType qtype, vector<DNSRecord>&ret, unsigned int de
   // For RRSIGs, the type covered is stored in the second Qtype
   std::set<std::tuple<DNSName, QType, QType>> uniqueResults;
 
-  addAdditionals(qtype, ret, additionals, uniqueCalls, uniqueResults, depth, 0);
+  bool additionalsNotInCache = false;
+  addAdditionals(qtype, ret, additionals, uniqueCalls, uniqueResults, depth, 0, additionalsNotInCache);
 
   for (auto& rec : additionals) {
     rec.d_place = DNSResourceRecord::ADDITIONAL;
     ret.push_back(std::move(rec));
   }
+  return additionalsNotInCache;
 }
 
 /** everything begins here - this is the entry point just after receiving a packet */
@@ -580,7 +613,10 @@ int SyncRes::beginResolve(const DNSName &qname, const QType qtype, QClass qclass
   // Avoid calling addAdditionals() if we know we won't find anything
   auto luaLocal = g_luaconfs.getLocal();
   if (res == 0 && qclass == QClass::IN && luaLocal->allowAdditionalQTypes.find(qtype) != luaLocal->allowAdditionalQTypes.end()) {
-    addAdditionals(qtype, ret, depth);
+    bool additionalsNotInCache = addAdditionals(qtype, ret, depth);
+    if (additionalsNotInCache) {
+      d_wasVariable = true;
+    }
   }
   d_eventTrace.add(RecEventTrace::SyncRes, res, false);
   return res;
@@ -1772,7 +1808,7 @@ vector<ComboAddress> SyncRes::getAddrs(const DNSName &qname, unsigned int depth,
       // No IPv6 records in cache, check negcache and submit async task if negache does not have the data
       // so that the next time the cache or the negcache will have data
       NegCache::NegCacheEntry ne;
-      bool inNegCache = g_negCache->get(qname, QType::AAAA, d_now, ne, true);
+      bool inNegCache = g_negCache->get(qname, QType::AAAA, d_now, ne, false);
       if (!inNegCache) {
         pushResolveTask(qname, QType::AAAA, d_now.tv_sec, d_now.tv_sec + 60);
       }
index bc0ea9aa5d2b6d67106abebae73fe9466fc4cc67..f1c13e55b3ce9cf537692c64756635ee9c2a4d90 100644 (file)
@@ -645,9 +645,9 @@ private:
   typedef std::map<DNSName,vState> zonesStates_t;
   enum StopAtDelegation { DontStop, Stop, Stopped };
 
-  void resolveAdditionals(const DNSName& qname, QType qtype, AdditionalMode, std::vector<DNSRecord>& additionals, unsigned int depth);
-  void addAdditionals(QType qtype, const vector<DNSRecord>&start, vector<DNSRecord>&addditionals, std::set<std::pair<DNSName, QType>>& uniqueCalls, std::set<std::tuple<DNSName, QType, QType>>& uniqueResults, unsigned int depth, unsigned int adddepth);
-  void addAdditionals(QType qtype, vector<DNSRecord>&ret, unsigned int depth);
+  void resolveAdditionals(const DNSName& qname, QType qtype, AdditionalMode, std::vector<DNSRecord>& additionals, unsigned int depth, bool& pushed);
+  void addAdditionals(QType qtype, const vector<DNSRecord>&start, vector<DNSRecord>&addditionals, std::set<std::pair<DNSName, QType>>& uniqueCalls, std::set<std::tuple<DNSName, QType, QType>>& uniqueResults, unsigned int depth, unsigned int adddepth, bool& pushed);
+  bool addAdditionals(QType qtype, vector<DNSRecord>&ret, unsigned int depth);
 
   bool doDoTtoAuth(const DNSName& ns) const;
   int doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, const DNSName &qname, QType qtype, vector<DNSRecord>&ret,