]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Initial code for ecs-cache-limit-ttl.
authorOtto Moerbeek <otto.moerbeek@open-xchange.com>
Wed, 27 Mar 2019 11:37:19 +0000 (12:37 +0100)
committerOtto Moerbeek <otto.moerbeek@open-xchange.com>
Wed, 27 Mar 2019 12:04:04 +0000 (13:04 +0100)
pdns/pdns_recursor.cc
pdns/recursor_cache.cc
pdns/recursor_cache.hh
pdns/recursordist/docs/settings.rst
pdns/syncres.cc
pdns/syncres.hh

index d4619922d68bbf12e4033e0fbc591f87ff207775..712b5dae7f5ceb8630dd3b89f9a9d7eec08248a1 100644 (file)
@@ -3657,6 +3657,7 @@ static int serviceMain(int argc, char*argv[])
   SyncRes::clearECSStats();
   SyncRes::s_ecsipv4cachelimit = ::arg().asNum("ecs-ipv4-cache-bits");
   SyncRes::s_ecsipv6cachelimit = ::arg().asNum("ecs-ipv6-cache-bits");
+  SyncRes::s_ecscachelimitttl = ::arg().asNum("ecs-cache-limit-ttl");
 
   if (!::arg().isEmpty("ecs-scope-zero-address")) {
     ComboAddress scopeZero(::arg()["ecs-scope-zero-address"]);
@@ -4268,6 +4269,8 @@ int main(int argc, char **argv)
     ::arg().set("ecs-ipv6-bits", "Number of bits of IPv6 address to pass for EDNS Client Subnet")="56";
     ::arg().set("ecs-ipv6-cache-bits", "Maximum number of bits of IPv6 mask to cache ECS response")="56";
     ::arg().set("ecs-minimum-ttl-override", "Set under adverse conditions, a minimum TTL for records in ECS-specific answers")="0";
+    ::arg().set("ecs-cache-limit-ttl", "Minimum TTL to cache ECS response")="0";
+    ::arg().set("edns-subnet-whitelist", "List of netmasks and domains that we should enable EDNS subnet for")="";
     ::arg().set("edns-subnet-whitelist", "List of netmasks and domains that we should enable EDNS subnet for")="";
     ::arg().set("ecs-add-for", "List of client netmasks for which EDNS Client Subnet will be added")="0.0.0.0/0, ::/0, " LOCAL_NETS_INVERSE;
     ::arg().set("ecs-scope-zero-address", "Address to send to whitelisted authoritative servers for incoming queries with ECS prefix-length source of 0")="";
index 7e0bf054ce2b5c4193c5bdd03cdd289492588fce..51bfa3663b58db4ecc2282c1f9ec8b7916851ddd 100644 (file)
@@ -236,7 +236,7 @@ int32_t MemRecursorCache::get(time_t now, const DNSName &qname, const QType& qt,
   return -1;
 }
 
-void MemRecursorCache::replace(time_t now, const DNSName &qname, const QType& qt, const vector<DNSRecord>& content, const vector<shared_ptr<RRSIGRecordContent>>& signatures, const std::vector<std::shared_ptr<DNSRecord>>& authorityRecs, bool auth, boost::optional<Netmask> ednsmask, vState state)
+void MemRecursorCache::replace(time_t now, const DNSName &qname, const QType& qt, const vector<DNSRecord>& content, const vector<shared_ptr<RRSIGRecordContent>>& signatures, const std::vector<std::shared_ptr<DNSRecord>>& authorityRecs, bool auth, boost::optional<Netmask> ednsmask, time_t minTTD, vState state)
 {
   d_cachecachevalid = false;
   //  cerr<<"Replacing "<<qname<<" for "<< (ednsmask ? ednsmask->toString() : "everyone") << endl;
@@ -305,9 +305,11 @@ void MemRecursorCache::replace(time_t now, const DNSName &qname, const QType& qt
   for(const auto i : content) {
     /* Yes, we have altered the d_ttl value by adding time(nullptr) to it
        prior to calling this function, so the TTL actually holds a TTD. */
-    ce.d_ttd=min(maxTTD, static_cast<time_t>(i.d_ttl));   // XXX this does weird things if TTLs differ in the set
-    //    cerr<<"To store: "<<i.d_content->getZoneRepresentation()<<" with ttl/ttd "<<i.d_ttl<<", capped at: "<<maxTTD<<endl;
-    ce.d_records.push_back(i.d_content);
+    if (minTTD == 0 || static_cast<time_t>(i.d_ttl) >= minTTD) {
+      ce.d_ttd = min(maxTTD, static_cast<time_t>(i.d_ttl));   // XXX this does weird things if TTLs differ in the set
+      //    cerr<<"To store: "<<i.d_content->getZoneRepresentation()<<" with ttl/ttd "<<i.d_ttl<<", capped at: "<<maxTTD<<endl;
+      ce.d_records.push_back(i.d_content);
+    }
   }
 
   if (!isNew) {
index e9a44c86c747c4edd173fa3d9402b4e47e343a91..3e841c5e612272d0a812de495ee3858e54063227 100644 (file)
@@ -57,7 +57,7 @@ public:
 
   int32_t get(time_t, const DNSName &qname, const QType& qt, bool requireAuth, vector<DNSRecord>* res, const ComboAddress& who, vector<std::shared_ptr<RRSIGRecordContent>>* signatures=nullptr, std::vector<std::shared_ptr<DNSRecord>>* authorityRecs=nullptr, bool* variable=nullptr, vState* state=nullptr, bool* wasAuth=nullptr);
 
-  void replace(time_t, const DNSName &qname, const QType& qt,  const vector<DNSRecord>& content, const vector<shared_ptr<RRSIGRecordContent>>& signatures, const std::vector<std::shared_ptr<DNSRecord>>& authorityRecs, bool auth, boost::optional<Netmask> ednsmask=boost::none, vState state=Indeterminate);
+  void replace(time_t, const DNSName &qname, const QType& qt,  const vector<DNSRecord>& content, const vector<shared_ptr<RRSIGRecordContent>>& signatures, const std::vector<std::shared_ptr<DNSRecord>>& authorityRecs, bool auth, boost::optional<Netmask> ednsmask=boost::none, time_t minTTD = 0, vState state=Indeterminate);
 
   void doPrune(unsigned int keep);
   uint64_t doDump(int fd);
index d3d072724316e7ded811d4f47aae2a8f1f750a7e..a1b692b49b3e8218aca57bb67c4becd3b00af58e 100644 (file)
@@ -421,6 +421,15 @@ This setting artificially raises the TTLs of records in the ANSWER section of EC
 While this is a gross hack, and violates RFCs, under conditions of DoS, it may enable you to continue serving your customers.
 Can be set at runtime using ``rec_control set-ecs-minimum-ttl 3600``.
 
+.. _setting-ecs-cache-limit-ttl:
+
+``ecs-cache-limit-ttl``
+-----------------------
+-  Integer
+-  Default: 0 (disabled)
+
+The minumum TTL for an ECS-specific answer to be inserted into the query cache.
+
 .. _setting-ecs-scope-zero-address:
 
 ``ecs-scope-zero-address``
index 04d9499e00f66d5ab5b2147cc104e161c5b7433f..c0412b388beffbabbe1a8b0d2fd2f32545fbba9c 100644 (file)
@@ -80,6 +80,8 @@ uint8_t SyncRes::s_ecsipv4limit;
 uint8_t SyncRes::s_ecsipv6limit;
 uint8_t SyncRes::s_ecsipv4cachelimit;
 uint8_t SyncRes::s_ecsipv6cachelimit;
+unsigned int SyncRes::s_ecscachelimitttl;
+
 bool SyncRes::s_doIPv6;
 bool SyncRes::s_nopacketcache;
 bool SyncRes::s_rootNXTrust;
@@ -119,8 +121,8 @@ static void accountAuthLatency(int usec, int family)
 SyncRes::SyncRes(const struct timeval& now) :  d_authzonequeries(0), d_outqueries(0), d_tcpoutqueries(0), d_throttledqueries(0), d_timeouts(0), d_unreachables(0),
                                               d_totUsec(0), d_now(now),
                                               d_cacheonly(false), d_doDNSSEC(false), d_doEDNS0(false), d_lm(s_lm)
-                                                 
-{ 
+
+{
 }
 
 /** everything begins here - this is the entry point just after receiving a packet */
@@ -433,10 +435,10 @@ uint64_t SyncRes::doDumpThrottleMap(int fd)
    Another cause of "No answer" may simply be a network condition.
    Nonsense answers are a clearer indication this host won't be able to do DNSSEC evah.
 
-   Previous implementations have suffered from turning off DNSSEC questions for an authoritative server based on timeouts. 
+   Previous implementations have suffered from turning off DNSSEC questions for an authoritative server based on timeouts.
    A clever idea is to only turn off DNSSEC if we know a domain isn't signed anyhow. The problem with that really
-   clever idea however is that at this point in PowerDNS, we may simply not know that yet. All the DNSSEC thinking happens 
-   elsewhere. It may not have happened yet. 
+   clever idea however is that at this point in PowerDNS, we may simply not know that yet. All the DNSSEC thinking happens
+   elsewhere. It may not have happened yet.
 
    For now this means we can't be clever, but will turn off DNSSEC if you reply with FormError or gibberish.
 */
@@ -447,19 +449,19 @@ int SyncRes::asyncresolveWrapper(const ComboAddress& ip, bool ednsMANDATORY, con
      the goal is to get as many remotes as possible on the highest level of EDNS support
      The levels are:
 
-     0) UNKNOWN Unknown state 
+     0) UNKNOWN Unknown state
      1) EDNS: Honors EDNS0
      2) EDNSIGNORANT: Ignores EDNS0, gives replies without EDNS0
      3) NOEDNS: Generates FORMERR on EDNS queries
 
      Everybody starts out assumed to be '0'.
      If '0', send out EDNS0
-        If you FORMERR us, go to '3', 
+        If you FORMERR us, go to '3',
         If no EDNS in response, go to '2'
      If '1', send out EDNS0
         If FORMERR, downgrade to 3
      If '2', keep on including EDNS0, see what happens
-        Same behaviour as 0 
+        Same behaviour as 0
      If '3', send bare queries
   */
 
@@ -483,7 +485,7 @@ int SyncRes::asyncresolveWrapper(const ComboAddress& ip, bool ednsMANDATORY, con
   int ret;
   for(int tries = 0; tries < 3; ++tries) {
     //    cerr<<"Remote '"<<ip.toString()<<"' currently in mode "<<mode<<endl;
-    
+
     if(mode==EDNSStatus::NOEDNS) {
       g_stats.noEdnsOutQueries++;
       EDNSLevel = 0; // level != mode
@@ -524,11 +526,11 @@ int SyncRes::asyncresolveWrapper(const ComboAddress& ip, bool ednsMANDATORY, con
        mode = EDNSStatus::EDNSOK;
        //      cerr<<"We find that "<<ip.toString()<<" is EDNS OK!"<<endl;
       }
-      
+
     }
     if(oldmode != mode || !ednsstatus->modeSetAt)
       ednsstatus->modeSetAt=d_now.tv_sec;
-    //    cerr<<"Result: ret="<<ret<<", EDNS-level: "<<EDNSLevel<<", haveEDNS: "<<res->d_haveEDNS<<", new mode: "<<mode<<endl;  
+    //    cerr<<"Result: ret="<<ret<<", EDNS-level: "<<EDNSLevel<<", haveEDNS: "<<res->d_haveEDNS<<", new mode: "<<mode<<endl;
     return ret;
   }
   return ret;
@@ -1278,7 +1280,7 @@ bool SyncRes::doCacheCheck(const DNSName &qname, const DNSName& authname, bool w
       DNSRecord dr;
       dr.d_type=QType::RRSIG;
       dr.d_name=sqname;
-      dr.d_ttl=ttl; 
+      dr.d_ttl=ttl;
       dr.d_content=signature;
       dr.d_place = DNSResourceRecord::ANSWER;
       dr.d_class=QClass::IN;
@@ -2422,7 +2424,11 @@ RCode::rcodes_ SyncRes::updateCacheFromRecords(unsigned int depth, LWResult& lwr
           !ednsmask ||
           (ednsmask->isIpv4() && ednsmask->getBits() <= SyncRes::s_ecsipv4cachelimit) ||
           (ednsmask->isIpv6() && ednsmask->getBits() <= SyncRes::s_ecsipv6cachelimit)) {
-        t_RC->replace(d_now.tv_sec, i->first.name, QType(i->first.type), i->second.records, i->second.signatures, authorityRecs, i->first.type == QType::DS ? true : isAA, i->first.place == DNSResourceRecord::ANSWER ? ednsmask : boost::none, recordState);
+        time_t minTTD = 0;
+        if (ednsmask && SyncRes::s_ecscachelimitttl > 0) {
+          minTTD = SyncRes::s_ecscachelimitttl + d_now.tv_sec;
+        }
+        t_RC->replace(d_now.tv_sec, i->first.name, QType(i->first.type), i->second.records, i->second.signatures, authorityRecs, i->first.type == QType::DS ? true : isAA, i->first.place == DNSResourceRecord::ANSWER ? ednsmask : boost::none, minTTD, recordState);
       }
     }
 
@@ -3199,7 +3205,7 @@ int directResolve(const DNSName& qname, const QType& qtype, int qclass, vector<D
     g_log<<Logger::Error<<"Failed to resolve "<<qname.toLogString()<<", got an exception"<<endl;
     ret.clear();
   }
-  
+
   return res;
 }
 
index c3b057757f556e119e40a80b8738db99a1074052..7b0734fd19000bdc570658605098263abdcbfb8e 100644 (file)
@@ -720,6 +720,7 @@ public:
   static uint8_t s_ecsipv6limit;
   static uint8_t s_ecsipv4cachelimit;
   static uint8_t s_ecsipv6cachelimit;
+  static unsigned int s_ecscachelimitttl;
   static bool s_doIPv6;
   static bool s_noEDNSPing;
   static bool s_noEDNS;