]> granicus.if.org Git - pdns/commitdiff
rec: Add ECS unit tests for `SyncRes`
authorRemi Gacogne <remi.gacogne@powerdns.com>
Wed, 22 Mar 2017 15:44:24 +0000 (16:44 +0100)
committerPieter Lexis <pieter.lexis@powerdns.com>
Tue, 4 Apr 2017 15:11:25 +0000 (17:11 +0200)
pdns/pdns_recursor.cc
pdns/recursordist/ecs.cc
pdns/recursordist/test-syncres_cc.cc
pdns/syncres.cc
pdns/syncres.hh

index 01ae81906e1671c481e92537b255dfcb5eaf6b27..478bc4a2abdf70f8c7b5aa6a2a8c806ca8197c1a 100644 (file)
@@ -2810,6 +2810,9 @@ static int serviceMain(int argc, char*argv[])
     SyncRes::s_serverID=tmp;
   }
 
+  SyncRes::s_ecsipv4limit = ::arg().asNum("ecs-ipv4-bits");
+  SyncRes::s_ecsipv6limit = ::arg().asNum("ecs-ipv6-bits");
+
   g_networkTimeoutMsec = ::arg().asNum("network-timeout");
 
   g_initialDomainMap = parseAuthAndForwards();
index f70e732729a9cf2f73318d94a3ab5ae057a2bbf3..7e9b7178bf7ff0923c2ef2c8e469f0eeb9033321 100644 (file)
@@ -5,41 +5,6 @@ NetmaskGroup g_ednssubnets;
 SuffixMatchNode g_ednsdomains;
 bool g_useIncomingECS;
 
-boost::optional<Netmask> getEDNSSubnetMask(const ComboAddress& local, const DNSName&dn, const ComboAddress& rem, boost::optional<const EDNSSubnetOpts&> incomingECS)
-{
-  static uint8_t l_ipv4limit, l_ipv6limit;
-  if(!l_ipv4limit) {
-    l_ipv4limit = ::arg().asNum("ecs-ipv4-bits");
-    l_ipv6limit = ::arg().asNum("ecs-ipv6-bits");
-  }
-  boost::optional<Netmask> result;
-  ComboAddress trunc;
-  uint8_t bits;
-  if(incomingECS) {
-    if (incomingECS->source.getBits() == 0) {
-      /* RFC7871 says we MUST NOT send any ECS if the source scope is 0 */
-      return result;
-    }
-    trunc = incomingECS->source.getMaskedNetwork();
-    bits = incomingECS->source.getBits();
-  }
-  else if(!local.isIPv4() || local.sin4.sin_addr.s_addr) { // detect unset 'requestor'
-    trunc = local;
-    bits = local.isIPv4() ? 32 : 128;
-  }
-  else {
-    /* nothing usable */
-    return result;
-  }
-
-  if(g_ednsdomains.check(dn) || g_ednssubnets.match(rem)) {
-    bits = std::min(bits, (trunc.isIPv4() ? l_ipv4limit : l_ipv6limit));
-    trunc.truncate(bits);
-    return boost::optional<Netmask>(Netmask(trunc, bits));
-  }
-  return result;
-}
-
 void  parseEDNSSubnetWhitelist(const std::string& wlist)
 {
   vector<string> parts;
index 7f531e08759f38f59e9db88f27fef35059399ba0..2e634ab39a95324c59ec656fec0cf4070df46884 100644 (file)
@@ -129,9 +129,12 @@ static void init(bool debug=false)
   SyncRes::s_serverdownmaxfails = 64;
   SyncRes::s_serverdownthrottletime = 60;
   SyncRes::s_doIPv6 = true;
+  SyncRes::s_ecsipv4limit = 24;
+  SyncRes::s_ecsipv6limit = 56;
 
-  ::arg().set("ecs-ipv4-bits", "24");
-  ::arg().set("ecs-ipv6-bits", "56");
+  g_ednssubnets = NetmaskGroup();
+  g_ednsdomains = SuffixMatchNode();
+  g_useIncomingECS = false;
 }
 
 static void initSR(std::unique_ptr<SyncRes>& sr, bool edns0, bool dnssec)
@@ -476,7 +479,7 @@ BOOST_AUTO_TEST_CASE(test_glued_referral) {
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, 0);
   BOOST_REQUIRE_EQUAL(ret.size(), 1);
-  BOOST_CHECK_EQUAL(ret[0].d_type, QType::A);
+  BOOST_CHECK(ret[0].d_type == QType::A);
   BOOST_CHECK_EQUAL(ret[0].d_name, target);
 }
 
@@ -545,10 +548,81 @@ BOOST_AUTO_TEST_CASE(test_glueless_referral) {
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, 0);
   BOOST_REQUIRE_EQUAL(ret.size(), 1);
-  BOOST_CHECK_EQUAL(ret[0].d_type, QType::A);
+  BOOST_CHECK(ret[0].d_type == QType::A);
   BOOST_CHECK_EQUAL(ret[0].d_name, target);
 }
 
+BOOST_AUTO_TEST_CASE(test_edns_submask_by_domain) {
+  std::unique_ptr<SyncRes> sr;
+  init();
+  initSR(sr, true, false);
+
+  primeHints();
+
+  const DNSName target("powerdns.com.");
+  g_useIncomingECS = true;
+  g_ednsdomains.add(target);
+
+  EDNSSubnetOpts incomingECS;
+  incomingECS.source = Netmask("192.0.2.128/32");
+  sr->setIncomingECSFound(true);
+  sr->setIncomingECS(incomingECS);
+
+  sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+
+      BOOST_REQUIRE(srcmask);
+      BOOST_CHECK_EQUAL(srcmask->toString(), "192.0.2.0/24");
+      return 0;
+    });
+
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, 2);
+}
+
+BOOST_AUTO_TEST_CASE(test_edns_submask_by_addr) {
+  std::unique_ptr<SyncRes> sr;
+  init();
+  initSR(sr, true, false);
+
+  primeHints();
+
+  const DNSName target("powerdns.com.");
+  g_useIncomingECS = true;
+  g_ednssubnets.addMask("192.0.2.1/32");
+
+  EDNSSubnetOpts incomingECS;
+  incomingECS.source = Netmask("2001:DB8::FF/128");
+  sr->setIncomingECSFound(true);
+  sr->setIncomingECS(incomingECS);
+
+  sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+
+      if (isRootServer(ip)) {
+        BOOST_REQUIRE(!srcmask);
+
+        setLWResult(res, 0, true, false, true);
+        addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
+        addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
+        return 1;
+      } else if (ip == ComboAddress("192.0.2.1:53")) {
+
+        BOOST_REQUIRE(srcmask);
+        BOOST_CHECK_EQUAL(srcmask->toString(), "2001:db8::/56");
+
+        setLWResult(res, 0, true, false, false);
+        addRecordToLW(res, domain, QType::A, "192.0.2.2");
+        return 1;
+      }
+
+      return 0;
+    });
+
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, 0);
+}
+
 /*
   TODO:
 
@@ -571,8 +645,6 @@ check we query the fastest auth available first?
 
 check we correctly store slow servers
 
-check EDNS subnetmask
-
 if possible, check preoutquery
 
 check depth limit
index f88900e41265aa539759bd885bac1b8b591b406f..8b1c95ef23a488833c466a1a24fabfd34bc4fb07 100644 (file)
@@ -67,6 +67,8 @@ std::atomic<uint64_t> SyncRes::s_throttledqueries;
 std::atomic<uint64_t> SyncRes::s_dontqueries;
 std::atomic<uint64_t> SyncRes::s_nodelegated;
 std::atomic<uint64_t> SyncRes::s_unreachables;
+uint8_t SyncRes::s_ecsipv4limit;
+uint8_t SyncRes::s_ecsipv6limit;
 unsigned int SyncRes::s_minimumTTL;
 bool SyncRes::s_doIPv6;
 bool SyncRes::s_nopacketcache;
@@ -1407,7 +1409,7 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con
              LOG(prefix<<qname<<": query handled by Lua"<<endl);
            }
            else {
-             ednsmask=getEDNSSubnetMask(d_requestor, qname, *remoteIP, d_incomingECSFound ? d_incomingECS : boost::none);
+             ednsmask=getEDNSSubnetMask(d_requestor, qname, *remoteIP);
               if(ednsmask) {
                 LOG(prefix<<qname<<": Adding EDNS Client Subnet Mask "<<ednsmask->toString()<<" to query"<<endl);
               }
@@ -1584,6 +1586,36 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con
   return -1;
 }
 
+boost::optional<Netmask> SyncRes::getEDNSSubnetMask(const ComboAddress& local, const DNSName&dn, const ComboAddress& rem)
+{
+  boost::optional<Netmask> result;
+  ComboAddress trunc;
+  uint8_t bits;
+  if(d_incomingECSFound) {
+    if (d_incomingECS->source.getBits() == 0) {
+      /* RFC7871 says we MUST NOT send any ECS if the source scope is 0 */
+      return result;
+    }
+    trunc = d_incomingECS->source.getMaskedNetwork();
+    bits = d_incomingECS->source.getBits();
+  }
+  else if(!local.isIPv4() || local.sin4.sin_addr.s_addr) { // detect unset 'requestor'
+    trunc = local;
+    bits = local.isIPv4() ? 32 : 128;
+  }
+  else {
+    /* nothing usable */
+    return result;
+  }
+
+  if(g_ednsdomains.check(dn) || g_ednssubnets.match(rem)) {
+    bits = std::min(bits, (trunc.isIPv4() ? s_ecsipv4limit : s_ecsipv6limit));
+    trunc.truncate(bits);
+    return boost::optional<Netmask>(Netmask(trunc, bits));
+  }
+
+  return result;
+}
 
 // used by PowerDNSLua - note that this neglects to add the packet count & statistics back to pdns_ercursor.cc
 int directResolve(const DNSName& qname, const QType& qtype, int qclass, vector<DNSRecord>& ret)
index d32e913c3ada4b66d8cc5ef9046b3a2ca06b2fa7..9910f5c09932470767f9efd149a9357b9eb4ef56 100644 (file)
@@ -544,6 +544,8 @@ public:
   static unsigned int s_packetcacheservfailttl;
   static unsigned int s_serverdownmaxfails;
   static unsigned int s_serverdownthrottletime;
+  static uint8_t s_ecsipv4limit;
+  static uint8_t s_ecsipv6limit;
   static bool s_nopacketcache;
   static string s_serverID;
 
@@ -585,6 +587,8 @@ private:
 
   int asyncresolveWrapper(const ComboAddress& ip, bool ednsMANDATORY, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, struct timeval* now, boost::optional<Netmask>& srcmask, LWResult* res) const;
 
+  boost::optional<Netmask> getEDNSSubnetMask(const ComboAddress& local, const DNSName&dn, const ComboAddress& rem);
+
   ostringstream d_trace;
   shared_ptr<RecursorLua4> d_pdl;
   boost::optional<const EDNSSubnetOpts&> d_incomingECS;
@@ -808,7 +812,6 @@ uint64_t* pleaseWipeCache(const DNSName& canon, bool subtree=false);
 uint64_t* pleaseWipePacketCache(const DNSName& canon, bool subtree);
 uint64_t* pleaseWipeAndCountNegCache(const DNSName& canon, bool subtree=false);
 void doCarbonDump(void*);
-boost::optional<Netmask> getEDNSSubnetMask(const ComboAddress& local, const DNSName&dn, const ComboAddress& rem, boost::optional<const EDNSSubnetOpts&> incomingECS);
 void  parseEDNSSubnetWhitelist(const std::string& wlist);
 
 extern __thread struct timeval g_now;