From: Remi Gacogne Date: Fri, 21 Dec 2018 16:10:13 +0000 (+0100) Subject: rec: Support several types for local RPZ records X-Git-Tag: rec-4.2.0-alpha1~37^2~1 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=6da513b25210a0a7ffae529acdd8f5c180b1843a;p=thirdparty%2Fpdns.git rec: Support several types for local RPZ records --- diff --git a/pdns/filterpo.cc b/pdns/filterpo.cc index 90179584ae..38df90cea6 100644 --- a/pdns/filterpo.cc +++ b/pdns/filterpo.cc @@ -196,107 +196,190 @@ void DNSFilterEngine::assureZones(size_t zone) d_zones.resize(zone+1); } -void DNSFilterEngine::Zone::addClientTrigger(const Netmask& nm, Policy pol) +void DNSFilterEngine::Zone::addClientTrigger(const Netmask& nm, Policy&& pol) { pol.d_name = d_name; pol.d_type = PolicyType::ClientIP; - d_qpolAddr.insert(nm).second=pol; + d_qpolAddr.insert(nm).second=std::move(pol); } -void DNSFilterEngine::Zone::addResponseTrigger(const Netmask& nm, Policy pol) +void DNSFilterEngine::Zone::addResponseTrigger(const Netmask& nm, Policy&& pol) { pol.d_name = d_name; pol.d_type = PolicyType::ResponseIP; - d_postpolAddr.insert(nm).second=pol; + d_postpolAddr.insert(nm).second=std::move(pol); } -void DNSFilterEngine::Zone::addQNameTrigger(const DNSName& n, Policy pol) +void DNSFilterEngine::Zone::addQNameTrigger(const DNSName& n, Policy&& pol) { - pol.d_name = d_name; - pol.d_type = PolicyType::QName; - d_qpolName[n]=pol; + auto it = d_qpolName.find(n); + + if (it != d_qpolName.end()) { + auto& existingPol = it->second; + + if (existingPol.d_kind != pol.d_kind) { + throw std::runtime_error("Adding a QName-based filter policy of kind " + getKindToString(existingPol.d_kind) + " but a policy of kind " + getKindToString(existingPol.d_kind) + " already exists for the following QName: " + n.toString()); + } + + if (existingPol.d_kind != PolicyKind::Custom) { + throw std::runtime_error("Adding a QName-based filter policy of kind " + getKindToString(existingPol.d_kind) + " but there was already an existing policy for the following QName: " + n.toString()); + } + + existingPol.d_custom.reserve(existingPol.d_custom.size() + pol.d_custom.size()); + + for (auto& custom : pol.d_custom) { + existingPol.d_custom.emplace_back(std::move(custom)); + } + } + else { + auto& qpol = d_qpolName.insert({n, std::move(pol)}).first->second; + qpol.d_name = d_name; + qpol.d_type = PolicyType::QName; + } } -void DNSFilterEngine::Zone::addNSTrigger(const DNSName& n, Policy pol) +void DNSFilterEngine::Zone::addNSTrigger(const DNSName& n, Policy&& pol) { pol.d_name = d_name; pol.d_type = PolicyType::NSDName; - d_propolName[n]=pol; + d_propolName.insert({n, std::move(pol)}); } -void DNSFilterEngine::Zone::addNSIPTrigger(const Netmask& nm, Policy pol) +void DNSFilterEngine::Zone::addNSIPTrigger(const Netmask& nm, Policy&& pol) { pol.d_name = d_name; pol.d_type = PolicyType::NSIP; - d_propolNSAddr.insert(nm).second = pol; + d_propolNSAddr.insert(nm).second = std::move(pol); } -bool DNSFilterEngine::Zone::rmClientTrigger(const Netmask& nm, Policy& pol) +bool DNSFilterEngine::Zone::rmClientTrigger(const Netmask& nm, const Policy& pol) { d_qpolAddr.erase(nm); return true; } -bool DNSFilterEngine::Zone::rmResponseTrigger(const Netmask& nm, Policy& pol) +bool DNSFilterEngine::Zone::rmResponseTrigger(const Netmask& nm, const Policy& pol) { d_postpolAddr.erase(nm); return true; } -bool DNSFilterEngine::Zone::rmQNameTrigger(const DNSName& n, Policy& pol) +bool DNSFilterEngine::Zone::rmQNameTrigger(const DNSName& n, const Policy& pol) { - d_qpolName.erase(n); // XXX verify we had identical policy? - return true; + auto it = d_qpolName.find(n); + if (it == d_qpolName.end()) { + return false; + } + + auto& existing = it->second; + if (existing.d_kind != DNSFilterEngine::PolicyKind::Custom) { + d_qpolName.erase(it); + return true; + } + + /* for custom types, we might have more than one type, + and then we need to remove only the right ones. */ + if (existing.d_custom.size() <= 1) { + d_qpolName.erase(it); + return true; + } + + bool result = false; + for (auto& toremove : pol.d_custom) { + for (auto it = existing.d_custom.begin(); it != existing.d_custom.end(); ++it) { + if (**it == *toremove) { + existing.d_custom.erase(it); + result = true; + break; + } + } + } + + return result; } -bool DNSFilterEngine::Zone::rmNSTrigger(const DNSName& n, Policy& pol) +bool DNSFilterEngine::Zone::rmNSTrigger(const DNSName& n, const Policy& pol) { d_propolName.erase(n); // XXX verify policy matched? =pol; return true; } -bool DNSFilterEngine::Zone::rmNSIPTrigger(const Netmask& nm, Policy& pol) +bool DNSFilterEngine::Zone::rmNSIPTrigger(const Netmask& nm, const Policy& pol) { d_propolNSAddr.erase(nm); return true; } -DNSRecord DNSFilterEngine::Policy::getCustomRecord(const DNSName& qname) const +DNSRecord DNSFilterEngine::Policy::getRecordFromCustom(const DNSName& qname, const std::shared_ptr& custom) const +{ + DNSRecord dr; + dr.d_name = qname; + dr.d_type = custom->getType(); + dr.d_ttl = d_ttl; + dr.d_class = QClass::IN; + dr.d_place = DNSResourceRecord::ANSWER; + dr.d_content = custom; + + if (dr.d_type == QType::CNAME) { + const auto content = std::dynamic_pointer_cast(custom); + if (content) { + DNSName target = content->getTarget(); + if (target.isWildcard()) { + target.chopOff(); + dr.d_content = std::make_shared(qname + target); + } + } + } + + return dr; +} + +std::vector DNSFilterEngine::Policy::getCustomRecords(const DNSName& qname, uint16_t qtype) const { if (d_kind != PolicyKind::Custom) { throw std::runtime_error("Asking for a custom record from a filtering policy of a non-custom type"); } - DNSRecord result; - result.d_name = qname; - result.d_type = d_custom->getType(); - result.d_ttl = d_ttl; - result.d_class = QClass::IN; - result.d_place = DNSResourceRecord::ANSWER; - result.d_content = d_custom; + std::vector result; - if (result.d_type == QType::CNAME) { - const auto content = std::dynamic_pointer_cast(d_custom); - if (content) { - DNSName target = content->getTarget(); - if (target.isWildcard()) { - target.chopOff(); - result.d_content = std::make_shared(qname + target); + for (const auto& custom : d_custom) { + if (qtype != QType::ANY && qtype != custom->getType() && custom->getType() != QType::CNAME) { + continue; + } + + DNSRecord dr; + dr.d_name = qname; + dr.d_type = custom->getType(); + dr.d_ttl = d_ttl; + dr.d_class = QClass::IN; + dr.d_place = DNSResourceRecord::ANSWER; + dr.d_content = custom; + + if (dr.d_type == QType::CNAME) { + const auto content = std::dynamic_pointer_cast(custom); + if (content) { + DNSName target = content->getTarget(); + if (target.isWildcard()) { + target.chopOff(); + dr.d_content = std::make_shared(qname + target); + } } } + + result.emplace_back(getRecordFromCustom(qname, custom)); } return result; } -std::string DNSFilterEngine::Policy::getKindToString() const +std::string DNSFilterEngine::getKindToString(DNSFilterEngine::PolicyKind kind) { static const DNSName drop("rpz-drop."), truncate("rpz-tcp-only."), noaction("rpz-passthru."); static const DNSName rpzClientIP("rpz-client-ip"), rpzIP("rpz-ip"), rpzNSDname("rpz-nsdname"), rpzNSIP("rpz-nsip."); static const std::string rpzPrefix("rpz-"); - switch(d_kind) { + switch(kind) { case DNSFilterEngine::PolicyKind::NoAction: return noaction.toString(); case DNSFilterEngine::PolicyKind::Drop: @@ -312,28 +395,52 @@ std::string DNSFilterEngine::Policy::getKindToString() const } } -DNSRecord DNSFilterEngine::Policy::getRecord(const DNSName& qname) const +std::string DNSFilterEngine::getTypeToString(DNSFilterEngine::PolicyType type) { - DNSRecord dr; + switch(type) { + case DNSFilterEngine::PolicyType::None: + return "none"; + case DNSFilterEngine::PolicyType::QName: + return "QName"; + case DNSFilterEngine::PolicyType::ClientIP: + return "Client IP"; + case DNSFilterEngine::PolicyType::ResponseIP: + return "Response IP"; + case DNSFilterEngine::PolicyType::NSDName: + return "Name Server Name"; + case DNSFilterEngine::PolicyType::NSIP: + return "Name Server IP"; + default: + throw std::runtime_error("Unexpected DNSFilterEngine::Policy type"); + } +} + +std::vector DNSFilterEngine::Policy::getRecords(const DNSName& qname) const +{ + std::vector result; if (d_kind == PolicyKind::Custom) { - dr = getCustomRecord(qname); + result = getCustomRecords(qname, QType::ANY); } else { + DNSRecord dr; dr.d_name = qname; dr.d_ttl = static_cast(d_ttl); dr.d_type = QType::CNAME; dr.d_class = QClass::IN; - dr.d_content = DNSRecordContent::mastermake(QType::CNAME, QClass::IN, getKindToString()); + dr.d_content = DNSRecordContent::mastermake(QType::CNAME, QClass::IN, getKindToString(d_kind)); + result.push_back(std::move(dr)); } - return dr; + return result; } void DNSFilterEngine::Zone::dumpNamedPolicy(FILE* fp, const DNSName& name, const Policy& pol) const { - DNSRecord dr = pol.getRecord(name); - fprintf(fp, "%s %" PRIu32 " IN %s %s\n", dr.d_name.toString().c_str(), dr.d_ttl, QType(dr.d_type).getName().c_str(), dr.d_content->getZoneRepresentation().c_str()); + auto records = pol.getRecords(name); + for (const auto& dr : records) { + fprintf(fp, "%s %" PRIu32 " IN %s %s\n", dr.d_name.toString().c_str(), dr.d_ttl, QType(dr.d_type).getName().c_str(), dr.d_content->getZoneRepresentation().c_str()); + } } DNSName DNSFilterEngine::Zone::maskToRPZ(const Netmask& nm) @@ -386,8 +493,10 @@ void DNSFilterEngine::Zone::dumpAddrPolicy(FILE* fp, const Netmask& nm, const DN DNSName full = maskToRPZ(nm); full += name; - DNSRecord dr = pol.getRecord(full); - fprintf(fp, "%s %" PRIu32 " IN %s %s\n", dr.d_name.toString().c_str(), dr.d_ttl, QType(dr.d_type).getName().c_str(), dr.d_content->getZoneRepresentation().c_str()); + auto records = pol.getRecords(full); + for (const auto& dr : records) { + fprintf(fp, "%s %" PRIu32 " IN %s %s\n", dr.d_name.toString().c_str(), dr.d_ttl, QType(dr.d_type).getName().c_str(), dr.d_content->getZoneRepresentation().c_str()); + } } void DNSFilterEngine::Zone::dump(FILE* fp) const diff --git a/pdns/filterpo.hh b/pdns/filterpo.hh index f07d37102e..22900cb37d 100644 --- a/pdns/filterpo.hh +++ b/pdns/filterpo.hh @@ -68,25 +68,36 @@ public: enum class PolicyKind { NoAction, Drop, NXDOMAIN, NODATA, Truncate, Custom}; enum class PolicyType { None, QName, ClientIP, ResponseIP, NSDName, NSIP }; + static std::string getKindToString(PolicyKind kind); + static std::string getTypeToString(PolicyType type); + struct Policy { - Policy(): d_custom(nullptr), d_name(nullptr), d_kind(PolicyKind::NoAction), d_type(PolicyType::None), d_ttl(0) + Policy(): d_name(nullptr), d_kind(PolicyKind::NoAction), d_type(PolicyType::None), d_ttl(0) + { + } + + Policy(PolicyKind kind, PolicyType type, int32_t ttl=0, std::shared_ptr name=nullptr, const std::vector>& custom={}): d_custom(custom), d_name(name), d_kind(kind), d_type(type), d_ttl(ttl) { } + bool operator==(const Policy& rhs) const { - return d_kind == rhs.d_kind; // XXX check d_custom too! + return d_kind == rhs.d_kind && d_type == rhs.d_type && d_ttl == rhs.d_ttl && d_custom == rhs.d_custom; } - std::string getKindToString() const; - DNSRecord getCustomRecord(const DNSName& qname) const; - DNSRecord getRecord(const DNSName& qname) const; + std::vector getCustomRecords(const DNSName& qname, uint16_t qtype) const; + std::vector getRecords(const DNSName& qname) const; - std::shared_ptr d_custom; - std::shared_ptr d_name; + std::vector> d_custom; + std::shared_ptr d_name; // the name of the policy PolicyKind d_kind; PolicyType d_type; + /* Yup, we are currently using the same TTL for every record for a given name */ int32_t d_ttl; - }; + + private: + DNSRecord getRecordFromCustom(const DNSName& qname, const std::shared_ptr& custom) const; +}; class Zone { public: @@ -133,6 +144,11 @@ public: return d_refresh; } + uint32_t getSerial() const + { + return d_serial; + } + size_t size() const { return d_qpolAddr.size() + d_postpolAddr.size() + d_propolName.size() + d_propolNSAddr.size() + d_qpolName.size(); @@ -141,17 +157,17 @@ public: void dump(FILE * fp) const; - void addClientTrigger(const Netmask& nm, Policy pol); - void addQNameTrigger(const DNSName& nm, Policy pol); - void addNSTrigger(const DNSName& dn, Policy pol); - void addNSIPTrigger(const Netmask& nm, Policy pol); - void addResponseTrigger(const Netmask& nm, Policy pol); - - bool rmClientTrigger(const Netmask& nm, Policy& pol); - bool rmQNameTrigger(const DNSName& nm, Policy& pol); - bool rmNSTrigger(const DNSName& dn, Policy& pol); - bool rmNSIPTrigger(const Netmask& nm, Policy& pol); - bool rmResponseTrigger(const Netmask& nm, Policy& pol); + void addClientTrigger(const Netmask& nm, Policy&& pol); + void addQNameTrigger(const DNSName& nm, Policy&& pol); + void addNSTrigger(const DNSName& dn, Policy&& pol); + void addNSIPTrigger(const Netmask& nm, Policy&& pol); + void addResponseTrigger(const Netmask& nm, Policy&& pol); + + bool rmClientTrigger(const Netmask& nm, const Policy& pol); + bool rmQNameTrigger(const DNSName& nm, const Policy& pol); + bool rmNSTrigger(const DNSName& dn, const Policy& pol); + bool rmNSIPTrigger(const Netmask& nm, const Policy& pol); + bool rmResponseTrigger(const Netmask& nm, const Policy& pol); bool findQNamePolicy(const DNSName& qname, DNSFilterEngine::Policy& pol) const; bool findNSPolicy(const DNSName& qname, DNSFilterEngine::Policy& pol) const; diff --git a/pdns/lua-recursor4.cc b/pdns/lua-recursor4.cc index d0f1af10a2..e6afc0a5c3 100644 --- a/pdns/lua-recursor4.cc +++ b/pdns/lua-recursor4.cc @@ -253,13 +253,24 @@ void RecursorLua4::postPrepareContext() d_lw->registerMember("policyTTL", &DNSFilterEngine::Policy::d_ttl); d_lw->registerMember("policyCustom", [](const DNSFilterEngine::Policy& pol) -> std::string { - if(pol.d_custom) - return pol.d_custom->getZoneRepresentation(); - return std::string(); + std::string result; + if (pol.d_kind != DNSFilterEngine::PolicyKind::Custom) { + return result; + } + + for (const auto& dr : pol.d_custom) { + if (!result.empty()) { + result += "\n"; + } + result += dr->getZoneRepresentation(); + } + + return result; }, [](DNSFilterEngine::Policy& pol, const std::string& content) { // Only CNAMES for now, when we ever add a d_custom_type, there will be pain - pol.d_custom = DNSRecordContent::mastermake(QType::CNAME, 1, content); + pol.d_custom.clear(); + pol.d_custom.push_back(DNSRecordContent::mastermake(QType::CNAME, QClass::IN, content)); } ); d_lw->registerFunction("getDH", &DNSQuestion::getDH); diff --git a/pdns/pdns_recursor.cc b/pdns/pdns_recursor.cc index 39028c183c..3e7044c239 100644 --- a/pdns/pdns_recursor.cc +++ b/pdns/pdns_recursor.cc @@ -838,7 +838,7 @@ static void handleRPZCustom(const DNSRecord& spoofed, const QType& qtype, SyncRe bool oldWantsRPZ = sr.getWantsRPZ(); sr.setWantsRPZ(false); vector ans; - res = sr.beginResolve(DNSName(spoofed.d_content->getZoneRepresentation()), qtype, 1, ans); + res = sr.beginResolve(DNSName(spoofed.d_content->getZoneRepresentation()), qtype, QClass::IN, ans); for (const auto& rec : ans) { if(rec.d_place == DNSResourceRecord::ANSWER) { ret.push_back(rec); @@ -1145,7 +1145,7 @@ static void startDoResolve(void *p) /* preresolve expects res (dq.rcode) to be set to RCode::NoError by default */ int res = RCode::NoError; DNSFilterEngine::Policy appliedPolicy; - DNSRecord spoofed; + std::vector spoofed; RecursorLua4::DNSQuestion dq(dc->d_source, dc->d_destination, dc->d_mdp.d_qname, dc->d_mdp.d_qtype, dc->d_tcp, variableAnswer, wantsRPZ, logResponse); dq.ednsFlags = &edo.d_extFlags; dq.ednsOptions = &ednsOpts; @@ -1223,9 +1223,11 @@ static void startDoResolve(void *p) case DNSFilterEngine::PolicyKind::Custom: g_stats.policyResults[appliedPolicy.d_kind]++; res=RCode::NoError; - spoofed=appliedPolicy.getCustomRecord(dc->d_mdp.d_qname); - ret.push_back(spoofed); - handleRPZCustom(spoofed, QType(dc->d_mdp.d_qtype), sr, res, ret); + spoofed=appliedPolicy.getCustomRecords(dc->d_mdp.d_qname, dc->d_mdp.d_qtype); + for (const auto& dr : spoofed) { + ret.push_back(dr); + handleRPZCustom(dr, QType(dc->d_mdp.d_qtype), sr, res, ret); + } goto haveAnswer; case DNSFilterEngine::PolicyKind::Truncate: if(!dc->d_tcp) { @@ -1283,9 +1285,11 @@ static void startDoResolve(void *p) case DNSFilterEngine::PolicyKind::Custom: ret.clear(); res=RCode::NoError; - spoofed=appliedPolicy.getCustomRecord(dc->d_mdp.d_qname); - ret.push_back(spoofed); - handleRPZCustom(spoofed, QType(dc->d_mdp.d_qtype), sr, res, ret); + spoofed=appliedPolicy.getCustomRecords(dc->d_mdp.d_qname, dc->d_mdp.d_qtype); + for (const auto& dr : spoofed) { + ret.push_back(dr); + handleRPZCustom(dr, QType(dc->d_mdp.d_qtype), sr, res, ret); + } goto haveAnswer; } } @@ -1341,9 +1345,11 @@ static void startDoResolve(void *p) case DNSFilterEngine::PolicyKind::Custom: ret.clear(); res=RCode::NoError; - spoofed=appliedPolicy.getCustomRecord(dc->d_mdp.d_qname); - ret.push_back(spoofed); - handleRPZCustom(spoofed, QType(dc->d_mdp.d_qtype), sr, res, ret); + spoofed=appliedPolicy.getCustomRecords(dc->d_mdp.d_qname, dc->d_mdp.d_qtype); + for (const auto& dr : spoofed) { + ret.push_back(dr); + handleRPZCustom(dr, QType(dc->d_mdp.d_qtype), sr, res, ret); + } goto haveAnswer; } } diff --git a/pdns/rec-lua-conf.cc b/pdns/rec-lua-conf.cc index 125d1a043e..a94c992a2f 100644 --- a/pdns/rec-lua-conf.cc +++ b/pdns/rec-lua-conf.cc @@ -62,10 +62,8 @@ static void parseRPZParameters(const std::unordered_mapd_kind = (DNSFilterEngine::PolicyKind)boost::get(constGet(have, "defpol")); defpol->d_name = std::make_shared(polName); if(defpol->d_kind == DNSFilterEngine::PolicyKind::Custom) { - defpol->d_custom= - DNSRecordContent::mastermake(QType::CNAME, 1, - boost::get(constGet(have,"defcontent")) - ); + defpol->d_custom.push_back(DNSRecordContent::mastermake(QType::CNAME, QClass::IN, + boost::get(constGet(have,"defcontent")))); if(have.count("defttl")) defpol->d_ttl = static_cast(boost::get(constGet(have, "defttl"))); diff --git a/pdns/recursordist/Makefile.am b/pdns/recursordist/Makefile.am index cca58a8ad8..d87c7df2b3 100644 --- a/pdns/recursordist/Makefile.am +++ b/pdns/recursordist/Makefile.am @@ -256,6 +256,7 @@ testrunner_SOURCES = \ test-dnsparser_hh.cc \ test-dnsrecords_cc.cc \ test-ednsoptions_cc.cc \ + test-filterpo_cc.cc \ test-iputils_hh.cc \ test-ixfr_cc.cc \ test-misc_hh.cc \ diff --git a/pdns/recursordist/test-filterpo_cc.cc b/pdns/recursordist/test-filterpo_cc.cc new file mode 100644 index 0000000000..fbfa184a07 --- /dev/null +++ b/pdns/recursordist/test-filterpo_cc.cc @@ -0,0 +1,358 @@ + +#define BOOST_TEST_DYN_LINK +#define BOOST_TEST_NO_MAIN + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include + +#include "dnsrecords.hh" +#include "filterpo.hh" + +BOOST_AUTO_TEST_CASE(test_filter_policies_basic) { + DNSFilterEngine dfe; + + std::string zoneName("Unit test policy 0"); + auto zone = std::make_shared(); + zone->setName(zoneName); + BOOST_CHECK_EQUAL(*(zone->getName()), zoneName); + zone->setDomain(DNSName("powerdns.com.")); + BOOST_CHECK_EQUAL(zone->getDomain(), DNSName("powerdns.com.")); + zone->setSerial(42); + BOOST_CHECK_EQUAL(zone->getSerial(), 42); + zone->setRefresh(99); + BOOST_CHECK_EQUAL(zone->getRefresh(), 99); + + const ComboAddress nsIP("192.0.2.1"); + const DNSName nsName("ns.bad.wolf."); + const ComboAddress clientIP("192.0.2.128"); + const DNSName blockedName("blocked."); + const ComboAddress responseIP("192.0.2.254"); + BOOST_CHECK_EQUAL(zone->size(), 0); + zone->addClientTrigger(Netmask(clientIP, 32), DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Drop, DNSFilterEngine::PolicyType::ClientIP)); + BOOST_CHECK_EQUAL(zone->size(), 1); + zone->addQNameTrigger(blockedName, DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Drop, DNSFilterEngine::PolicyType::QName)); + BOOST_CHECK_EQUAL(zone->size(), 2); + zone->addNSIPTrigger(Netmask(nsIP, 32), DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Drop, DNSFilterEngine::PolicyType::NSIP)); + BOOST_CHECK_EQUAL(zone->size(), 3); + zone->addNSTrigger(nsName, DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Drop, DNSFilterEngine::PolicyType::NSDName)); + BOOST_CHECK_EQUAL(zone->size(), 4); + zone->addResponseTrigger(Netmask(responseIP, 32), DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Drop, DNSFilterEngine::PolicyType::ResponseIP)); + BOOST_CHECK_EQUAL(zone->size(), 5); + + size_t zoneIdx = dfe.addZone(zone); + + BOOST_CHECK_EQUAL(dfe.size(), 1); + BOOST_CHECK(dfe.getZone(zoneName) == zone); + BOOST_CHECK(dfe.getZone(zoneIdx) == zone); + + dfe.setZone(zoneIdx, zone); + + BOOST_CHECK_EQUAL(dfe.size(), 1); + BOOST_CHECK(dfe.getZone(zoneName) == zone); + BOOST_CHECK(dfe.getZone(zoneIdx) == zone); + + { + /* blocked NS name */ + const auto matchingPolicy = dfe.getProcessingPolicy(nsName, std::unordered_map()); + BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::NSDName); + BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Drop); + DNSFilterEngine::Policy zonePolicy; + BOOST_CHECK(zone->findNSPolicy(nsName, zonePolicy)); + BOOST_CHECK(zonePolicy == matchingPolicy); + } + + { + /* allowed NS name */ + const auto matchingPolicy = dfe.getProcessingPolicy(DNSName("ns.bad.rabbit."), std::unordered_map()); + BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None); + DNSFilterEngine::Policy zonePolicy; + BOOST_CHECK(zone->findNSPolicy(DNSName("ns.bad.rabbit."), zonePolicy) == false); + } + + { + /* blocked NS IP */ + const auto matchingPolicy = dfe.getProcessingPolicy(nsIP, std::unordered_map()); + BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::NSIP); + BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Drop); + DNSFilterEngine::Policy zonePolicy; + BOOST_CHECK(zone->findNSIPPolicy(nsIP, zonePolicy)); + BOOST_CHECK(zonePolicy == matchingPolicy); + } + + { + /* allowed NS IP */ + const auto matchingPolicy = dfe.getProcessingPolicy(ComboAddress("192.0.2.142"), std::unordered_map()); + BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None); + DNSFilterEngine::Policy zonePolicy; + BOOST_CHECK(zone->findNSIPPolicy(ComboAddress("192.0.2.142"), zonePolicy) == false); + } + + { + /* blocked qname */ + const auto matchingPolicy = dfe.getQueryPolicy(blockedName, ComboAddress("192.0.2.142"), std::unordered_map()); + BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName); + BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Drop); + DNSFilterEngine::Policy zonePolicy; + BOOST_CHECK(zone->findQNamePolicy(blockedName, zonePolicy)); + BOOST_CHECK(zonePolicy == matchingPolicy); + } + + { + /* blocked client IP */ + const auto matchingPolicy = dfe.getQueryPolicy(DNSName("totally.legit."), clientIP, std::unordered_map()); + BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::ClientIP); + BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Drop); + DNSFilterEngine::Policy zonePolicy; + BOOST_CHECK(zone->findClientPolicy(clientIP, zonePolicy)); + BOOST_CHECK(zonePolicy == matchingPolicy); + } + + { + /* not blocked */ + const auto matchingPolicy = dfe.getQueryPolicy(DNSName("totally.legit."), ComboAddress("192.0.2.142"), std::unordered_map()); + BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None); + DNSFilterEngine::Policy zonePolicy; + BOOST_CHECK(zone->findClientPolicy(ComboAddress("192.0.2.142"), zonePolicy) == false); + BOOST_CHECK(zone->findQNamePolicy(DNSName("totally.legit."), zonePolicy) == false); + } + + { + /* blocked A */ + DNSRecord dr; + dr.d_type = QType::A; + dr.d_content = DNSRecordContent::mastermake(QType::A, QClass::IN, responseIP.toString()); + const auto matchingPolicy = dfe.getPostPolicy({ dr }, std::unordered_map()); + BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::ResponseIP); + BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Drop); + DNSFilterEngine::Policy zonePolicy; + BOOST_CHECK(zone->findResponsePolicy(responseIP, zonePolicy)); + BOOST_CHECK(zonePolicy == matchingPolicy); + } + + { + /* allowed A */ + DNSRecord dr; + dr.d_type = QType::A; + dr.d_content = DNSRecordContent::mastermake(QType::A, QClass::IN, "192.0.2.142"); + const auto matchingPolicy = dfe.getPostPolicy({ dr }, std::unordered_map()); + BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None); + DNSFilterEngine::Policy zonePolicy; + BOOST_CHECK(zone->findResponsePolicy(ComboAddress("192.0.2.142"), zonePolicy) == false); + } + + BOOST_CHECK_EQUAL(zone->size(), 5); + zone->rmClientTrigger(Netmask(clientIP, 32), DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Drop, DNSFilterEngine::PolicyType::ClientIP)); + BOOST_CHECK_EQUAL(zone->size(), 4); + zone->rmQNameTrigger(blockedName, DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Drop, DNSFilterEngine::PolicyType::QName)); + BOOST_CHECK_EQUAL(zone->size(), 3); + zone->rmNSIPTrigger(Netmask(nsIP, 32), DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Drop, DNSFilterEngine::PolicyType::NSIP)); + BOOST_CHECK_EQUAL(zone->size(), 2); + zone->rmNSTrigger(nsName, DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Drop, DNSFilterEngine::PolicyType::NSDName)); + BOOST_CHECK_EQUAL(zone->size(), 1); + zone->rmResponseTrigger(Netmask(responseIP, 32), DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Drop, DNSFilterEngine::PolicyType::ResponseIP)); + BOOST_CHECK_EQUAL(zone->size(), 0); + + /* DNSFilterEngine::clear() calls clear() on all zones, but keeps the zones */ + dfe.clear(); + BOOST_CHECK_EQUAL(dfe.size(), 1); + BOOST_CHECK(dfe.getZone(zoneName) == zone); + BOOST_CHECK(dfe.getZone(zoneIdx) == zone); +} + +BOOST_AUTO_TEST_CASE(test_filter_policies_local_data) { + DNSFilterEngine dfe; + + std::string zoneName("Unit test policy local data"); + auto zone = std::make_shared(); + zone->setName(zoneName); + + const DNSName bad1("bad1.example.com."); + const DNSName bad2("bad2.example.com."); + + zone->addQNameTrigger(bad1, DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Custom, DNSFilterEngine::PolicyType::QName, 0, nullptr, { DNSRecordContent::mastermake(QType::CNAME, QClass::IN, "garden.example.net.") } )); + BOOST_CHECK_EQUAL(zone->size(), 1); + + zone->addQNameTrigger(bad2, DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Custom, DNSFilterEngine::PolicyType::QName, 0, nullptr, { DNSRecordContent::mastermake(QType::A, QClass::IN, "192.0.2.1") } )); + BOOST_CHECK_EQUAL(zone->size(), 2); + + zone->addQNameTrigger(bad2, DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Custom, DNSFilterEngine::PolicyType::QName, 0, nullptr, { DNSRecordContent::mastermake(QType::A, QClass::IN, "192.0.2.2") } )); + BOOST_CHECK_EQUAL(zone->size(), 2); + + zone->addQNameTrigger(bad2, DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Custom, DNSFilterEngine::PolicyType::QName, 0, nullptr, { DNSRecordContent::mastermake(QType::MX, QClass::IN, "10 garden-mail.example.net.") } )); + BOOST_CHECK_EQUAL(zone->size(), 2); + + dfe.addZone(zone); + + { + /* exact type does not exist, but we have a CNAME */ + const auto matchingPolicy = dfe.getQueryPolicy(bad1, ComboAddress("192.0.2.142"), std::unordered_map()); + BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName); + BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom); + auto records = matchingPolicy.getCustomRecords(bad1, QType::A); + BOOST_CHECK_EQUAL(records.size(), 1); + const auto& record = records.at(0); + BOOST_CHECK(record.d_type == QType::CNAME); + BOOST_CHECK(record.d_class == QClass::IN); + auto content = std::dynamic_pointer_cast(record.d_content); + BOOST_CHECK(content != nullptr); + BOOST_CHECK_EQUAL(content->getTarget().toString(), "garden.example.net."); + } + + { + /* exact type exists */ + const auto matchingPolicy = dfe.getQueryPolicy(bad2, ComboAddress("192.0.2.142"), std::unordered_map()); + BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName); + BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom); + + { + auto records = matchingPolicy.getCustomRecords(bad2, QType::A); + BOOST_REQUIRE_EQUAL(records.size(), 2); + { + const auto& record = records.at(0); + BOOST_CHECK(record.d_type == QType::A); + BOOST_CHECK(record.d_class == QClass::IN); + auto content = std::dynamic_pointer_cast(record.d_content); + BOOST_CHECK(content != nullptr); + BOOST_CHECK_EQUAL(content->getCA().toString(), "192.0.2.1"); + } + { + const auto& record = records.at(1); + BOOST_CHECK(record.d_type == QType::A); + BOOST_CHECK(record.d_class == QClass::IN); + auto content = std::dynamic_pointer_cast(record.d_content); + BOOST_CHECK(content != nullptr); + BOOST_CHECK_EQUAL(content->getCA().toString(), "192.0.2.2"); + } + } + + { + auto records = matchingPolicy.getCustomRecords(bad2, QType::MX); + BOOST_CHECK_EQUAL(records.size(), 1); + const auto& record = records.at(0); + BOOST_CHECK(record.d_type == QType::MX); + BOOST_CHECK(record.d_class == QClass::IN); + auto content = std::dynamic_pointer_cast(record.d_content); + BOOST_CHECK(content != nullptr); + BOOST_CHECK_EQUAL(content->d_mxname.toString(), "garden-mail.example.net."); + } + + { + /* the name exists but there is no CNAME nor matching type, so NODATA */ + auto records = matchingPolicy.getCustomRecords(bad2, QType::AAAA); + BOOST_CHECK_EQUAL(records.size(), 0); + } + } + + /* remove only one entry, one of the A local records */ + zone->rmQNameTrigger(bad2, DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Custom, DNSFilterEngine::PolicyType::QName, 0, nullptr, { DNSRecordContent::mastermake(QType::A, QClass::IN, "192.0.2.1") } )); + BOOST_CHECK_EQUAL(zone->size(), 2); + + { + /* exact type exists */ + const auto matchingPolicy = dfe.getQueryPolicy(bad2, ComboAddress("192.0.2.142"), std::unordered_map()); + BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName); + BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom); + + { + auto records = matchingPolicy.getCustomRecords(bad2, QType::A); + BOOST_REQUIRE_EQUAL(records.size(), 1); + { + const auto& record = records.at(0); + BOOST_CHECK(record.d_type == QType::A); + BOOST_CHECK(record.d_class == QClass::IN); + auto content = std::dynamic_pointer_cast(record.d_content); + BOOST_CHECK(content != nullptr); + BOOST_CHECK_EQUAL(content->getCA().toString(), "192.0.2.2"); + } + } + + { + auto records = matchingPolicy.getCustomRecords(bad2, QType::MX); + BOOST_CHECK_EQUAL(records.size(), 1); + const auto& record = records.at(0); + BOOST_CHECK(record.d_type == QType::MX); + BOOST_CHECK(record.d_class == QClass::IN); + auto content = std::dynamic_pointer_cast(record.d_content); + BOOST_CHECK(content != nullptr); + BOOST_CHECK_EQUAL(content->d_mxname.toString(), "garden-mail.example.net."); + } + + { + /* the name exists but there is no CNAME nor matching type, so NODATA */ + auto records = matchingPolicy.getCustomRecords(bad2, QType::AAAA); + BOOST_CHECK_EQUAL(records.size(), 0); + } + } +} + +BOOST_AUTO_TEST_CASE(test_multiple_filter_policies) { + DNSFilterEngine dfe; + + auto zone1 = std::make_shared(); + zone1->setName("Unit test policy 0"); + + auto zone2 = std::make_shared(); + zone2->setName("Unit test policy 1"); + + const DNSName bad("bad.example.com."); + + zone1->addQNameTrigger(bad, DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Custom, DNSFilterEngine::PolicyType::QName, 0, nullptr, { DNSRecordContent::mastermake(QType::CNAME, QClass::IN, "garden1.example.net.") } )); + zone2->addQNameTrigger(bad, DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Custom, DNSFilterEngine::PolicyType::QName, 0, nullptr, { DNSRecordContent::mastermake(QType::CNAME, QClass::IN, "garden2.example.net.") } )); + + dfe.addZone(zone1); + dfe.addZone(zone2); + + { + /* zone 1 should match first */ + const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.142"), std::unordered_map()); + BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName); + BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom); + auto records = matchingPolicy.getCustomRecords(bad, QType::A); + BOOST_CHECK_EQUAL(records.size(), 1); + const auto& record = records.at(0); + BOOST_CHECK(record.d_type == QType::CNAME); + BOOST_CHECK(record.d_class == QClass::IN); + auto content = std::dynamic_pointer_cast(record.d_content); + BOOST_CHECK(content != nullptr); + BOOST_CHECK_EQUAL(content->getTarget().toString(), "garden1.example.net."); + } + + { + /* zone 1 should still match if zone 2 has been disabled */ + const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.142"), { { *(zone2->getName()), true } }); + BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName); + BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom); + auto records = matchingPolicy.getCustomRecords(bad, QType::A); + BOOST_CHECK_EQUAL(records.size(), 1); + const auto& record = records.at(0); + BOOST_CHECK(record.d_type == QType::CNAME); + BOOST_CHECK(record.d_class == QClass::IN); + auto content = std::dynamic_pointer_cast(record.d_content); + BOOST_CHECK(content != nullptr); + BOOST_CHECK_EQUAL(content->getTarget().toString(), "garden1.example.net."); + } + + { + /* if zone 1 is disabled, zone 2 should match */ + const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.142"), { { *(zone1->getName()), true } }); + BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName); + BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom); + auto records = matchingPolicy.getCustomRecords(bad, QType::A); + BOOST_CHECK_EQUAL(records.size(), 1); + const auto& record = records.at(0); + BOOST_CHECK(record.d_type == QType::CNAME); + BOOST_CHECK(record.d_class == QClass::IN); + auto content = std::dynamic_pointer_cast(record.d_content); + BOOST_CHECK(content != nullptr); + BOOST_CHECK_EQUAL(content->getTarget().toString(), "garden2.example.net."); + } + + { + /* if both zones are disabled, we should not match */ + const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.142"), { { *(zone1->getName()), true }, { *(zone2->getName()), true } }); + BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None); + } + +} diff --git a/pdns/recursordist/test-syncres_cc.cc b/pdns/recursordist/test-syncres_cc.cc index 5a361b15fe..783dfefabc 100644 --- a/pdns/recursordist/test-syncres_cc.cc +++ b/pdns/recursordist/test-syncres_cc.cc @@ -2797,7 +2797,7 @@ BOOST_AUTO_TEST_CASE(test_nameserver_ipv4_rpz) { pol.d_kind = DNSFilterEngine::PolicyKind::Drop; std::shared_ptr zone = std::make_shared(); zone->setName("Unit test policy 0"); - zone->addNSIPTrigger(Netmask(ns, 32), pol); + zone->addNSIPTrigger(Netmask(ns, 32), std::move(pol)); auto luaconfsCopy = g_luaconfs.getCopy(); luaconfsCopy.dfe.addZone(zone); g_luaconfs.setState(luaconfsCopy); @@ -2838,7 +2838,7 @@ BOOST_AUTO_TEST_CASE(test_nameserver_ipv6_rpz) { pol.d_kind = DNSFilterEngine::PolicyKind::Drop; std::shared_ptr zone = std::make_shared(); zone->setName("Unit test policy 0"); - zone->addNSIPTrigger(Netmask(ns, 128), pol); + zone->addNSIPTrigger(Netmask(ns, 128), std::move(pol)); auto luaconfsCopy = g_luaconfs.getCopy(); luaconfsCopy.dfe.addZone(zone); g_luaconfs.setState(luaconfsCopy); @@ -2880,7 +2880,7 @@ BOOST_AUTO_TEST_CASE(test_nameserver_name_rpz) { pol.d_kind = DNSFilterEngine::PolicyKind::Drop; std::shared_ptr zone = std::make_shared(); zone->setName("Unit test policy 0"); - zone->addNSTrigger(nsName, pol); + zone->addNSTrigger(nsName, std::move(pol)); auto luaconfsCopy = g_luaconfs.getCopy(); luaconfsCopy.dfe.addZone(zone); g_luaconfs.setState(luaconfsCopy); @@ -2922,8 +2922,8 @@ BOOST_AUTO_TEST_CASE(test_nameserver_name_rpz_disabled) { pol.d_kind = DNSFilterEngine::PolicyKind::Drop; std::shared_ptr zone = std::make_shared(); zone->setName("Unit test policy 0"); - zone->addNSIPTrigger(Netmask(ns, 128), pol); - zone->addNSTrigger(nsName, pol); + zone->addNSIPTrigger(Netmask(ns, 128), DNSFilterEngine::Policy(pol)); + zone->addNSTrigger(nsName, std::move(pol)); auto luaconfsCopy = g_luaconfs.getCopy(); luaconfsCopy.dfe.addZone(zone); g_luaconfs.setState(luaconfsCopy); diff --git a/pdns/rpzloader.cc b/pdns/rpzloader.cc index eaa258b7b5..d878053e2a 100644 --- a/pdns/rpzloader.cc +++ b/pdns/rpzloader.cc @@ -116,7 +116,7 @@ void RPZRecordToPolicy(const DNSRecord& dr, std::shared_ptrgetZoneRepresentation()<<" for "<addNSTrigger(filt, pol); + zone->addNSTrigger(filt, std::move(pol)); else - zone->rmNSTrigger(filt, pol); + zone->rmNSTrigger(filt, std::move(pol)); } else if(dr.d_name.isPartOf(rpzClientIP)) { DNSName filt=dr.d_name.makeRelative(rpzClientIP); auto nm=makeNetmaskFromRPZ(filt); if(addOrRemove) - zone->addClientTrigger(nm, pol); + zone->addClientTrigger(nm, std::move(pol)); else - zone->rmClientTrigger(nm, pol); + zone->rmClientTrigger(nm, std::move(pol)); } else if(dr.d_name.isPartOf(rpzIP)) { // cerr<<"Should apply answer content IP policy: "<addResponseTrigger(nm, pol); + zone->addResponseTrigger(nm, std::move(pol)); else - zone->rmResponseTrigger(nm, pol); + zone->rmResponseTrigger(nm, std::move(pol)); } else if(dr.d_name.isPartOf(rpzNSIP)) { DNSName filt=dr.d_name.makeRelative(rpzNSIP); auto nm=makeNetmaskFromRPZ(filt); if(addOrRemove) - zone->addNSIPTrigger(nm, pol); + zone->addNSIPTrigger(nm, std::move(pol)); else - zone->rmNSIPTrigger(nm, pol); + zone->rmNSIPTrigger(nm, std::move(pol)); } else { if(addOrRemove) - zone->addQNameTrigger(dr.d_name, pol); + zone->addQNameTrigger(dr.d_name, std::move(pol)); else - zone->rmQNameTrigger(dr.d_name, pol); + zone->rmQNameTrigger(dr.d_name, std::move(pol)); } } diff --git a/regression-tests.recursor-dnssec/test_RPZ.py b/regression-tests.recursor-dnssec/test_RPZ.py index d6d13172b4..9c3c143468 100644 --- a/regression-tests.recursor-dnssec/test_RPZ.py +++ b/regression-tests.recursor-dnssec/test_RPZ.py @@ -98,7 +98,18 @@ class RPZServer(object): dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % oldSerial), dns.rrset.from_text('d.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'), dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial), - dns.rrset.from_text('e.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'), + dns.rrset.from_text('e.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1', '192.0.2.2'), + dns.rrset.from_text('e.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.MX, '10 mx.example.'), + dns.rrset.from_text('f.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.CNAME, 'e.example.'), + dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial) + ] + elif newSerial == 7: + records = [ + dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial), + dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % oldSerial), + dns.rrset.from_text('e.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1', '192.0.2.2'), + dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial), + dns.rrset.from_text('e.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.2'), dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial) ] @@ -244,6 +255,20 @@ e 3600 IN A 192.0.2.42 def checkNotBlocked(self, name, adQuery=False): self.checkBlocked(name, False, adQuery) + def checkCustom(self, qname, qtype, expected): + query = dns.message.make_query(qname, qtype, want_dnssec=True) + query.flags |= dns.flags.CD + res = self.sendUDPQuery(query) + + self.assertRRsetInAnswer(res, expected) + + def checkNoData(self, qname, qtype): + query = dns.message.make_query(qname, qtype, want_dnssec=True) + query.flags |= dns.flags.CD + res = self.sendUDPQuery(query) + + self.assertEqual(len(res.answer), 0) + def waitUntilCorrectSerialIsLoaded(self, serial, timeout=5): global rpzServer @@ -318,13 +343,28 @@ e 3600 IN A 192.0.2.42 self.checkNotBlocked('c.example.') self.checkBlocked('d.example.') - # sixth zone, only e should be blocked + # sixth zone, only e should be blocked, f is a local data record self.waitUntilCorrectSerialIsLoaded(6) - self.checkRPZStats(6, 1, 2, self._xfrDone) + self.checkRPZStats(6, 2, 2, self._xfrDone) + self.checkNotBlocked('a.example.') + self.checkNotBlocked('b.example.') + self.checkNotBlocked('c.example.') + self.checkNotBlocked('d.example.') + self.checkCustom('e.example.', 'A', dns.rrset.from_text('e.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.1', '192.0.2.2')) + self.checkCustom('e.example.', 'MX', dns.rrset.from_text('e.example.', 0, dns.rdataclass.IN, 'MX', '10 mx.example.')) + self.checkNoData('e.example.', 'AAAA') + self.checkCustom('f.example.', 'A', dns.rrset.from_text('f.example.', 0, dns.rdataclass.IN, 'CNAME', 'e.example.')) + + # seventh zone, e should only have one A + self.waitUntilCorrectSerialIsLoaded(7) + self.checkRPZStats(7, 2, 2, self._xfrDone) self.checkNotBlocked('a.example.') self.checkNotBlocked('b.example.') self.checkNotBlocked('c.example.') self.checkNotBlocked('d.example.') - self.checkBlocked('e.example.') + self.checkCustom('e.example.', 'A', dns.rrset.from_text('e.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.2')) + self.checkCustom('e.example.', 'MX', dns.rrset.from_text('e.example.', 0, dns.rdataclass.IN, 'MX', '10 mx.example.')) + self.checkNoData('e.example.', 'AAAA') + self.checkCustom('f.example.', 'A', dns.rrset.from_text('f.example.', 0, dns.rdataclass.IN, 'CNAME', 'e.example.')) # check that the policy is disabled for AD=1 queries self.checkNotBlocked('e.example.', True)