From: Pieter Lexis Date: Fri, 1 Mar 2019 13:59:32 +0000 (+0100) Subject: SyncRes: Process DNAME answers X-Git-Tag: rec-4.2.0-beta1~6^2~19 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=123da0f8bc6e66e63a85cc04d10808891616a882;p=thirdparty%2Fpdns.git SyncRes: Process DNAME answers --- diff --git a/pdns/recursordist/test-syncres_cc.cc b/pdns/recursordist/test-syncres_cc.cc index 8736f32aee..1a336cf22c 100644 --- a/pdns/recursordist/test-syncres_cc.cc +++ b/pdns/recursordist/test-syncres_cc.cc @@ -10792,6 +10792,72 @@ BOOST_AUTO_TEST_CASE(test_records_sanitization_scrubs_ns_nxd) { BOOST_CHECK_LT(t_RC->get(now, DNSName("spoofed.ns."), QType(QType::AAAA), false, &cached, who), 0); } +BOOST_AUTO_TEST_CASE(test_dname_processing) { + std::unique_ptr sr; + initSR(sr); + + primeHints(); + + const DNSName dnameOwner("powerdns.com"); + const DNSName dnameTarget("powerdns.net"); + + const DNSName target("dname.powerdns.com."); + const DNSName cnameTarget("dname.powerdns.net"); + + size_t queries = 0; + + sr->setAsyncCallback([dnameOwner, dnameTarget, target, cnameTarget, &queries](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, LWResult* res, bool* chained) { + queries++; + + if (isRootServer(ip)) { + if (domain.isPartOf(dnameOwner)) { + setLWResult(res, 0, false, false, true); + addRecordToLW(res, dnameOwner, 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; + } + if (domain.isPartOf(dnameTarget)) { + setLWResult(res, 0, false, false, true); + addRecordToLW(res, dnameTarget, QType::NS, "b.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800); + addRecordToLW(res, "b.gtld-servers.net.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600); + return 1; + } + } else if (ip == ComboAddress("192.0.2.1:53")) { + if (domain == target) { + setLWResult(res, 0, true, false, false); + addRecordToLW(res, dnameOwner, QType::DNAME, dnameTarget.toString()); + addRecordToLW(res, domain, QType::CNAME, cnameTarget.toString()); + return 1; + } + } else if (ip == ComboAddress("192.0.2.2:53")) { + if (domain == cnameTarget) { + setLWResult(res, 0, true, false, false); + addRecordToLW(res, domain, QType::A, "192.0.2.2"); + } + return 1; + } + return 0; + }); + + vector ret; + int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret); + + BOOST_CHECK_EQUAL(res, RCode::NoError); + BOOST_REQUIRE_EQUAL(ret.size(), 3); + + BOOST_CHECK_EQUAL(queries, 4); + + BOOST_CHECK(ret[0].d_type == QType::DNAME); + BOOST_CHECK(ret[0].d_name == dnameOwner); + BOOST_CHECK_EQUAL(getRR(ret[0])->getTarget(), dnameTarget); + + BOOST_CHECK(ret[1].d_type == QType::CNAME); + BOOST_CHECK_EQUAL(ret[1].d_name, target); + + BOOST_CHECK(ret[2].d_type == QType::A); + BOOST_CHECK_EQUAL(ret[2].d_name, cnameTarget); +} + /* // cerr<<"asyncresolve called to ask "< SyncRes::s_redirectionQTypes = {QType::CNAME, QType::DNAME}; unsigned int SyncRes::s_maxnegttl; unsigned int SyncRes::s_maxbogusttl; @@ -2079,7 +2080,7 @@ void SyncRes::sanitizeRecords(const std::string& prefix, LWResult& lwr, const DN continue; } - if (rec->d_place == DNSResourceRecord::ANSWER && (qtype != QType::ANY && rec->d_type != qtype.getCode() && rec->d_type != QType::CNAME && rec->d_type != QType::SOA && rec->d_type != QType::RRSIG)) { + if (rec->d_place == DNSResourceRecord::ANSWER && (qtype != QType::ANY && rec->d_type != qtype.getCode() && s_redirectionQTypes.count(rec->d_type) == 0 && rec->d_type != QType::SOA && rec->d_type != QType::RRSIG)) { LOG(prefix<<"Removing irrelevant record '"<d_name<<"|"<d_type)<<"|"<d_content->getZoneRepresentation()<<"' in the "<<(int)rec->d_place<<" section received from "<& ret, set& nsset, DNSName& newtarget, DNSName& newauth, bool& realreferral, bool& negindic, vState& state, const bool needWildcardProof, const unsigned int wildcardLabelsCount) { bool done = false; + DNSName dnameTarget, dnameOwner; for(auto& rec : lwr.d_records) { if (rec.d_type!=QType::OPT && rec.d_class!=QClass::IN) @@ -2513,10 +2515,19 @@ bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, co negindic=true; } - else if(rec.d_place==DNSResourceRecord::ANSWER && rec.d_type==QType::CNAME && (!(qtype==QType(QType::CNAME))) && rec.d_name == qname) { - ret.push_back(rec); - if (auto content = getRR(rec)) { - newtarget=content->getTarget(); + else if(rec.d_place==DNSResourceRecord::ANSWER && s_redirectionQTypes.count(rec.d_type) > 0 && // CNAME or DNAME answer + s_redirectionQTypes.count(qtype.getCode()) == 0) { // But not in response to a CNAME or DNAME query + if (rec.d_type == QType::CNAME && rec.d_name == qname) { + ret.push_back(rec); + if (auto content = getRR(rec)) { + newtarget=content->getTarget(); + } + } else if (rec.d_type == QType::DNAME && qname.isPartOf(rec.d_name)) { // DNAME + ret.push_back(rec); + if (auto content = getRR(rec)) { + dnameOwner = rec.d_name; + dnameTarget = content->getTarget(); + } } } /* if we have a positive answer synthetized from a wildcard, we need to @@ -2571,8 +2582,14 @@ bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, co ret.push_back(rec); } 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) + if(rec.d_type != QType::RRSIG || rec.d_name == qname) { ret.push_back(rec); // enjoy your DNSSEC + } else if(rec.d_type == QType::RRSIG && qname.isPartOf(rec.d_name)) { + auto rrsig = getRR(rec); + if (rrsig != nullptr && rrsig->d_type == QType::DNAME) { + ret.push_back(rec); + } + } } else if(rec.d_place==DNSResourceRecord::AUTHORITY && rec.d_type==QType::NS && qname.isPartOf(rec.d_name)) { if(moreSpecificThan(rec.d_name,auth)) { @@ -2662,6 +2679,14 @@ bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, co } } + if (!dnameTarget.empty() && !newtarget.empty()) { + DNSName substTarget = qname.makeRelative(dnameOwner) + dnameTarget; + if (substTarget != newtarget) { + throw ImmediateServFailException("Received wrong DNAME substitution. qname='" + qname.toLogString() + + "', DNAME owner='" + dnameOwner.toLogString() + "', DNAME target='" + dnameTarget.toLogString() + + "', received CNAME='" + newtarget.toLogString() + "', substituted CNAME='" + substTarget.toLogString() + "'"); + } + } return done; } diff --git a/pdns/syncres.hh b/pdns/syncres.hh index a19f21e54e..27a2e5d6bc 100644 --- a/pdns/syncres.hh +++ b/pdns/syncres.hh @@ -728,6 +728,7 @@ private: static EDNSSubnetOpts s_ecsScopeZero; static LogMode s_lm; static std::unique_ptr s_dontQuery; + const static std::unordered_set s_redirectionQTypes; struct GetBestNSAnswer {