]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
rec: Disable outgoing v4 when QLA has no v4 addresses
authorPieter Lexis <pieter.lexis@powerdns.com>
Tue, 2 Jun 2020 12:55:08 +0000 (14:55 +0200)
committerOtto Moerbeek <otto.moerbeek@open-xchange.com>
Fri, 3 Jul 2020 08:25:08 +0000 (10:25 +0200)
pdns/pdns_recursor.cc
pdns/recursordist/Makefile.am
pdns/recursordist/test-syncres_cc.cc
pdns/recursordist/test-syncres_cc10.cc [new file with mode: 0644]
pdns/syncres.cc
pdns/syncres.hh

index 73bcc4f7196129ddfed29829a66dfcdb09523320..0fcb702b6aff3e82636d22b93abe290851a5c768 100644 (file)
@@ -4158,6 +4158,15 @@ static int serviceMain(int argc, char*argv[])
     exit(99);
   }
 
+  if(pdns::isQueryLocalAddressFamilyEnabled(AF_INET)) {
+    SyncRes::s_doIPv4=true;
+    g_log<<Logger::Warning<<"Enabling IPv4 transport for outgoing queries"<<endl;
+  }
+  else {
+    g_log<<Logger::Warning<<"NOT using IPv6 for outgoing queries - add an IPv4 address (like '0.0.0.0') to query-local-address to enable"<<endl;
+  }
+
+
   if(pdns::isQueryLocalAddressFamilyEnabled(AF_INET6)) {
     SyncRes::s_doIPv6=true;
     g_log<<Logger::Warning<<"Enabling IPv6 transport for outgoing queries"<<endl;
index 4fcb19ca723520bcb18a11f8998ad4a449916fa1..573047c75eec4a66bbcf773835a3b7c42553b4e4 100644 (file)
@@ -306,6 +306,7 @@ testrunner_SOURCES = \
        test-syncres_cc7.cc \
        test-syncres_cc8.cc \
        test-syncres_cc9.cc \
+       test-syncres_cc10.cc \
        test-tsig.cc \
        test-xpf_cc.cc \
        testrunner.cc \
index 97ff66d4b7617b73bdcd1bb858e75db71e92333e..c3d7af8ef55d8ed6f8c4cea2f817c85b1f9523b2 100644 (file)
@@ -118,6 +118,7 @@ void initSR(bool debug)
   SyncRes::s_packetcacheservfailttl = 60;
   SyncRes::s_serverdownmaxfails = 64;
   SyncRes::s_serverdownthrottletime = 60;
+  SyncRes::s_doIPv4 = true;
   SyncRes::s_doIPv6 = true;
   SyncRes::s_ecsipv4limit = 24;
   SyncRes::s_ecsipv6limit = 56;
diff --git a/pdns/recursordist/test-syncres_cc10.cc b/pdns/recursordist/test-syncres_cc10.cc
new file mode 100644 (file)
index 0000000..c7a8708
--- /dev/null
@@ -0,0 +1,138 @@
+#define BOOST_TEST_DYN_LINK
+#include <boost/test/unit_test.hpp>
+
+#include "test-syncres_cc.hh"
+
+BOOST_AUTO_TEST_SUITE(syncres_cc10)
+BOOST_AUTO_TEST_CASE(test_outgoing_v4_only)
+{
+  std::unique_ptr<SyncRes> sr;
+  initSR(sr);
+  SyncRes::s_doIPv6 = false;
+  primeHints();
+  bool v6Hit = false;
+  bool v4Hit = false;
+  int queries = 0;
+
+  const DNSName target("powerdns.com.");
+  sr->setAsyncCallback([target, &v4Hit, &v6Hit, &queries](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, LWResult* res, bool* chained) {
+    queries++;
+    if (isRootServer(ip)) {
+      setLWResult(res, 0, false, false, true);
+
+      if (domain == DNSName("powerdns.com.")) {
+        addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
+        addRecordToLW(res, "ns1.powerdns.com.", QType::AAAA, "2001:DB8:1::53", DNSResourceRecord::ADDITIONAL, 3600);
+        addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
+      }
+      return 1;
+    }
+    else if (ip == ComboAddress("192.0.2.1:53")) {
+      setLWResult(res, 0, true, false, false);
+      v4Hit = true;
+      if (domain == DNSName("powerdns.com.")) {
+        addRecordToLW(res, domain, QType::A, "192.0.2.2");
+      }
+      return 1;
+    }
+    else if (ip == ComboAddress("[2001:DB8:1::53]:53")) {
+      setLWResult(res, 0, true, false, false);
+      v6Hit = true;
+      if (domain == DNSName("powerdns.com.")) {
+        addRecordToLW(res, domain, QType::A, "192.0.2.2");
+      }
+      return 1;
+    }
+    return 0;
+  });
+
+  vector<DNSRecord> ret;
+  int rcode;
+  rcode = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+  BOOST_REQUIRE_EQUAL(queries, 2);
+  BOOST_REQUIRE_EQUAL(v4Hit, true);
+  BOOST_REQUIRE_EQUAL(v6Hit, false);
+  BOOST_CHECK_EQUAL(rcode, RCode::NoError);
+  BOOST_CHECK_EQUAL(ret.size(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(test_outgoing_v4_only_no_A_in_delegation)
+{
+  // The name is not resolvable, as there's no A glue for an in-bailiwick NS
+  std::unique_ptr<SyncRes> sr;
+  initSR(sr);
+  SyncRes::s_doIPv6 = false;
+  primeHints();
+  int queries = 0;
+
+  const DNSName target("powerdns.com.");
+  sr->setAsyncCallback([target, &queries](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, LWResult* res, bool* chained) {
+    queries++;
+    if (isRootServer(ip)) {
+      setLWResult(res, 0, false, false, true);
+
+      if (domain == DNSName("powerdns.com.")) {
+        addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
+        addRecordToLW(res, "ns1.powerdns.com.", QType::AAAA, "2001:DB8:1::53", DNSResourceRecord::ADDITIONAL, 3600);
+      }
+      return 1;
+    }
+    else if (ip == ComboAddress("[2001:DB8:1::53]:53")) {
+      setLWResult(res, 0, true, false, false);
+      if (domain == DNSName("powerdns.com.")) {
+        addRecordToLW(res, domain, QType::A, "192.0.2.2");
+      }
+      return 1;
+    }
+    return 0;
+  });
+
+  vector<DNSRecord> ret;
+  int rcode;
+  rcode = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+  BOOST_REQUIRE_EQUAL(queries, 14); // We keep trying all parent nameservers, this is wrong!
+  BOOST_CHECK_EQUAL(rcode, RCode::ServFail);
+  BOOST_CHECK_EQUAL(ret.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(test_outgoing_v6_only_no_AAAA_in_delegation)
+{
+  std::unique_ptr<SyncRes> sr;
+  initSR(sr);
+  SyncRes::s_doIPv4 = false;
+  SyncRes::s_doIPv6 = true;
+  primeHints();
+  int queries = 0;
+
+  const DNSName target("powerdns.com.");
+  sr->setAsyncCallback([target, &queries](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, LWResult* res, bool* chained) {
+    cout<<ip.toString()<<endl;
+    cout<<domain<<"|"<<type<<endl;
+    queries++;
+    if (isRootServer(ip)) {
+      setLWResult(res, 0, false, false, true);
+      if (domain == DNSName("powerdns.com.")) {
+        addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
+        addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
+      }
+      return 1;
+    }
+    else if (ip == ComboAddress("192.0.2.1:53")) {
+      setLWResult(res, 0, true, false, false);
+      if (domain == DNSName("powerdns.com.")) {
+        addRecordToLW(res, domain, QType::A, "192.0.2.2");
+      }
+      return 1;
+    }
+    return 0;
+  });
+
+  vector<DNSRecord> ret;
+  int rcode;
+  rcode = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+  BOOST_REQUIRE_EQUAL(queries, 14); // The recursor tries all parent nameservers... this needs to be fixed
+  BOOST_CHECK_EQUAL(rcode, RCode::ServFail);
+  BOOST_CHECK_EQUAL(ret.size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
index 19eaa418a4d0658889fe128c68a75090ba9ca011..fe40aeea4b6aa097ce80530269c245f18267406b 100644 (file)
@@ -85,6 +85,7 @@ uint8_t SyncRes::s_ecsipv6limit;
 uint8_t SyncRes::s_ecsipv4cachelimit;
 uint8_t SyncRes::s_ecsipv6cachelimit;
 
+bool SyncRes::s_doIPv4;
 bool SyncRes::s_doIPv6;
 bool SyncRes::s_nopacketcache;
 bool SyncRes::s_rootNXTrust;
@@ -934,7 +935,7 @@ vector<ComboAddress> SyncRes::getAddrs(const DNSName &qname, unsigned int depth,
     vState newState = Indeterminate;
     res_t resv4;
     // If IPv4 ever becomes second class, we should revisit this
-    if (doResolve(qname, QType::A, resv4, depth+1, beenthere, newState) == 0) {  // this consults cache, OR goes out
+    if (s_doIPv4 && doResolve(qname, QType::A, resv4, depth+1, beenthere, newState) == 0) {  // this consults cache, OR goes out
       for (auto const &i : resv4) {
         if (i.d_type == QType::A) {
           if (auto rec = getRR<ARecordContent>(i)) {
@@ -943,7 +944,7 @@ vector<ComboAddress> SyncRes::getAddrs(const DNSName &qname, unsigned int depth,
         }
       }
     }
-    if (s_doIPv6) {
+    if (s_doIPv6) { // s_doIPv6 **IMPLIES** pdns::isQueryLocalAddressFamilyEnabled(AF_INET6) returned true
       if (ret.empty()) {
         // We did not find IPv4 addresses, try to get IPv6 ones
         newState = Indeterminate;
@@ -1046,10 +1047,16 @@ void SyncRes::getBestNSFromCache(const DNSName &qname, const QType& qtype, vecto
       for(auto k=ns.cbegin();k!=ns.cend(); ++k) {
         if(k->d_ttl > (unsigned int)d_now.tv_sec ) {
           vector<DNSRecord> aset;
+          QType nsqt{QType::ADDR};
+          if (s_doIPv4 && !s_doIPv6) {
+            nsqt = QType::A;
+          } else if (!s_doIPv4 && s_doIPv6) {
+            nsqt = QType::AAAA;
+          }
 
           const DNSRecord& dr=*k;
          auto nrr = getRR<NSRecordContent>(dr);
-          if(nrr && (!nrr->getNS().isPartOf(subdomain) || s_RC->get(d_now.tv_sec, nrr->getNS(), s_doIPv6 ? QType(QType::ADDR) : QType(QType::A),
+          if(nrr && (!nrr->getNS().isPartOf(subdomain) || s_RC->get(d_now.tv_sec, nrr->getNS(), nsqt,
                                                                     false, doLog() ? &aset : 0, d_cacheRemote, d_routingTag) > 5)) {
             bestns.push_back(dr);
             LOG(prefix<<qname<<": NS (with ip, or non-glue) in cache for '"<<subdomain<<"' -> '"<<nrr->getNS()<<"'"<<endl);
index 6acfcea2f8868fb11f381a0abbbb37e749d3b472..405a24c25d619a1e47e08882ee8f15f94721d83c 100644 (file)
@@ -768,6 +768,7 @@ public:
   static uint8_t s_ecsipv6limit;
   static uint8_t s_ecsipv4cachelimit;
   static uint8_t s_ecsipv6cachelimit;
+  static bool s_doIPv4;
   static bool s_doIPv6;
   static bool s_noEDNSPing;
   static bool s_noEDNS;