]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Merge pull request #5570 from rgacogne/rec-neg-validation
authorRemi Gacogne <rgacogne@users.noreply.github.com>
Thu, 10 Aug 2017 09:14:45 +0000 (11:14 +0200)
committerGitHub <noreply@github.com>
Thu, 10 Aug 2017 09:14:45 +0000 (11:14 +0200)
rec: Be more careful about the validation of negative answers

pdns/recursordist/test-syncres_cc.cc
pdns/syncres.cc

index 399e49f5bf1c160f6b675a7e3001d22ba2e78cb7..6c128e5923ec1ec8941d547091e5b38e0b2178b0 100644 (file)
@@ -6597,6 +6597,63 @@ BOOST_AUTO_TEST_CASE(test_dnssec_no_ta) {
   BOOST_CHECK_EQUAL(queriesCount, 1);
 }
 
+BOOST_AUTO_TEST_CASE(test_dnssec_bogus_nodata) {
+  std::unique_ptr<SyncRes> sr;
+  initSR(sr, true);
+
+  g_dnssecmode = DNSSECMode::ValidateAll;
+
+  primeHints();
+  const DNSName target("powerdns.com.");
+  testkeysset_t keys;
+
+  auto luaconfsCopy = g_luaconfs.getCopy();
+  luaconfsCopy.dsAnchors.clear();
+  generateKeyMaterial(DNSName("."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys, luaconfsCopy.dsAnchors);
+  generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys);
+  g_luaconfs.setState(luaconfsCopy);
+
+  size_t queriesCount = 0;
+
+  sr->setAsyncCallback([target,&queriesCount,keys](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) {
+      queriesCount++;
+
+      if (type == QType::DS) {
+        return 0;
+      }
+      else if (type == QType::DNSKEY) {
+        setLWResult(res, 0, true, false, true);
+        addDNSKEY(keys, domain, 300, res->d_records);
+        addRRSIG(keys, res->d_records, domain, 300);
+        return 1;
+      }
+      else {
+
+        setLWResult(res, 0, true, false, true);
+        return 1;
+      }
+
+      return 0;
+    });
+
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, RCode::NoError);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_REQUIRE_EQUAL(ret.size(), 0);
+  /* com|NS, powerdns.com|NS, powerdns.com|A */
+  BOOST_CHECK_EQUAL(queriesCount, 3);
+
+  /* again, to test the cache */
+  ret.clear();
+  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, RCode::NoError);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_REQUIRE_EQUAL(ret.size(), 0);
+  /* we don't store empty results */
+  BOOST_CHECK_EQUAL(queriesCount, 6);
+}
+
 /*
 // cerr<<"asyncresolve called to ask "<<ip.toStringWithPort()<<" about "<<domain.toString()<<" / "<<QType(type).getName()<<" over "<<(doTCP ? "TCP" : "UDP")<<" (rd: "<<sendRDQuery<<", EDNS0 level: "<<EDNS0Level<<")"<<endl;
 
index 89975fc8e68a6b0d2344b97a3f3170069a10de02..1b1e70208015439414745a20903057cc295c05fd 100644 (file)
@@ -1909,15 +1909,17 @@ bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, co
       rec.d_ttl = min(rec.d_ttl, s_maxnegttl);
       if(newtarget.empty()) // only add a SOA if we're not going anywhere after this
         ret.push_back(rec);
-      if(!wasVariable()) {
-        NegCache::NegCacheEntry ne;
 
-        ne.d_ttd = d_now.tv_sec + rec.d_ttl;
-        ne.d_name = qname;
-        ne.d_qtype = QType(0); // this encodes 'whole record'
-        ne.d_auth = rec.d_name;
-        harvestNXRecords(lwr.d_records, ne);
-        getDenialValidationState(ne, state, NXDOMAIN, false);
+      NegCache::NegCacheEntry ne;
+
+      ne.d_ttd = d_now.tv_sec + rec.d_ttl;
+      ne.d_name = qname;
+      ne.d_qtype = QType(0); // this encodes 'whole record'
+      ne.d_auth = rec.d_name;
+      harvestNXRecords(lwr.d_records, ne);
+      getDenialValidationState(ne, state, NXDOMAIN, false);
+
+      if(!wasVariable()) {
         t_sstorage.negcache.add(ne);
         if(s_rootNXTrust && ne.d_auth.isRoot() && auth.isRoot()) {
           ne.d_name = ne.d_name.getLastLabel();
@@ -2010,14 +2012,16 @@ bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, co
       else {
         rec.d_ttl = min(s_maxnegttl, rec.d_ttl);
         ret.push_back(rec);
+
+        NegCache::NegCacheEntry ne;
+        ne.d_auth = rec.d_name;
+        ne.d_ttd = d_now.tv_sec + rec.d_ttl;
+        ne.d_name = qname;
+        ne.d_qtype = qtype;
+        harvestNXRecords(lwr.d_records, ne);
+        getDenialValidationState(ne, state, NXQTYPE, qtype == QType::DS);
+
         if(!wasVariable()) {
-          NegCache::NegCacheEntry ne;
-          ne.d_auth = rec.d_name;
-          ne.d_ttd = d_now.tv_sec + rec.d_ttl;
-          ne.d_name = qname;
-          ne.d_qtype = qtype;
-          harvestNXRecords(lwr.d_records, ne);
-          getDenialValidationState(ne, state, NXQTYPE, qtype == QType::DS);
           if(qtype.getCode()) {  // prevents us from blacking out a whole domain
             t_sstorage.negcache.add(ne);
           }
@@ -2222,6 +2226,10 @@ bool SyncRes::processAnswer(unsigned int depth, LWResult& lwr, const DNSName& qn
   if(nsset.empty() && !lwr.d_rcode && (negindic || lwr.d_aabit || sendRDQuery)) {
     LOG(prefix<<qname<<": status=noerror, other types may exist, but we are done "<<(negindic ? "(have negative SOA) " : "")<<(lwr.d_aabit ? "(have aa bit) " : "")<<endl);
 
+    if(state == Secure && lwr.d_aabit && !negindic) {
+      updateValidationState(state, Bogus);
+    }
+
     if(d_doDNSSEC)
       addNXNSECS(ret, lwr.d_records);