From: Remi Gacogne Date: Wed, 10 May 2017 16:08:23 +0000 (+0200) Subject: rec: Reply with and store DNSSEC wildcard proofs X-Git-Tag: rec-4.1.0-alpha1~50^2~22 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2b9842517fdc7699d0d26a76dc3bee9ab4354962;p=thirdparty%2Fpdns.git rec: Reply with and store DNSSEC wildcard proofs --- diff --git a/pdns/recursor_cache.cc b/pdns/recursor_cache.cc index e5d5ec1067..65397f8e62 100644 --- a/pdns/recursor_cache.cc +++ b/pdns/recursor_cache.cc @@ -34,7 +34,7 @@ unsigned int MemRecursorCache::bytes() } // returns -1 for no hits -int32_t MemRecursorCache::get(time_t now, const DNSName &qname, const QType& qt, bool requireAuth, vector* res, const ComboAddress& who, vector>* signatures, bool* variable, vState* state) +int32_t MemRecursorCache::get(time_t now, const DNSName &qname, const QType& qt, bool requireAuth, vector* res, const ComboAddress& who, vector>* signatures, std::vector>* authorityRecs, bool* variable, vState* state) { time_t ttd=0; // cerr<<"looking up "<< qname<<"|"+qt.getName()<<"\n"; @@ -80,6 +80,10 @@ int32_t MemRecursorCache::get(time_t now, const DNSName &qname, const QType& qt, if(signatures) // if you do an ANY lookup you are hosed XXXX *signatures=i->d_signatures; + + if(authorityRecs) // if you do an ANY lookup you are hosed here too XXXX + *authorityRecs=i->d_authorityRecs; + if(res) { if(res->empty()) moveCacheItemToFront(d_cache, i); @@ -130,7 +134,7 @@ bool MemRecursorCache::attemptToRefreshNSTTL(const QType& qt, const vector& content, const vector>& signatures, bool auth, boost::optional ednsmask, vState state) +void MemRecursorCache::replace(time_t now, const DNSName &qname, const QType& qt, const vector& content, const vector>& signatures, const std::vector>& authorityRecs, bool auth, boost::optional ednsmask, vState state) { d_cachecachevalid=false; cache_t::iterator stored; @@ -146,6 +150,7 @@ void MemRecursorCache::replace(time_t now, const DNSName &qname, const QType& qt CacheEntry ce=*stored; // this is a COPY ce.d_qtype=qt.getCode(); ce.d_signatures=signatures; + ce.d_authorityRecs=authorityRecs; ce.d_state=state; // cerr<<"asked to store "<< (qname.empty() ? "EMPTY" : qname.toString()) <<"|"+qt.getName()<<" -> '"; diff --git a/pdns/recursor_cache.hh b/pdns/recursor_cache.hh index 5b5780fa43..78150d2929 100644 --- a/pdns/recursor_cache.hh +++ b/pdns/recursor_cache.hh @@ -54,9 +54,10 @@ public: } unsigned int size(); unsigned int bytes(); - int32_t get(time_t, const DNSName &qname, const QType& qt, bool requireAuth, vector* res, const ComboAddress& who, vector>* signatures=0, bool* variable=0, vState* state=nullptr); + int32_t get(time_t, const DNSName &qname, const QType& qt, bool requireAuth, vector* res, const ComboAddress& who, vector>* signatures=nullptr, std::vector>* authorityRecs=nullptr, bool* variable=nullptr, vState* state=nullptr); + + void replace(time_t, const DNSName &qname, const QType& qt, const vector& content, const vector>& signatures, const std::vector>& authorityRecs, bool auth, boost::optional ednsmask=boost::none, vState state=Indeterminate); - void replace(time_t, const DNSName &qname, const QType& qt, const vector& content, const vector>& signatures, bool auth, boost::optional ednsmask=boost::none, vState state=Indeterminate); void doPrune(void); uint64_t doDump(int fd); @@ -85,6 +86,7 @@ private: bool d_auth; time_t d_ttd; records_t d_records; + std::vector> d_authorityRecs; Netmask d_netmask; vState d_state; }; diff --git a/pdns/recursordist/test-recursorcache_cc.cc b/pdns/recursordist/test-recursorcache_cc.cc index a0539d69b2..ec95ee015f 100644 --- a/pdns/recursordist/test-recursorcache_cc.cc +++ b/pdns/recursordist/test-recursorcache_cc.cc @@ -16,11 +16,12 @@ BOOST_AUTO_TEST_CASE(test_RecursorCacheSimple) { MemRecursorCache MRC; std::vector records; + std::vector> authRecords; std::vector> signatures; time_t now = time(nullptr); BOOST_CHECK_EQUAL(MRC.size(), 0); - MRC.replace(now, DNSName("hello"), QType(QType::A), records, signatures, true, boost::none); + MRC.replace(now, DNSName("hello"), QType(QType::A), records, signatures, authRecords, true, boost::none); BOOST_CHECK_EQUAL(MRC.size(), 1); BOOST_CHECK_GT(MRC.bytes(), 1); BOOST_CHECK_EQUAL(MRC.doWipeCache(DNSName("hello"), false, QType::A), 1); @@ -33,10 +34,10 @@ BOOST_AUTO_TEST_CASE(test_RecursorCacheSimple) { DNSName a = DNSName("hello ")+DNSName(std::to_string(counter)); BOOST_CHECK_EQUAL(DNSName(a.toString()), a); - MRC.replace(now, a, QType(QType::A), records, signatures, true, boost::none); + MRC.replace(now, a, QType(QType::A), records, signatures, authRecords, true, boost::none); if(!MRC.doWipeCache(a, false)) BOOST_FAIL("Could not remove entry we just added to the cache!"); - MRC.replace(now, a, QType(QType::A), records, signatures, true, boost::none); + MRC.replace(now, a, QType(QType::A), records, signatures, authRecords, true, boost::none); } BOOST_CHECK_EQUAL(MRC.size(), counter); @@ -88,7 +89,7 @@ BOOST_AUTO_TEST_CASE(test_RecursorCacheSimple) { // insert a subnet specific entry records.push_back(dr1); - MRC.replace(now, power, QType(QType::AAAA), records, signatures, true, boost::optional("192.0.2.1/25")); + MRC.replace(now, power, QType(QType::AAAA), records, signatures, authRecords, true, boost::optional("192.0.2.1/25")); BOOST_CHECK_EQUAL(MRC.size(), 1); retrieved.clear(); @@ -109,7 +110,7 @@ BOOST_AUTO_TEST_CASE(test_RecursorCacheSimple) { // insert a NON-subnet specific entry records.clear(); records.push_back(dr2); - MRC.replace(now, power, QType(QType::A), records, signatures, true, boost::none); + MRC.replace(now, power, QType(QType::A), records, signatures, authRecords, true, boost::none); BOOST_CHECK_EQUAL(MRC.size(), 1); // NON-subnet specific should always be returned @@ -121,14 +122,14 @@ BOOST_AUTO_TEST_CASE(test_RecursorCacheSimple) { // insert a subnet specific entry for the same name but a different QType records.clear(); records.push_back(dr1); - MRC.replace(now, power, QType(QType::AAAA), records, signatures, true, boost::optional("192.0.2.1/25")); + MRC.replace(now, power, QType(QType::AAAA), records, signatures, authRecords, true, boost::optional("192.0.2.1/25")); // we should not have replaced the existing entry BOOST_CHECK_EQUAL(MRC.size(), 2); // insert a TXT one, we will use that later records.clear(); records.push_back(dr1); - MRC.replace(now, power, QType(QType::TXT), records, signatures, true, boost::none); + MRC.replace(now, power, QType(QType::TXT), records, signatures, authRecords, true, boost::none); // we should not have replaced any existing entry BOOST_CHECK_EQUAL(MRC.size(), 3); @@ -212,7 +213,7 @@ BOOST_AUTO_TEST_CASE(test_RecursorCacheSimple) { // insert auth record records.push_back(dr2); - MRC.replace(now, power, QType(QType::A), records, signatures, true, boost::none); + MRC.replace(now, power, QType(QType::A), records, signatures, authRecords, true, boost::none); BOOST_CHECK_EQUAL(MRC.size(), 1); BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1"), nullptr), (ttd-now)); BOOST_CHECK_EQUAL(retrieved.size(), 1); @@ -234,14 +235,14 @@ BOOST_AUTO_TEST_CASE(test_RecursorCacheSimple) { records.push_back(dr3); // non-auth should not replace valid auth - MRC.replace(now, power, QType(QType::A), records, signatures, false, boost::none); + MRC.replace(now, power, QType(QType::A), records, signatures, authRecords, false, boost::none); BOOST_CHECK_EQUAL(MRC.size(), 1); BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1"), nullptr), (ttd-now)); BOOST_REQUIRE_EQUAL(retrieved.size(), 1); BOOST_CHECK_EQUAL(getRR(retrieved.at(0))->getCA().toString(), dr2Content.toString()); // but non-auth _should_ replace expired auth - MRC.replace(ttd + 1, power, QType(QType::A), records, signatures, false, boost::none); + MRC.replace(ttd + 1, power, QType(QType::A), records, signatures, authRecords, false, boost::none); BOOST_CHECK_EQUAL(MRC.size(), 1); BOOST_CHECK_EQUAL(MRC.get(ttd + 1, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1"), nullptr), (dr3.d_ttl - (ttd + 1))); BOOST_REQUIRE_EQUAL(retrieved.size(), 1); @@ -250,7 +251,7 @@ BOOST_AUTO_TEST_CASE(test_RecursorCacheSimple) { // auth should replace non-auth records.clear(); records.push_back(dr2); - MRC.replace(now, power, QType(QType::A), records, signatures, false, boost::none); + MRC.replace(now, power, QType(QType::A), records, signatures, authRecords, false, boost::none); BOOST_CHECK_EQUAL(MRC.size(), 1); BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1"), nullptr), (ttd-now)); BOOST_REQUIRE_EQUAL(retrieved.size(), 1); diff --git a/pdns/recursordist/test-syncres_cc.cc b/pdns/recursordist/test-syncres_cc.cc index 1a4b3e3aa0..1ce08f2740 100644 --- a/pdns/recursordist/test-syncres_cc.cc +++ b/pdns/recursordist/test-syncres_cc.cc @@ -72,18 +72,18 @@ void primeHints(void) arr.d_content=std::make_shared(ComboAddress(rootIps4[c-'a'])); vector aset; aset.push_back(arr); - t_RC->replace(time(0), DNSName(templ), QType(QType::A), aset, vector>(), true); // auth, nuke it all + t_RC->replace(time(0), DNSName(templ), QType(QType::A), aset, vector>(), vector>(), true); // auth, nuke it all if (rootIps6[c-'a'] != NULL) { aaaarr.d_content=std::make_shared(ComboAddress(rootIps6[c-'a'])); vector aaaaset; aaaaset.push_back(aaaarr); - t_RC->replace(time(0), DNSName(templ), QType(QType::AAAA), aaaaset, vector>(), true); + t_RC->replace(time(0), DNSName(templ), QType(QType::AAAA), aaaaset, vector>(), vector>(), true); } nsset.push_back(nsrr); } - t_RC->replace(time(0), g_rootdnsname, QType(QType::NS), nsset, vector>(), false); // and stuff in the cache + t_RC->replace(time(0), g_rootdnsname, QType(QType::NS), nsset, vector>(), vector>(), false); // and stuff in the cache } LuaConfigItems::LuaConfigItems() @@ -282,7 +282,7 @@ static void computeRRSIG(const DNSSECPrivateKey& dpk, const DNSName& signer, con typedef std::unordered_map > testkeysset_t; -static void addRRSIG(const testkeysset_t& keys, std::vector& records, const DNSName& signer, uint32_t sigValidity, bool broken=false, boost::optional algo=boost::none) +static void addRRSIG(const testkeysset_t& keys, std::vector& records, const DNSName& signer, uint32_t sigValidity, bool broken=false, boost::optional algo=boost::none, boost::optional wildcard=boost::none) { if (records.empty()) { return; @@ -305,7 +305,7 @@ static void addRRSIG(const testkeysset_t& keys, std::vector& records, } RRSIGRecordContent rrc; - computeRRSIG(it->second.first, signer, records[recordsCount-1].d_name, records[recordsCount-1].d_type, records[recordsCount-1].d_ttl, sigValidity, rrc, recordcontents, algo); + computeRRSIG(it->second.first, signer, wildcard ? * wildcard : records[recordsCount-1].d_name, records[recordsCount-1].d_type, records[recordsCount-1].d_ttl, sigValidity, rrc, recordcontents, algo); if (broken) { rrc.d_signature[0] ^= 42; } @@ -1765,7 +1765,7 @@ BOOST_AUTO_TEST_CASE(test_flawed_nsset) { std::vector > sigs; addRecordToList(records, target, QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, now + 3600); - t_RC->replace(now, target, QType(QType::NS), records, sigs, true, boost::optional()); + t_RC->replace(now, target, QType(QType::NS), records, sigs, vector>(), true, boost::optional()); vector ret; int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret); @@ -1827,7 +1827,7 @@ BOOST_AUTO_TEST_CASE(test_cache_hit) { std::vector > sigs; addRecordToList(records, target, QType::A, "192.0.2.1", DNSResourceRecord::ANSWER, now + 3600); - t_RC->replace(now, target , QType(QType::A), records, sigs, true, boost::optional()); + t_RC->replace(now, target , QType(QType::A), records, sigs, vector>(), true, boost::optional()); vector ret; int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret); @@ -1943,7 +1943,7 @@ BOOST_AUTO_TEST_CASE(test_cache_expired_ttl) { std::vector > sigs; addRecordToList(records, target, QType::A, "192.0.2.42", DNSResourceRecord::ANSWER, now - 60); - t_RC->replace(now - 3600, target, QType(QType::A), records, sigs, true, boost::optional()); + t_RC->replace(now - 3600, target, QType(QType::A), records, sigs, vector>(), true, boost::optional()); vector ret; int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret); @@ -2076,7 +2076,7 @@ BOOST_AUTO_TEST_CASE(test_nodata_nsec_nodnssec) { addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600); /* the NSEC and RRSIG contents are complete garbage, please ignore them */ addRecordToLW(res, domain, QType::NSEC, "deadbeef", DNSResourceRecord::AUTHORITY); - addRecordToLW(res, domain, QType::RRSIG, "NSEC 5 3 600 2100010100000000 2100010100000000 24567 dummy data", DNSResourceRecord::AUTHORITY); + addRecordToLW(res, domain, QType::RRSIG, "NSEC 5 2 600 2100010100000000 2100010100000000 24567 dummy data", DNSResourceRecord::AUTHORITY); addRecordToLW(res, domain, QType::RRSIG, "SOA 5 3 600 2100010100000000 2100010100000000 24567 dummy data", DNSResourceRecord::AUTHORITY); return 1; }); @@ -2101,7 +2101,7 @@ BOOST_AUTO_TEST_CASE(test_nodata_nsec_dnssec) { addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600); /* the NSEC and RRSIG contents are complete garbage, please ignore them */ addRecordToLW(res, domain, QType::NSEC, "deadbeef", DNSResourceRecord::AUTHORITY); - addRecordToLW(res, domain, QType::RRSIG, "NSEC 5 3 600 2100010100000000 2100010100000000 24567 dummy data", DNSResourceRecord::AUTHORITY); + addRecordToLW(res, domain, QType::RRSIG, "NSEC 5 2 600 2100010100000000 2100010100000000 24567 dummy data", DNSResourceRecord::AUTHORITY); addRecordToLW(res, domain, QType::RRSIG, "SOA 5 3 600 2100010100000000 2100010100000000 24567 dummy data", DNSResourceRecord::AUTHORITY); return 1; }); @@ -2126,7 +2126,7 @@ BOOST_AUTO_TEST_CASE(test_nx_nsec_nodnssec) { addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600); /* the NSEC and RRSIG contents are complete garbage, please ignore them */ addRecordToLW(res, domain, QType::NSEC, "deadbeef", DNSResourceRecord::AUTHORITY); - addRecordToLW(res, domain, QType::RRSIG, "NSEC 5 3 600 2100010100000000 2100010100000000 24567 dummy data", DNSResourceRecord::AUTHORITY); + addRecordToLW(res, domain, QType::RRSIG, "NSEC 5 2 600 2100010100000000 2100010100000000 24567 dummy data", DNSResourceRecord::AUTHORITY); addRecordToLW(res, domain, QType::RRSIG, "SOA 5 3 600 2100010100000000 2100010100000000 24567 dummy data", DNSResourceRecord::AUTHORITY); return 1; }); @@ -2151,7 +2151,7 @@ BOOST_AUTO_TEST_CASE(test_nx_nsec_dnssec) { addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600); /* the NSEC and RRSIG contents are complete garbage, please ignore them */ addRecordToLW(res, domain, QType::NSEC, "deadbeef", DNSResourceRecord::AUTHORITY); - addRecordToLW(res, domain, QType::RRSIG, "NSEC 5 3 600 2100010100000000 2100010100000000 24567 dummy data", DNSResourceRecord::AUTHORITY); + addRecordToLW(res, domain, QType::RRSIG, "NSEC 5 2 600 2100010100000000 2100010100000000 24567 dummy data", DNSResourceRecord::AUTHORITY); addRecordToLW(res, domain, QType::RRSIG, "SOA 5 3 600 2100010100000000 2100010100000000 24567 dummy data", DNSResourceRecord::AUTHORITY); return 1; }); @@ -4393,6 +4393,87 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_nxdomain_nsec) { BOOST_CHECK_EQUAL(queriesCount, 6); } +BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec_wildcard) { + std::unique_ptr sr; + initSR(sr, true); + + g_dnssecmode = DNSSECMode::ValidateAll; + + primeHints(); + const DNSName target("www.powerdns.com."); + testkeysset_t keys; + + auto luaconfsCopy = g_luaconfs.getCopy(); + luaconfsCopy.dsAnchors.clear(); + generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys, luaconfsCopy.dsAnchors); + generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys); + 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& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* res) { + queriesCount++; + + if (type == QType::DS) { + setLWResult(res, 0, false, false, true); + addDS(domain, 300, res->d_records, keys); + addRRSIG(keys, res->d_records, domain, 300); + return 1; + } + 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 if (domain == target) { + if (isRootServer(ip)) { + setLWResult(res, 0, false, false, true); + addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600); + addDS(DNSName("com."), 300, res->d_records, keys); + addRRSIG(keys, res->d_records, DNSName("."), 300); + addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600); + return 1; + } + else if (ip == ComboAddress("192.0.2.1:53")) { + setLWResult(res, 0, false, false, true); + addRecordToLW(res, "powerdns.com.", QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600); + addDS(DNSName("powerdns.com."), 300, res->d_records, keys); + addRRSIG(keys, res->d_records, DNSName("com."), 300); + addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600); + return 1; + } + else if (ip == ComboAddress("192.0.2.2:53")) { + setLWResult(res, 0, true, false, true); + addRecordToLW(res, domain, QType::A, "192.0.2.42"); + addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300, false, boost::none, DNSName("*.powerdns.com")); + addNSECRecordToLW(DNSName("a.powerdns.com."), DNSName("wwz.powerdns.com."), { QType::A, QType::NSEC, QType::RRSIG }, 600, res->d_records); + addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300); + return 1; + } + } + + return 0; + }); + + vector ret; + int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, RCode::NoError); + BOOST_CHECK_EQUAL(sr->getValidationState(), Secure); + BOOST_REQUIRE_EQUAL(ret.size(), 4); + BOOST_CHECK_EQUAL(queriesCount, 6); + + /* 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(), Secure); + BOOST_REQUIRE_EQUAL(ret.size(), 4); + BOOST_CHECK_EQUAL(queriesCount, 6); +} + BOOST_AUTO_TEST_CASE(test_dnssec_validation_bogus_no_nsec) { std::unique_ptr sr; initSR(sr, true); diff --git a/pdns/reczones.cc b/pdns/reczones.cc index 1f21fd9c9f..41142b049a 100644 --- a/pdns/reczones.cc +++ b/pdns/reczones.cc @@ -62,13 +62,13 @@ void primeHints(void) arr.d_content=std::make_shared(ComboAddress(rootIps4[c-'a'])); vector aset; aset.push_back(arr); - t_RC->replace(time(0), DNSName(templ), QType(QType::A), aset, vector>(), true, boost::none, validationState); // auth, nuke it all + t_RC->replace(time(0), DNSName(templ), QType(QType::A), aset, vector>(), vector>(), true, boost::none, validationState); // auth, nuke it all if (rootIps6[c-'a'] != NULL) { aaaarr.d_content=std::make_shared(ComboAddress(rootIps6[c-'a'])); vector aaaaset; aaaaset.push_back(aaaarr); - t_RC->replace(time(0), DNSName(templ), QType(QType::AAAA), aaaaset, vector>(), true, boost::none, validationState); + t_RC->replace(time(0), DNSName(templ), QType(QType::AAAA), aaaaset, vector>(), vector>(), true, boost::none, validationState); } nsset.push_back(nsrr); @@ -83,11 +83,11 @@ void primeHints(void) if(rr.qtype.getCode()==QType::A) { vector aset; aset.push_back(DNSRecord(rr)); - t_RC->replace(time(0), rr.qname, QType(QType::A), aset, vector>(), true, boost::none, validationState); // auth, etc see above + t_RC->replace(time(0), rr.qname, QType(QType::A), aset, vector>(), vector>(), true, boost::none, validationState); // auth, etc see above } else if(rr.qtype.getCode()==QType::AAAA) { vector aaaaset; aaaaset.push_back(DNSRecord(rr)); - t_RC->replace(time(0), rr.qname, QType(QType::AAAA), aaaaset, vector>(), true, boost::none, validationState); + t_RC->replace(time(0), rr.qname, QType(QType::AAAA), aaaaset, vector>(), vector>(), true, boost::none, validationState); } else if(rr.qtype.getCode()==QType::NS) { rr.content=toLower(rr.content); nsset.push_back(DNSRecord(rr)); @@ -95,7 +95,7 @@ void primeHints(void) } } t_RC->doWipeCache(g_rootdnsname, false, QType::NS); - t_RC->replace(time(0), g_rootdnsname, QType(QType::NS), nsset, vector>(), false, boost::none, validationState); // and stuff in the cache + t_RC->replace(time(0), g_rootdnsname, QType(QType::NS), nsset, vector>(), vector>(), false, boost::none, validationState); // and stuff in the cache } static void makeNameToIPZone(std::shared_ptr newMap, const DNSName& hostname, const string& ip) diff --git a/pdns/syncres.cc b/pdns/syncres.cc index 2ec5dd0bf9..66e871e8c7 100644 --- a/pdns/syncres.cc +++ b/pdns/syncres.cc @@ -684,7 +684,7 @@ void SyncRes::getBestNSFromCache(const DNSName &qname, const QType& qtype, vecto vector ns; *flawedNSSet = false; - if(t_RC->get(d_now.tv_sec, subdomain, QType(QType::NS), false, &ns, d_requestor, nullptr) > 0) { + if(t_RC->get(d_now.tv_sec, subdomain, QType(QType::NS), false, &ns, d_requestor) > 0) { for(auto k=ns.cbegin();k!=ns.cend(); ++k) { if(k->d_ttl > (unsigned int)d_now.tv_sec ) { vector aset; @@ -810,7 +810,8 @@ bool SyncRes::doCNAMECacheCheck(const DNSName &qname, const QType &qtype, vector LOG(prefix< cset; vector> signatures; - if(t_RC->get(d_now.tv_sec, qname, QType(QType::CNAME), d_requireAuthData, &cset, d_requestor, &signatures, &d_wasVariable, &state) > 0) { + vector> authorityRecs; + if(t_RC->get(d_now.tv_sec, qname, QType(QType::CNAME), d_requireAuthData, &cset, d_requestor, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &state) > 0) { for(auto j=cset.cbegin() ; j != cset.cend() ; ++j) { if(j->d_ttl>(unsigned int) d_now.tv_sec) { @@ -830,6 +831,12 @@ bool SyncRes::doCNAMECacheCheck(const DNSName &qname, const QType &qtype, vector ret.push_back(sigdr); } + for(const auto& rec : authorityRecs) { + DNSRecord dr(*rec); + dr.d_ttl=j->d_ttl - d_now.tv_sec; + ret.push_back(dr); + } + if(qtype != QType::CNAME) { // perhaps they really wanted a CNAME! setbeenthere; @@ -939,8 +946,9 @@ bool SyncRes::doCacheCheck(const DNSName &qname, const QType &qtype, vector cset; bool found=false, expired=false; vector> signatures; + vector> authorityRecs; uint32_t ttl=0; - if(t_RC->get(d_now.tv_sec, sqname, sqt, d_requireAuthData, &cset, d_requestor, d_doDNSSEC ? &signatures : nullptr, &d_wasVariable, &cachedState) > 0) { + if(t_RC->get(d_now.tv_sec, sqname, sqt, d_requireAuthData, &cset, d_requestor, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &cachedState) > 0) { LOG(prefix<d_content->getZoneRepresentation()); @@ -967,7 +975,13 @@ bool SyncRes::doCacheCheck(const DNSName &qname, const QType &qtype, vector nsecTypes = {QType::NSEC, QType::NSEC3}; + /* Fills the authoritySOA and DNSSECRecords fields from ne with those found in the records * * \param records The records to parse for the authority SOA and NSEC(3) records * \param ne The NegCacheEntry to be filled out (will not be cleared, only appended to */ static void harvestNXRecords(const vector& records, NegCache::NegCacheEntry& ne) { - static const set nsecTypes = {QType::NSEC, QType::NSEC3}; for(const auto& rec : records) { if(rec.d_place != DNSResourceRecord::AUTHORITY) // RFC 4035 section 3.1.3. indicates that NSEC records MUST be placed in @@ -1352,8 +1367,7 @@ vState SyncRes::getValidationStatus(const DNSName& subdomain, unsigned int depth } vector cset; - vector > signatures; - if(t_RC->get(d_now.tv_sec, subdomain, QType(QType::DS), false, &cset, d_requestor, &signatures, nullptr, &result) > 0) { + if(t_RC->get(d_now.tv_sec, subdomain, QType(QType::DS), false, &cset, d_requestor, nullptr, nullptr, nullptr, &result) > 0) { if (result != Indeterminate) { return result; } @@ -1527,7 +1541,7 @@ vState SyncRes::validateRecordsWithSigs(unsigned int depth, const DNSName& name, return Bogus; } -RCode::rcodes_ SyncRes::updateCacheFromRecords(unsigned int depth, LWResult& lwr, const DNSName& qname, const DNSName& auth, bool wasForwarded, const boost::optional ednsmask, vState& state) +RCode::rcodes_ SyncRes::updateCacheFromRecords(unsigned int depth, LWResult& lwr, const DNSName& qname, const DNSName& auth, bool wasForwarded, const boost::optional ednsmask, vState& state, bool& needWildcardProof) { struct CacheEntry { @@ -1553,10 +1567,27 @@ RCode::rcodes_ SyncRes::updateCacheFromRecords(unsigned int depth, LWResult& lwr prefix.append(depth, ' '); } + std::vector> authorityRecs; for(const auto& rec : lwr.d_records) { + if(needWildcardProof) { + if (nsecTypes.count(rec.d_type)) { + authorityRecs.push_back(std::make_shared(rec)); + } + else if (rec.d_type == QType::RRSIG) { + auto rrsig = getRR(rec); + if (rrsig && nsecTypes.count(rrsig->d_type)) { + authorityRecs.push_back(std::make_shared(rec)); + } + } + } if(rec.d_type == QType::RRSIG) { auto rrsig = getRR(rec); if (rrsig) { + if (rec.d_name == qname && rrsig->d_labels < rec.d_name.countLabels()) { + LOG(prefix<d_type)<<" with name '"<d_type, rec.d_place}].signatures.push_back(rrsig); tcache[{rec.d_name, rrsig->d_type, rec.d_place}].signaturesTTL = std::min(tcache[{rec.d_name, rrsig->d_type, rec.d_place}].signaturesTTL, rec.d_ttl); @@ -1615,9 +1646,8 @@ RCode::rcodes_ SyncRes::updateCacheFromRecords(unsigned int depth, LWResult& lwr rec.d_ttl=min(s_maxcachettl, rec.d_ttl); DNSRecord dr(rec); - dr.d_place=DNSResourceRecord::ANSWER; - dr.d_ttl += d_now.tv_sec; + dr.d_place=DNSResourceRecord::ANSWER; tcache[{rec.d_name,rec.d_type,rec.d_place}].records.push_back(dr); } } @@ -1673,7 +1703,7 @@ RCode::rcodes_ SyncRes::updateCacheFromRecords(unsigned int depth, LWResult& lwr } } - t_RC->replace(d_now.tv_sec, i->first.name, QType(i->first.type), i->second.records, i->second.signatures, lwr.d_aabit, i->first.place == DNSResourceRecord::ANSWER ? ednsmask : boost::none, recordState); + t_RC->replace(d_now.tv_sec, i->first.name, QType(i->first.type), i->second.records, i->second.signatures, authorityRecs, lwr.d_aabit, i->first.place == DNSResourceRecord::ANSWER ? ednsmask : boost::none, recordState); if(i->first.place == DNSResourceRecord::ANSWER && ednsmask) d_wasVariable=true; @@ -1709,7 +1739,7 @@ void SyncRes::getDenialValidationState(NegCache::NegCacheEntry& ne, vState& stat } } -bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, const QType& qtype, const DNSName& auth, LWResult& lwr, const bool sendRDQuery, vector& ret, set& nsset, DNSName& newtarget, DNSName& newauth, bool& realreferral, bool& negindic, vState& state) +bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, const QType& qtype, const DNSName& auth, LWResult& lwr, const bool sendRDQuery, vector& ret, set& nsset, DNSName& newtarget, DNSName& newauth, bool& realreferral, bool& negindic, vState& state, bool needWildcardProof) { bool done = false; @@ -1748,10 +1778,13 @@ bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, co newtarget=content->getTarget(); } } - else if((rec.d_type==QType::RRSIG || rec.d_type==QType::NSEC || rec.d_type==QType::NSEC3) && rec.d_place==DNSResourceRecord::ANSWER){ + else if((rec.d_type==QType::RRSIG || rec.d_type==QType::NSEC || rec.d_type==QType::NSEC3) && rec.d_place==DNSResourceRecord::ANSWER) { if(rec.d_type != QType::RRSIG || rec.d_name == qname) ret.push_back(rec); // enjoy your DNSSEC } + else if(needWildcardProof && (rec.d_type==QType::RRSIG || rec.d_type==QType::NSEC || rec.d_type==QType::NSEC3) && rec.d_place==DNSResourceRecord::AUTHORITY) { + ret.push_back(rec); // enjoy your DNSSEC + } // for ANY answers we *must* have an authoritative answer, unless we are forwarding recursively else if(rec.d_place==DNSResourceRecord::ANSWER && rec.d_name == qname && ( @@ -1965,7 +1998,8 @@ bool SyncRes::processAnswer(unsigned int depth, LWResult& lwr, const DNSName& qn } } - *rcode = updateCacheFromRecords(depth, lwr, qname, auth, wasForwarded, ednsmask, state); + bool needWildcardProof = false; + *rcode = updateCacheFromRecords(depth, lwr, qname, auth, wasForwarded, ednsmask, state, needWildcardProof); if (*rcode != RCode::NoError) { return true; } @@ -1977,7 +2011,7 @@ bool SyncRes::processAnswer(unsigned int depth, LWResult& lwr, const DNSName& qn DNSName newauth; DNSName newtarget; - bool done = processRecords(prefix, qname, qtype, auth, lwr, sendRDQuery, ret, nsset, newtarget, newauth, realreferral, negindic, state); + bool done = processRecords(prefix, qname, qtype, auth, lwr, sendRDQuery, ret, nsset, newtarget, newauth, realreferral, negindic, state, needWildcardProof); if(done){ LOG(prefix< retrieveAddressesForNS(const std::string& prefix, const DNSName& qname, vector::const_iterator& tns, const unsigned int depth, set& beenthere, const vector& rnameservers, NsSet& nameservers, bool& sendRDQuery, bool& pierceDontQuery, bool& flawedNSSet); - RCode::rcodes_ updateCacheFromRecords(unsigned int depth, LWResult& lwr, const DNSName& qname, const DNSName& auth, bool wasForwarded, const boost::optional, vState& state); - bool processRecords(const std::string& prefix, const DNSName& qname, const QType& qtype, const DNSName& auth, LWResult& lwr, const bool sendRDQuery, vector& ret, set& nsset, DNSName& newtarget, DNSName& newauth, bool& realreferral, bool& negindic, vState& state); + RCode::rcodes_ updateCacheFromRecords(unsigned int depth, LWResult& lwr, const DNSName& qname, const DNSName& auth, bool wasForwarded, const boost::optional, vState& state, bool& needWildcardProof); + bool processRecords(const std::string& prefix, const DNSName& qname, const QType& qtype, const DNSName& auth, LWResult& lwr, const bool sendRDQuery, vector& ret, set& nsset, DNSName& newtarget, DNSName& newauth, bool& realreferral, bool& negindic, vState& state, bool needWildcardProof); bool doSpecialNamesResolve(const DNSName &qname, const QType &qtype, const uint16_t qclass, vector &ret);