From: Remi Gacogne Date: Fri, 13 Mar 2020 14:33:47 +0000 (+0100) Subject: rec: Add custom tags to RPZ hits X-Git-Tag: dnsdist-1.5.0-alpha1~8^2~3 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=b502d5221e4f64a3b6538530f4025032a986b116;p=thirdparty%2Fpdns.git rec: Add custom tags to RPZ hits This commit adds the possibility to set custom tags to a RPZ zone, adding these tags to the policy ones (that can be set with Lua) when a policy matches. It does so by creating a new PolicyZoneData object that is shared between the zone and all the policies that it holds, in order to - avoid duplicating the name, priority and tags for each policy ; - prevent a circular dependency between shared pointers for the zone and its policies. It also refactors the handling of RPZ policy hits in `startDoResolve()` to remove some code duplication. --- diff --git a/pdns/filterpo.cc b/pdns/filterpo.cc index 95f7b70d64..087be580a5 100644 --- a/pdns/filterpo.cc +++ b/pdns/filterpo.cc @@ -123,11 +123,11 @@ bool DNSFilterEngine::getProcessingPolicy(const DNSName& qname, const std::unord bool allEmpty = true; for (const auto& z : d_zones) { bool enabled = true; - const auto zoneName = z->getName(); - if (z->getPriority() >= pol.d_priority) { + const auto& zoneName = z->getName(); + if (z->getPriority() >= pol.getPriority()) { enabled = false; } - else if (zoneName && discardedPolicies.find(*zoneName) != discardedPolicies.end()) { + else if (discardedPolicies.find(zoneName) != discardedPolicies.end()) { enabled = false; } else { @@ -182,11 +182,11 @@ bool DNSFilterEngine::getProcessingPolicy(const ComboAddress& address, const std { // cout<<"Got question for nameserver IP "<getPriority() >= pol.d_priority) { + if (z->getPriority() >= pol.getPriority()) { break; } - const auto zoneName = z->getName(); - if(zoneName && discardedPolicies.find(*zoneName) != discardedPolicies.end()) { + const auto& zoneName = z->getName(); + if (discardedPolicies.find(zoneName) != discardedPolicies.end()) { continue; } @@ -206,11 +206,11 @@ bool DNSFilterEngine::getQueryPolicy(const DNSName& qname, const ComboAddress& c bool allEmpty = true; for (const auto& z : d_zones) { bool enabled = true; - if (z->getPriority() >= pol.d_priority) { + if (z->getPriority() >= pol.getPriority()) { enabled = false; } else { - const auto zoneName = z->getName(); - if (zoneName && discardedPolicies.find(*zoneName) != discardedPolicies.end()) { + const auto& zoneName = z->getName(); + if (discardedPolicies.find(zoneName) != discardedPolicies.end()) { enabled = false; } else { @@ -289,11 +289,11 @@ bool DNSFilterEngine::getPostPolicy(const vector& records, const std: continue; for (const auto& z : d_zones) { - if (z->getPriority() >= pol.d_priority) { + if (z->getPriority() >= pol.getPriority()) { break; } - const auto zoneName = z->getName(); - if (zoneName && discardedPolicies.find(*zoneName) != discardedPolicies.end()) { + const auto& zoneName = z->getName(); + if (discardedPolicies.find(zoneName) != discardedPolicies.end()) { continue; } @@ -313,14 +313,14 @@ void DNSFilterEngine::assureZones(size_t zone) void DNSFilterEngine::Zone::addClientTrigger(const Netmask& nm, Policy&& pol) { - pol.d_name = d_name; + pol.d_zoneData = d_zoneData; pol.d_type = PolicyType::ClientIP; d_qpolAddr.insert(nm).second=std::move(pol); } void DNSFilterEngine::Zone::addResponseTrigger(const Netmask& nm, Policy&& pol) { - pol.d_name = d_name; + pol.d_zoneData = d_zoneData; pol.d_type = PolicyType::ResponseIP; d_postpolAddr.insert(nm).second=std::move(pol); } @@ -346,21 +346,21 @@ void DNSFilterEngine::Zone::addQNameTrigger(const DNSName& n, Policy&& pol, bool } else { auto& qpol = d_qpolName.insert({n, std::move(pol)}).first->second; - qpol.d_name = d_name; + qpol.d_zoneData = d_zoneData; qpol.d_type = PolicyType::QName; } } void DNSFilterEngine::Zone::addNSTrigger(const DNSName& n, Policy&& pol) { - pol.d_name = d_name; + pol.d_zoneData = d_zoneData; pol.d_type = PolicyType::NSDName; d_propolName.insert({n, std::move(pol)}); } void DNSFilterEngine::Zone::addNSIPTrigger(const Netmask& nm, Policy&& pol) { - pol.d_name = d_name; + pol.d_zoneData = d_zoneData; pol.d_type = PolicyType::NSIP; d_propolNSAddr.insert(nm).second = std::move(pol); } @@ -638,3 +638,10 @@ void DNSFilterEngine::Zone::dump(FILE* fp) const dumpAddrPolicy(fp, pair.first, DNSName("rpz-ip.") + d_domain, pair.second); } } + +void mergePolicyTags(std::unordered_set& tags, const std::unordered_set& newTags) +{ + for (const auto& tag : newTags) { + tags.insert(tag); + } +} diff --git a/pdns/filterpo.hh b/pdns/filterpo.hh index 43c7404ff1..654d3a9467 100644 --- a/pdns/filterpo.hh +++ b/pdns/filterpo.hh @@ -74,13 +74,21 @@ public: static std::string getKindToString(PolicyKind kind); static std::string getTypeToString(PolicyType type); + struct PolicyZoneData + { + /* shared by all the policies from a single zone */ + std::unordered_set d_tags; + std::string d_name; + Priority d_priority{maximumPriority}; + }; + struct Policy { - Policy(): d_name(nullptr), d_ttl(0), d_priority(maximumPriority), d_kind(PolicyKind::NoAction), d_type(PolicyType::None) + Policy(): d_ttl(0), d_kind(PolicyKind::NoAction), d_type(PolicyType::None) { } - 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_ttl(ttl), d_priority(maximumPriority), d_kind(kind), d_type(type) + Policy(PolicyKind kind, PolicyType type, int32_t ttl=0, std::shared_ptr data=nullptr, const std::vector>& custom={}): d_custom(custom), d_zoneData(data), d_ttl(ttl), d_kind(kind), d_type(type) { } @@ -88,14 +96,56 @@ public: { return d_kind == rhs.d_kind && d_type == rhs.d_type && d_ttl == rhs.d_ttl && d_custom == rhs.d_custom; } + + const std::string& getName() const + { + static std::string notSet; + if (d_zoneData) { + return d_zoneData->d_name; + } + return notSet; + } + + void setName(const std::string& name) + { + /* until now the PolicyZoneData was shared, + we now need to copy it, then write to it */ + std::shared_ptr newZoneData; + if (d_zoneData) { + newZoneData = std::make_shared(*d_zoneData); + } + else { + newZoneData = std::make_shared(); + } + newZoneData->d_name = name; + d_zoneData = newZoneData; + } + + const std::unordered_set& getTags() const + { + static std::unordered_set notSet; + if (d_zoneData) { + return d_zoneData->d_tags; + } + return notSet; + } + + Priority getPriority() const + { + static Priority notSet = maximumPriority; + if (d_zoneData) { + return d_zoneData->d_priority; + } + return notSet; + } + std::vector getCustomRecords(const DNSName& qname, uint16_t qtype) const; std::vector getRecords(const DNSName& qname) const; std::vector> d_custom; - std::shared_ptr d_name; // the name of the policy + std::shared_ptr d_zoneData{nullptr}; /* Yup, we are currently using the same TTL for every record for a given name */ int32_t d_ttl; - Priority d_priority; PolicyKind d_kind; PolicyType d_type; @@ -105,6 +155,10 @@ public: class Zone { public: + Zone(): d_zoneData(std::make_shared()) + { + } + void clear() { d_qpolAddr.clear(); @@ -119,7 +173,7 @@ public: } void setName(const std::string& name) { - d_name = std::make_shared(name); + d_zoneData->d_name = name; } void setDomain(const DNSName& domain) { @@ -133,9 +187,13 @@ public: { d_refresh = refresh; } - const std::shared_ptr getName() const + void setTags(std::unordered_set&& tags) { - return d_name; + d_zoneData->d_tags = std::move(tags); + } + const std::string& getName() const + { + return d_zoneData->d_name; } DNSName getDomain() const @@ -199,25 +257,10 @@ public: return !d_postpolAddr.empty(); } Priority getPriority() const { - return d_priority; + return d_zoneData->d_priority; } void setPriority(Priority p) { - d_priority = p; - for (auto& pair : d_qpolName) { - pair.second.d_priority = p; - } - for (auto& pair : d_propolName) { - pair.second.d_priority = p; - } - for (auto& pair : d_qpolAddr) { - pair.second.d_priority = p; - } - for (auto& pair : d_propolNSAddr) { - pair.second.d_priority = p; - } - for (auto& pair : d_postpolAddr) { - pair.second.d_priority = p; - } + d_zoneData->d_priority = p; } private: static DNSName maskToRPZ(const Netmask& nm); @@ -232,10 +275,9 @@ public: NetmaskTree d_propolNSAddr; // NSIP (RPZ) NetmaskTree d_postpolAddr; // IP trigger (RPZ) DNSName d_domain; - std::shared_ptr d_name; + std::shared_ptr d_zoneData{nullptr}; uint32_t d_serial{0}; uint32_t d_refresh{0}; - Priority d_priority{0}; }; DNSFilterEngine(); @@ -261,7 +303,7 @@ public: { for (const auto zone : d_zones) { const auto& zName = zone->getName(); - if (zName && *zName == name) { + if (zName == name) { return zone; } } @@ -290,28 +332,32 @@ public: // A few convenience methods for the unit test code Policy getQueryPolicy(const DNSName& qname, const ComboAddress& nm, const std::unordered_map& discardedPolicies, Priority p) const { Policy policy; - policy.d_priority = p; + policy.d_zoneData = std::make_shared(); + policy.d_zoneData->d_priority = p; getQueryPolicy(qname, nm, discardedPolicies, policy); return policy; } Policy getProcessingPolicy(const DNSName& qname, const std::unordered_map& discardedPolicies, Priority p) const { Policy policy; - policy.d_priority = p; + policy.d_zoneData = std::make_shared(); + policy.d_zoneData->d_priority = p; getProcessingPolicy(qname, discardedPolicies, policy); return policy; } Policy getProcessingPolicy(const ComboAddress& address, const std::unordered_map& discardedPolicies, Priority p) const { Policy policy; - policy.d_priority = p; + policy.d_zoneData = std::make_shared(); + policy.d_zoneData->d_priority = p; getProcessingPolicy(address, discardedPolicies, policy); return policy; } Policy getPostPolicy(const vector& records, const std::unordered_map& discardedPolicies, Priority p) const { Policy policy; - policy.d_priority = p; + policy.d_zoneData = std::make_shared(); + policy.d_zoneData->d_priority = p; getPostPolicy(records, discardedPolicies, policy); return policy; } @@ -323,3 +369,5 @@ private: void assureZones(size_t zone); vector> d_zones; }; + +void mergePolicyTags(std::unordered_set& tags, const std::unordered_set& newTags); diff --git a/pdns/lua-recursor4.cc b/pdns/lua-recursor4.cc index 404a773d8d..3b13bd464d 100644 --- a/pdns/lua-recursor4.cc +++ b/pdns/lua-recursor4.cc @@ -269,12 +269,10 @@ void RecursorLua4::postPrepareContext() d_lw->registerMember("appliedPolicy", &DNSQuestion::appliedPolicy); d_lw->registerMember("policyName", [](const DNSFilterEngine::Policy& pol) -> std::string { - if(pol.d_name) - return *pol.d_name; - return std::string(); + return pol.getName(); }, [](DNSFilterEngine::Policy& pol, const std::string& name) { - pol.d_name = std::make_shared(name); + pol.setName(name); }); d_lw->registerMember("policyKind", &DNSFilterEngine::Policy::d_kind); d_lw->registerMember("policyType", &DNSFilterEngine::Policy::d_type); @@ -360,12 +358,13 @@ void RecursorLua4::postPrepareContext() d_lw->registerFunction("getRecords", &DNSQuestion::getRecords); d_lw->registerFunction("setRecords", &DNSQuestion::setRecords); - d_lw->registerFunction("addPolicyTag", [](DNSQuestion& dq, const std::string& tag) { if (dq.policyTags) { dq.policyTags->push_back(tag); } }); + d_lw->registerFunction("addPolicyTag", [](DNSQuestion& dq, const std::string& tag) { if (dq.policyTags) { dq.policyTags->insert(tag); } }); d_lw->registerFunction >&)>("setPolicyTags", [](DNSQuestion& dq, const std::vector >& tags) { if (dq.policyTags) { dq.policyTags->clear(); + dq.policyTags->reserve(tags.size()); for (const auto& tag : tags) { - dq.policyTags->push_back(tag.second); + dq.policyTags->insert(tag.second); } } }); @@ -373,6 +372,7 @@ void RecursorLua4::postPrepareContext() std::vector > ret; if (dq.policyTags) { int count = 1; + ret.reserve(dq.policyTags->size()); for (const auto& tag : *dq.policyTags) { ret.push_back({count++, tag}); } @@ -537,7 +537,7 @@ bool RecursorLua4::ipfilter(const ComboAddress& remote, const ComboAddress& loca return false; // don't block } -unsigned int RecursorLua4::gettag(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::vector* policyTags, LuaContext::LuaObject& data, const EDNSOptionViewMap& ednsOptions, bool tcp, std::string& requestorId, std::string& deviceId, std::string& deviceName, const std::vector& proxyProtocolValues) const +unsigned int RecursorLua4::gettag(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::unordered_set* policyTags, LuaContext::LuaObject& data, const EDNSOptionViewMap& ednsOptions, bool tcp, std::string& requestorId, std::string& deviceId, std::string& deviceName, const std::vector& proxyProtocolValues) const { if(d_gettag) { std::vector> proxyProtocolValuesMap; @@ -552,8 +552,9 @@ unsigned int RecursorLua4::gettag(const ComboAddress& remote, const Netmask& edn if (policyTags) { const auto& tags = std::get<1>(ret); if (tags) { + policyTags->reserve(policyTags->size() + tags->size()); for (const auto& tag : *tags) { - policyTags->push_back(tag.second); + policyTags->insert(tag.second); } } } @@ -582,7 +583,7 @@ unsigned int RecursorLua4::gettag(const ComboAddress& remote, const Netmask& edn struct pdns_ffi_param { public: - pdns_ffi_param(const DNSName& qname_, uint16_t qtype_, const ComboAddress& local_, const ComboAddress& remote_, const Netmask& ednssubnet_, std::vector& policyTags_, std::vector& records_, const EDNSOptionViewMap& ednsOptions_, const std::vector& proxyProtocolValues_, std::string& requestorId_, std::string& deviceId_, std::string& deviceName_, boost::optional& rcode_, uint32_t& ttlCap_, bool& variable_, bool tcp_, bool& logQuery_, bool& logResponse_, bool& followCNAMERecords_): qname(qname_), local(local_), remote(remote_), ednssubnet(ednssubnet_), policyTags(policyTags_), records(records_), ednsOptions(ednsOptions_), proxyProtocolValues(proxyProtocolValues_), requestorId(requestorId_), deviceId(deviceId_), deviceName(deviceName_), rcode(rcode_), ttlCap(ttlCap_), variable(variable_), logQuery(logQuery_), logResponse(logResponse_), followCNAMERecords(followCNAMERecords_), qtype(qtype_), tcp(tcp_) + pdns_ffi_param(const DNSName& qname_, uint16_t qtype_, const ComboAddress& local_, const ComboAddress& remote_, const Netmask& ednssubnet_, std::unordered_set& policyTags_, std::vector& records_, const EDNSOptionViewMap& ednsOptions_, const std::vector& proxyProtocolValues_, std::string& requestorId_, std::string& deviceId_, std::string& deviceName_, boost::optional& rcode_, uint32_t& ttlCap_, bool& variable_, bool tcp_, bool& logQuery_, bool& logResponse_, bool& followCNAMERecords_): qname(qname_), local(local_), remote(remote_), ednssubnet(ednssubnet_), policyTags(policyTags_), records(records_), ednsOptions(ednsOptions_), proxyProtocolValues(proxyProtocolValues_), requestorId(requestorId_), deviceId(deviceId_), deviceName(deviceName_), rcode(rcode_), ttlCap(ttlCap_), variable(variable_), logQuery(logQuery_), logResponse(logResponse_), followCNAMERecords(followCNAMERecords_), qtype(qtype_), tcp(tcp_) { } @@ -597,7 +598,7 @@ public: const ComboAddress& local; const ComboAddress& remote; const Netmask& ednssubnet; - std::vector& policyTags; + std::unordered_set& policyTags; std::vector& records; const EDNSOptionViewMap& ednsOptions; const std::vector& proxyProtocolValues; @@ -616,7 +617,7 @@ public: bool tcp; }; -unsigned int RecursorLua4::gettag_ffi(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::vector* policyTags, std::vector& records, LuaContext::LuaObject& data, const EDNSOptionViewMap& ednsOptions, bool tcp, const std::vector& proxyProtocolValues, std::string& requestorId, std::string& deviceId, std::string& deviceName, boost::optional& rcode, uint32_t& ttlCap, bool& variable, bool& logQuery, bool& logResponse, bool& followCNAMERecords) const +unsigned int RecursorLua4::gettag_ffi(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::unordered_set* policyTags, std::vector& records, LuaContext::LuaObject& data, const EDNSOptionViewMap& ednsOptions, bool tcp, const std::vector& proxyProtocolValues, std::string& requestorId, std::string& deviceId, std::string& deviceName, boost::optional& rcode, uint32_t& ttlCap, bool& variable, bool& logQuery, bool& logResponse, bool& followCNAMERecords) const { if (d_gettag_ffi) { pdns_ffi_param_t param(qname, qtype, local, remote, ednssubnet, *policyTags, records, ednsOptions, proxyProtocolValues, requestorId, deviceId, deviceName, rcode, ttlCap, variable, tcp, logQuery, logResponse, followCNAMERecords); @@ -880,7 +881,7 @@ void pdns_ffi_param_set_tag(pdns_ffi_param_t* ref, unsigned int tag) void pdns_ffi_param_add_policytag(pdns_ffi_param_t *ref, const char* name) { - ref->policyTags.push_back(std::string(name)); + ref->policyTags.insert(std::string(name)); } void pdns_ffi_param_set_requestorid(pdns_ffi_param_t* ref, const char* name) diff --git a/pdns/lua-recursor4.hh b/pdns/lua-recursor4.hh index 88bf6fea8c..5b3dcb5002 100644 --- a/pdns/lua-recursor4.hh +++ b/pdns/lua-recursor4.hh @@ -75,7 +75,7 @@ public: const uint16_t* ednsFlags{nullptr}; vector* currentRecords{nullptr}; DNSFilterEngine::Policy* appliedPolicy{nullptr}; - std::vector* policyTags{nullptr}; + std::unordered_set* policyTags{nullptr}; const std::vector* proxyProtocolValues{nullptr}; std::unordered_map* discardedPolicies{nullptr}; std::string requestorId; @@ -115,8 +115,8 @@ public: DNSName followupName; }; - unsigned int gettag(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::vector* policyTags, LuaContext::LuaObject& data, const EDNSOptionViewMap&, bool tcp, std::string& requestorId, std::string& deviceId, std::string& deviceName, const std::vector& proxyProtocolValues) const; - unsigned int gettag_ffi(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::vector* policyTags, std::vector& records, LuaContext::LuaObject& data, const EDNSOptionViewMap& ednsOptions, bool tcp, const std::vector& proxyProtocolValues, std::string& requestorId, std::string& deviceId, std::string& deviceName, boost::optional& rcode, uint32_t& ttlCap, bool& variable, bool& logQuery, bool& logResponse, bool& followCNAMERecords) const; + unsigned int gettag(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::unordered_set* policyTags, LuaContext::LuaObject& data, const EDNSOptionViewMap&, bool tcp, std::string& requestorId, std::string& deviceId, std::string& deviceName, const std::vector& proxyProtocolValues) const; + unsigned int gettag_ffi(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::unordered_set* policyTags, std::vector& records, LuaContext::LuaObject& data, const EDNSOptionViewMap& ednsOptions, bool tcp, const std::vector& proxyProtocolValues, std::string& requestorId, std::string& deviceId, std::string& deviceName, boost::optional& rcode, uint32_t& ttlCap, bool& variable, bool& logQuery, bool& logResponse, bool& followCNAMERecords) const; void maintenance() const; bool prerpz(DNSQuestion& dq, int& ret) const; diff --git a/pdns/pdns_recursor.cc b/pdns/pdns_recursor.cc index 088cad58e3..e399e779c6 100644 --- a/pdns/pdns_recursor.cc +++ b/pdns/pdns_recursor.cc @@ -271,7 +271,7 @@ struct DNSComboWriter { { } - DNSComboWriter(const std::string& query, const struct timeval& now, std::vector&& policyTags, LuaContext::LuaObject&& data, std::vector&& records): d_mdp(true, query), d_now(now), d_query(query), d_policyTags(std::move(policyTags)), d_records(std::move(records)), d_data(std::move(data)) + DNSComboWriter(const std::string& query, const struct timeval& now, std::unordered_set&& policyTags, LuaContext::LuaObject&& data, std::vector&& records): d_mdp(true, query), d_now(now), d_query(query), d_policyTags(std::move(policyTags)), d_records(std::move(records)), d_data(std::move(data)) { } @@ -331,7 +331,7 @@ struct DNSComboWriter { struct timeval d_kernelTimestamp{0,0}; #endif std::string d_query; - std::vector d_policyTags; + std::unordered_set d_policyTags; std::vector d_records; LuaContext::LuaObject d_data; EDNSSubnetOpts d_ednssubnet; @@ -813,7 +813,7 @@ catch(...) } #ifdef HAVE_PROTOBUF -static void protobufLogQuery(uint8_t maskV4, uint8_t maskV6, const boost::uuids::uuid& uniqueId, const ComboAddress& remote, const ComboAddress& local, const Netmask& ednssubnet, bool tcp, uint16_t id, size_t len, const DNSName& qname, uint16_t qtype, uint16_t qclass, const std::vector& policyTags, const std::string& requestorId, const std::string& deviceId, const std::string& deviceName) +static void protobufLogQuery(uint8_t maskV4, uint8_t maskV6, const boost::uuids::uuid& uniqueId, const ComboAddress& remote, const ComboAddress& local, const Netmask& ednssubnet, bool tcp, uint16_t id, size_t len, const DNSName& qname, uint16_t qtype, uint16_t qclass, const std::unordered_set& policyTags, const std::string& requestorId, const std::string& deviceId, const std::string& deviceName) { if (!t_protobufServers) { return; @@ -1132,6 +1132,56 @@ int followCNAMERecords(vector& ret, const QType& qtype) return rcode; } +enum class PolicyResult : uint8_t { NoAction, HaveAnswer, Drop }; + +static PolicyResult handlePolicyHit(const DNSFilterEngine::Policy& appliedPolicy, const std::unique_ptr& dc, SyncRes& sr, int& res, vector& ret, DNSPacketWriter& pw) +{ + ++g_stats.policyResults[appliedPolicy.d_kind]; + + switch (appliedPolicy.d_kind) { + + case DNSFilterEngine::PolicyKind::NoAction: + return PolicyResult::NoAction; + + case DNSFilterEngine::PolicyKind::Drop: + ++g_stats.policyDrops; + return PolicyResult::Drop; + + case DNSFilterEngine::PolicyKind::NXDOMAIN: + ret.clear(); + res = RCode::NXDomain; + return PolicyResult::HaveAnswer; + + case DNSFilterEngine::PolicyKind::NODATA: + ret.clear(); + res = RCode::NoError; + return PolicyResult::HaveAnswer; + + case DNSFilterEngine::PolicyKind::Truncate: + if (!dc->d_tcp) { + ret.clear(); + res = RCode::NoError; + pw.getHeader()->tc = 1; + return PolicyResult::HaveAnswer; + } + return PolicyResult::NoAction; + + case DNSFilterEngine::PolicyKind::Custom: + ret.clear(); + res = RCode::NoError; + { + auto spoofed = appliedPolicy.getCustomRecords(dc->d_mdp.d_qname, dc->d_mdp.d_qtype); + for (auto& dr : spoofed) { + ret.push_back(dr); + handleRPZCustom(dr, QType(dc->d_mdp.d_qtype), sr, res, ret); + } + } + return PolicyResult::HaveAnswer; + } + + return PolicyResult::NoAction; +} + static void startDoResolve(void *p) { auto dc=std::unique_ptr(reinterpret_cast(p)); @@ -1271,7 +1321,6 @@ static void startDoResolve(void *p) int res = RCode::NoError; DNSFilterEngine::Policy appliedPolicy; - 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, dc->d_logResponse); dq.ednsFlags = &edo.d_extFlags; dq.ednsOptions = &ednsOpts; @@ -1334,52 +1383,29 @@ static void startDoResolve(void *p) // Check if the query has a policy attached to it if (wantsRPZ && (appliedPolicy.d_type == DNSFilterEngine::PolicyType::None || appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction)) { - luaconfsLocal->dfe.getQueryPolicy(dc->d_mdp.d_qname, dc->d_source, sr.d_discardedPolicies, appliedPolicy); + if (luaconfsLocal->dfe.getQueryPolicy(dc->d_mdp.d_qname, dc->d_source, sr.d_discardedPolicies, appliedPolicy)) { + mergePolicyTags(dc->d_policyTags, appliedPolicy.getTags()); + } } // if there is a RecursorLua active, and it 'took' the query in preResolve, we don't launch beginResolve if(!t_pdl || !t_pdl->preresolve(dq, res)) { sr.setWantsRPZ(wantsRPZ); - if(wantsRPZ) { - switch(appliedPolicy.d_kind) { - case DNSFilterEngine::PolicyKind::NoAction: - break; - case DNSFilterEngine::PolicyKind::Drop: - g_stats.policyDrops++; - g_stats.policyResults[appliedPolicy.d_kind]++; - return; - case DNSFilterEngine::PolicyKind::NXDOMAIN: - g_stats.policyResults[appliedPolicy.d_kind]++; - res=RCode::NXDomain; - goto haveAnswer; - case DNSFilterEngine::PolicyKind::NODATA: - g_stats.policyResults[appliedPolicy.d_kind]++; - res=RCode::NoError; - goto haveAnswer; - case DNSFilterEngine::PolicyKind::Custom: - g_stats.policyResults[appliedPolicy.d_kind]++; - res=RCode::NoError; - 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) { - g_stats.policyResults[appliedPolicy.d_kind]++; - res=RCode::NoError; - pw.getHeader()->tc=1; - goto haveAnswer; - } - break; + if (wantsRPZ && appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { + auto policyResult = handlePolicyHit(appliedPolicy, dc, sr, res, ret, pw); + if (policyResult == PolicyResult::HaveAnswer) { + goto haveAnswer; + } + else if (policyResult == PolicyResult::Drop) { + return; } } // Query got not handled for QNAME Policy reasons, now actually go out to find an answer try { sr.d_appliedPolicy = appliedPolicy; + sr.d_policyTags = std::move(dc->d_policyTags); res = sr.beginResolve(dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), dc->d_mdp.d_qclass, ret); shouldNotValidate = sr.wasOutOfBand(); } @@ -1394,50 +1420,26 @@ static void startDoResolve(void *p) } dq.validationState = sr.getValidationState(); appliedPolicy = sr.d_appliedPolicy; + dc->d_policyTags = std::move(sr.d_policyTags); // During lookup, an NSDNAME or NSIP trigger was hit in RPZ if (res == -2) { // XXX This block should be macro'd, it is repeated post-resolve. - appliedPolicy = sr.d_appliedPolicy; - g_stats.policyResults[appliedPolicy.d_kind]++; - switch(appliedPolicy.d_kind) { - case DNSFilterEngine::PolicyKind::NoAction: // This can never happen - throw PDNSException("NoAction policy returned while a NSDNAME or NSIP trigger was hit"); - case DNSFilterEngine::PolicyKind::Drop: - g_stats.policyDrops++; - return; - case DNSFilterEngine::PolicyKind::NXDOMAIN: - ret.clear(); - res=RCode::NXDomain; - goto haveAnswer; - - case DNSFilterEngine::PolicyKind::NODATA: - ret.clear(); - res=RCode::NoError; - goto haveAnswer; - - case DNSFilterEngine::PolicyKind::Truncate: - if(!dc->d_tcp) { - ret.clear(); - res=RCode::NoError; - pw.getHeader()->tc=1; - goto haveAnswer; - } - break; - - case DNSFilterEngine::PolicyKind::Custom: - ret.clear(); - res=RCode::NoError; - 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; + if (appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction) { + throw PDNSException("NoAction policy returned while a NSDNAME or NSIP trigger was hit"); + } + auto policyResult = handlePolicyHit(appliedPolicy, dc, sr, res, ret, pw); + if (policyResult == PolicyResult::HaveAnswer) { + goto haveAnswer; + } + else if (policyResult == PolicyResult::Drop) { + return; } } if (wantsRPZ && (appliedPolicy.d_type == DNSFilterEngine::PolicyType::None || appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction)) { - luaconfsLocal->dfe.getPostPolicy(ret, sr.d_discardedPolicies, appliedPolicy); + if (luaconfsLocal->dfe.getPostPolicy(ret, sr.d_discardedPolicies, appliedPolicy)) { + mergePolicyTags(dc->d_policyTags, appliedPolicy.getTags()); + } } if(t_pdl) { @@ -1458,41 +1460,13 @@ static void startDoResolve(void *p) } if (wantsRPZ) { //XXX This block is repeated, see above - g_stats.policyResults[appliedPolicy.d_kind]++; - switch(appliedPolicy.d_kind) { - case DNSFilterEngine::PolicyKind::NoAction: - break; - case DNSFilterEngine::PolicyKind::Drop: - g_stats.policyDrops++; - return; - case DNSFilterEngine::PolicyKind::NXDOMAIN: - ret.clear(); - res=RCode::NXDomain; - goto haveAnswer; - - case DNSFilterEngine::PolicyKind::NODATA: - ret.clear(); - res=RCode::NoError; - goto haveAnswer; - - case DNSFilterEngine::PolicyKind::Truncate: - if(!dc->d_tcp) { - ret.clear(); - res=RCode::NoError; - pw.getHeader()->tc=1; - goto haveAnswer; - } - break; - - case DNSFilterEngine::PolicyKind::Custom: - ret.clear(); - res=RCode::NoError; - 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; + + auto policyResult = handlePolicyHit(appliedPolicy, dc, sr, res, ret, pw); + if (policyResult == PolicyResult::HaveAnswer) { + goto haveAnswer; + } + else if (policyResult == PolicyResult::Drop) { + return; } } } @@ -1666,11 +1640,11 @@ static void startDoResolve(void *p) } #endif /* NOD_ENABLED */ #ifdef HAVE_PROTOBUF - if (t_protobufServers && !(luaconfsLocal->protobufExportConfig.taggedOnly && (!appliedPolicy.d_name || appliedPolicy.d_name->empty()) && dc->d_policyTags.empty())) { + if (t_protobufServers && !(luaconfsLocal->protobufExportConfig.taggedOnly && appliedPolicy.getName().empty() && dc->d_policyTags.empty())) { pbMessage->setBytes(packet.size()); pbMessage->setResponseCode(pw.getHeader()->rcode); - if (appliedPolicy.d_name) { - pbMessage->setAppliedPolicy(*appliedPolicy.d_name); + if (!appliedPolicy.getName().empty()) { + pbMessage->setAppliedPolicy(appliedPolicy.getName()); pbMessage->setAppliedPolicyType(appliedPolicy.d_type); } pbMessage->setPolicyTags(dc->d_policyTags); @@ -2392,7 +2366,7 @@ static string* doProcessUDPQuestion(const std::string& question, const ComboAddr uint32_t qhash = 0; bool needECS = false; bool needXPF = g_XPFAcl.match(fromaddr); - std::vector policyTags; + std::unordered_set policyTags; LuaContext::LuaObject data; string requestorId; string deviceId; diff --git a/pdns/rec-lua-conf.cc b/pdns/rec-lua-conf.cc index 046f1d485b..73bffa823e 100644 --- a/pdns/rec-lua-conf.cc +++ b/pdns/rec-lua-conf.cc @@ -51,9 +51,9 @@ typename C::value_type::second_type constGet(const C& c, const std::string& name return iter->second; } -typedef std::unordered_map > rpzOptions_t; +typedef std::unordered_map> > > rpzOptions_t; -static void parseRPZParameters(rpzOptions_t& have, std::string& polName, boost::optional& defpol, bool& defpolOverrideLocal, uint32_t& maxTTL, size_t& zoneSizeHint) +static void parseRPZParameters(rpzOptions_t& have, std::string& polName, boost::optional& defpol, bool& defpolOverrideLocal, uint32_t& maxTTL, size_t& zoneSizeHint, std::unordered_set& tags) { if(have.count("policyName")) { polName = boost::get(have["policyName"]); @@ -61,7 +61,7 @@ static void parseRPZParameters(rpzOptions_t& have, std::string& polName, boost:: if(have.count("defpol")) { defpol=DNSFilterEngine::Policy(); defpol->d_kind = (DNSFilterEngine::PolicyKind)boost::get(have["defpol"]); - defpol->d_name = std::make_shared(polName); + defpol->setName(polName); if(defpol->d_kind == DNSFilterEngine::PolicyKind::Custom) { defpol->d_custom.push_back(DNSRecordContent::mastermake(QType::CNAME, QClass::IN, boost::get(have["defcontent"]))); @@ -82,6 +82,12 @@ static void parseRPZParameters(rpzOptions_t& have, std::string& polName, boost:: if(have.count("zoneSizeHint")) { zoneSizeHint = static_cast(boost::get(have["zoneSizeHint"])); } + if (have.count("tags")) { + const auto tagsTable = boost::get>>(have["tags"]); + for (const auto& tag : tagsTable) { + tags.insert(tag.second); + } + } } #if HAVE_PROTOBUF @@ -238,10 +244,12 @@ void loadRecursorLuaConfig(const std::string& fname, luaConfigDelayedThreads& de if(options) { auto& have = *options; size_t zoneSizeHint = 0; - parseRPZParameters(have, polName, defpol, defpolOverrideLocal, maxTTL, zoneSizeHint); + std::unordered_set tags; + parseRPZParameters(have, polName, defpol, defpolOverrideLocal, maxTTL, zoneSizeHint, tags); if (zoneSizeHint > 0) { zone->reserve(zoneSizeHint); } + zone->setTags(std::move(tags)); } g_log<setName(polName); @@ -286,10 +294,12 @@ void loadRecursorLuaConfig(const std::string& fname, luaConfigDelayedThreads& de if (options) { auto& have = *options; size_t zoneSizeHint = 0; - parseRPZParameters(have, polName, defpol, defpolOverrideLocal, maxTTL, zoneSizeHint); + std::unordered_set tags; + parseRPZParameters(have, polName, defpol, defpolOverrideLocal, maxTTL, zoneSizeHint, tags); if (zoneSizeHint > 0) { zone->reserve(zoneSizeHint); } + zone->setTags(std::move(tags)); if(have.count("tsigname")) { tt.name=DNSName(toLower(boost::get(have["tsigname"]))); diff --git a/pdns/rec-protobuf.cc b/pdns/rec-protobuf.cc index c7eec6943d..bd82ef9523 100644 --- a/pdns/rec-protobuf.cc +++ b/pdns/rec-protobuf.cc @@ -167,7 +167,7 @@ void RecProtoBufMessage::setAppliedPolicyType(const DNSFilterEngine::PolicyType& #endif /* HAVE_PROTOBUF */ } -void RecProtoBufMessage::setPolicyTags(const std::vector& policyTags) +void RecProtoBufMessage::setPolicyTags(const std::unordered_set& policyTags) { #ifdef HAVE_PROTOBUF PBDNSMessage_DNSResponse* response = d_message.mutable_response(); diff --git a/pdns/rec-protobuf.hh b/pdns/rec-protobuf.hh index e4f76b843c..a64faea61b 100644 --- a/pdns/rec-protobuf.hh +++ b/pdns/rec-protobuf.hh @@ -52,7 +52,7 @@ public: #endif /* NOD_ENABLED */ void setAppliedPolicy(const std::string& policy); void setAppliedPolicyType(const DNSFilterEngine::PolicyType& policyType); - void setPolicyTags(const std::vector& policyTags); + void setPolicyTags(const std::unordered_set& policyTags); void addPolicyTag(const std::string& policyTag); void removePolicyTag(const std::string& policyTag); std::string getAppliedPolicy() const; diff --git a/pdns/recursordist/docs/lua-config/rpz.rst b/pdns/recursordist/docs/lua-config/rpz.rst index 3b833eb2ec..8cd75debf5 100644 --- a/pdns/recursordist/docs/lua-config/rpz.rst +++ b/pdns/recursordist/docs/lua-config/rpz.rst @@ -106,6 +106,12 @@ policyName ^^^^^^^^^^ The name logged as 'appliedPolicy' in :doc:`protobuf ` messages when this policy is applied. +tags +^^^^ +.. versionadded:: 4.4.0 + +List of tags as string, that will be added to the policy tags exported over protobuf when a policy of this zone matches. + zoneSizeHint ^^^^^^^^^^^^ An indication of the number of expected entries in the zone, speeding up the loading of huge zones by reserving space in advance. diff --git a/pdns/recursordist/test-filterpo_cc.cc b/pdns/recursordist/test-filterpo_cc.cc index f508925357..c95738cafb 100644 --- a/pdns/recursordist/test-filterpo_cc.cc +++ b/pdns/recursordist/test-filterpo_cc.cc @@ -17,7 +17,7 @@ BOOST_AUTO_TEST_CASE(test_filter_policies_basic) std::string zoneName("Unit test policy 0"); auto zone = std::make_shared(); zone->setName(zoneName); - BOOST_CHECK_EQUAL(*(zone->getName()), zoneName); + BOOST_CHECK_EQUAL(zone->getName(), zoneName); zone->setDomain(DNSName("powerdns.com.")); BOOST_CHECK_EQUAL(zone->getDomain(), DNSName("powerdns.com.")); zone->setSerial(42); @@ -471,7 +471,7 @@ BOOST_AUTO_TEST_CASE(test_multiple_filter_policies) { /* 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}}, DNSFilterEngine::maximumPriority); + const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.142"), {{zone2->getName(), true}}, DNSFilterEngine::maximumPriority); BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName); BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom); auto records = matchingPolicy.getCustomRecords(bad, QType::A); @@ -486,7 +486,7 @@ BOOST_AUTO_TEST_CASE(test_multiple_filter_policies) { /* if zone 1 is disabled, zone 2 should match */ - const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.142"), {{*(zone1->getName()), true}}, DNSFilterEngine::maximumPriority); + const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.142"), {{zone1->getName(), true}}, DNSFilterEngine::maximumPriority); BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName); BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom); auto records = matchingPolicy.getCustomRecords(bad, QType::A); @@ -501,7 +501,7 @@ BOOST_AUTO_TEST_CASE(test_multiple_filter_policies) { /* 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}}, DNSFilterEngine::maximumPriority); + const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.142"), {{zone1->getName(), true}, {zone2->getName(), true}}, DNSFilterEngine::maximumPriority); BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None); } } @@ -556,7 +556,7 @@ BOOST_AUTO_TEST_CASE(test_multiple_filter_policies_order) { /* client IP and qname should match, but zone 1 is disabled and zone2's priority is too high */ - const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.128"), {{*(zone1->getName()), true}}, 1); + const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.128"), {{zone1->getName(), true}}, 1); BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None); BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction); } @@ -600,7 +600,7 @@ BOOST_AUTO_TEST_CASE(test_multiple_filter_policies_order) { /* if we disable zone 1, zone 2 should match */ - const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.142"), {{*(zone1->getName()), true}}, DNSFilterEngine::maximumPriority); + const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.142"), {{zone1->getName(), true}}, DNSFilterEngine::maximumPriority); BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName); BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom); auto records = matchingPolicy.getCustomRecords(bad, QType::A); @@ -615,7 +615,7 @@ BOOST_AUTO_TEST_CASE(test_multiple_filter_policies_order) { /* if we disable zone 1, zone 2 should match, except if we require a priority < 1 */ - const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.142"), {{*(zone1->getName()), true}}, 1); + const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.142"), {{zone1->getName(), true}}, 1); BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None); BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction); } @@ -637,7 +637,7 @@ BOOST_AUTO_TEST_CASE(test_multiple_filter_policies_order) { /* blocked NS name, except policy 1 is disabled and policy2's priority is too high */ - auto matchingPolicy = dfe.getProcessingPolicy(nsName, {{*(zone1->getName()), true}}, 1); + auto matchingPolicy = dfe.getProcessingPolicy(nsName, {{zone1->getName(), true}}, 1); BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None); BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction); } @@ -659,7 +659,7 @@ BOOST_AUTO_TEST_CASE(test_multiple_filter_policies_order) { /* blocked NS ip, except policy 1 is disabled and policy2's priority is too high */ - auto matchingPolicy = dfe.getProcessingPolicy(nsIP, {{*(zone1->getName()), true}}, 1); + auto matchingPolicy = dfe.getProcessingPolicy(nsIP, {{zone1->getName(), true}}, 1); BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None); BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction); } @@ -687,7 +687,7 @@ BOOST_AUTO_TEST_CASE(test_multiple_filter_policies_order) DNSRecord dr; dr.d_type = QType::A; dr.d_content = DNSRecordContent::mastermake(QType::A, QClass::IN, responseIP.toString()); - const auto matchingPolicy = dfe.getPostPolicy({dr}, {{*(zone1->getName()), true}}, 1); + const auto matchingPolicy = dfe.getPostPolicy({dr}, {{zone1->getName(), true}}, 1); BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None); BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction); } diff --git a/pdns/rpzloader.cc b/pdns/rpzloader.cc index 53cad72860..908c3a4384 100644 --- a/pdns/rpzloader.cc +++ b/pdns/rpzloader.cc @@ -364,7 +364,7 @@ void RPZIXFRTracker(const std::vector& masters, boost::optionalgetDomain(); - std::string polName = oldZone->getName() ? *(oldZone->getName()) : zoneName.toString(); + std::string polName = oldZone->getName().empty() ? oldZone->getName() : zoneName.toString(); while (!sr) { /* if we received an empty sr, the zone was not really preloaded */ diff --git a/pdns/syncres.cc b/pdns/syncres.cc index b24a52d60f..024cf09efa 100644 --- a/pdns/syncres.cc +++ b/pdns/syncres.cc @@ -1807,17 +1807,23 @@ bool SyncRes::nameserversBlockedByRPZ(const DNSFilterEngine& dfe, const NsSet& n if (d_wantsRPZ && (d_appliedPolicy.d_type == DNSFilterEngine::PolicyType::None || d_appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction)) { for (auto const &ns : nameservers) { bool match = dfe.getProcessingPolicy(ns.first, d_discardedPolicies, d_appliedPolicy); - if (match && d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response - LOG(", however nameserver "< d_discardedPolicies; DNSFilterEngine::Policy d_appliedPolicy; + std::unordered_set d_policyTags; unsigned int d_authzonequeries; unsigned int d_outqueries; unsigned int d_tcpoutqueries; diff --git a/pdns/ws-recursor.cc b/pdns/ws-recursor.cc index 8a0977495b..cd8997309f 100644 --- a/pdns/ws-recursor.cc +++ b/pdns/ws-recursor.cc @@ -404,8 +404,8 @@ static void apiServerRPZStats(HttpRequest* req, HttpResponse* resp) { auto zone = luaconf->dfe.getZone(i); if (zone == nullptr) continue; - auto name = zone->getName(); - auto stats = getRPZZoneStats(*name); + const auto& name = zone->getName(); + auto stats = getRPZZoneStats(name); if (stats == nullptr) continue; Json::object zoneInfo = { @@ -416,7 +416,7 @@ static void apiServerRPZStats(HttpRequest* req, HttpResponse* resp) { {"last_update", (double)stats->d_lastUpdate}, {"serial", (double)stats->d_serial}, }; - ret[*name] = zoneInfo; + ret[name] = zoneInfo; } resp->setBody(ret); } diff --git a/regression-tests.recursor-dnssec/test_Protobuf.py b/regression-tests.recursor-dnssec/test_Protobuf.py index cde1e19c5b..689f5478db 100644 --- a/regression-tests.recursor-dnssec/test_Protobuf.py +++ b/regression-tests.recursor-dnssec/test_Protobuf.py @@ -202,6 +202,9 @@ class TestRecursorProtobuf(RecursorTest): self.assertEquals(msg.response.appliedPolicyType, policyType) def checkProtobufTags(self, msg, tags): + print(tags) + print('---') + print(msg.response.tags) self.assertEquals(len(msg.response.tags), len(tags)) for tag in msg.response.tags: self.assertTrue(tag in tags) @@ -550,7 +553,7 @@ auth-zones=example=configs/%s/example.zone""" % _confdir # check the protobuf messages corresponding to the UDP query and answer msg = self.getFirstProtobufMessage() self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name) - self.checkProtobufTags(msg, [self._tag_from_gettag]) + self.checkProtobufTags(msg, [ self._tag_from_gettag ]) # then the response msg = self.getFirstProtobufMessage() self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res) @@ -559,7 +562,7 @@ auth-zones=example=configs/%s/example.zone""" % _confdir # we have max-cache-ttl set to 15 self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15) self.assertEquals(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.84') - tags = [self._tag_from_gettag] + self._tags + tags = [ self._tag_from_gettag ] + self._tags self.checkProtobufTags(msg, tags) self.checkNoRemainingMessage() @@ -863,3 +866,71 @@ sub.test 3600 IN A 192.0.2.42 self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15) self.assertEquals(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.42') self.checkNoRemainingMessage() + +class ProtobufRPZTagsTest(TestRecursorProtobuf): + """ + This test makes sure that we correctly export the RPZ tags in our protobuf messages + """ + + _confdir = 'ProtobufRPZTags' + _config_template = """ +auth-zones=example=configs/%s/example.rpz.zone""" % _confdir + _tags = ['tag1', 'tag2'] + _tags_from_gettag = ['tag1-from-gettag', 'tag2-from-gettag'] + _tags_from_rpz = ['tag1-from-rpz', 'tag2-from-rpz' ] + _lua_config_file = """ + protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=true, logResponses=true, tags={'tag1', 'tag2'} } ) + rpzFile('configs/%s/zone.rpz', { policyName="zone.rpz.", tags={ '%s', '%s'} }) + """ % (protobufServersParameters[0].port, protobufServersParameters[1].port, _confdir, _tags_from_rpz[0], _tags_from_rpz[1]) + _lua_dns_script_file = """ + function gettag(remote, ednssubnet, localip, qname, qtype, ednsoptions, tcp) + return 0, { '%s', '%s' } + end + function preresolve(dq) + dq:addPolicyTag('%s') + dq:addPolicyTag('%s') + return false + end + """ % (_tags_from_gettag[0], _tags_from_gettag[1], _tags[0], _tags[1]) + + @classmethod + def generateRecursorConfig(cls, confdir): + authzonepath = os.path.join(confdir, 'example.rpz.zone') + with open(authzonepath, 'w') as authzone: + authzone.write("""$ORIGIN example. +@ 3600 IN SOA {soa} +sub.test 3600 IN A 192.0.2.42 +""".format(soa=cls._SOA)) + + rpzFilePath = os.path.join(confdir, 'zone.rpz') + with open(rpzFilePath, 'w') as rpzZone: + rpzZone.write("""$ORIGIN zone.rpz. +@ 3600 IN SOA {soa} +*.test.example.zone.rpz. 60 IN CNAME rpz-passthru. +""".format(soa=cls._SOA)) + + super(ProtobufRPZTagsTest, cls).generateRecursorConfig(confdir) + + def testA(self): + name = 'sub.test.example.' + expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42') + query = dns.message.make_query(name, 'A', want_dnssec=True) + query.flags |= dns.flags.CD + res = self.sendUDPQuery(query) + self.assertRRsetInAnswer(res, expected) + + # check the protobuf messages corresponding to the UDP query and answer + msg = self.getFirstProtobufMessage() + self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name) + + # then the response + msg = self.getFirstProtobufMessage() + self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res) + self.checkProtobufPolicy(msg, dnsmessage_pb2.PBDNSMessage.PolicyType.QNAME, 'zone.rpz.') + self.checkProtobufTags(msg, self._tags + self._tags_from_gettag + self._tags_from_rpz) + self.assertEquals(len(msg.response.rrs), 1) + rr = msg.response.rrs[0] + # we have max-cache-ttl set to 15 + self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15) + self.assertEquals(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.42') + self.checkNoRemainingMessage()