#!/bin/sh
export PDNSRECURSOR=${PDNSRECURSOR:-/usr/sbin/pdns_recursor}
+export PDNSRECCONTROL=${PDNSRECCONTROL:-/usr/bin/rec_control}
export DNSBULKTEST=${DNSBULKTEST:-/usr/bin/dnsbulktest}
if [ "$0" != "./build-scripts/test-recursor-bulk" ]; then
export context="${version}_v6:${IPv6}_csv:${CSV%%.*}"
export IPv6
export CSV
- RECURSOR=$PDNSRECURSOR THRESHOLD=0 TRACE=no time ./recursor-test 5401 $domains || EXIT=1
+ RECURSOR=$PDNSRECURSOR RECCONTROL=$PDNSRECCONTROL THRESHOLD=0 TRACE=no time ./recursor-test 5401 $domains || EXIT=1
mv -f recursor.log recursor-${context}.log
sleep 10
done
Changelogs for 4.3.x
====================
+.. changelog::
+ :version: 4.3.0-rc2
+ :released: 18th of March 2020
+
+ This is the first Release Candidate for version 4.3.0 of the Authoritative Server.
+ The version called 4.3.0-rc1 was never released because of the cache cleanup change mentioned below.
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 8924
+
+ Make sure we look at 10% of all cached items during cleanup (Kees Monshouwer)
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 8936
+
+ emit correct NSEC/NSEC3 bitmaps in hidden key situations (Robin Geuze)
+
.. changelog::
:version: 4.3.0-beta2
:released: 21st of February 2020
-@ 86400 IN SOA pdns-public-ns1.powerdns.com. pieter\.lexis.powerdns.com. 2020030304 10800 3600 604800 10800
+@ 86400 IN SOA pdns-public-ns1.powerdns.com. pieter\.lexis.powerdns.com. 2020031801 10800 3600 604800 10800
@ 3600 IN NS pdns-public-ns1.powerdns.com.
@ 3600 IN NS pdns-public-ns2.powerdns.com.
auth-4.2.1.security-status 60 IN TXT "1 OK"
auth-4.3.0-alpha1.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
auth-4.3.0-beta1.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-auth-4.3.0-beta2.security-status 60 IN TXT "1 OK"
+auth-4.3.0-beta2.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
+auth-4.3.0-rc1.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
+auth-4.3.0-rc2.security-status 60 IN TXT "1 OK"
; Auth Debian
auth-3.4.1-2.debian.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/3/security/powerdns-advisory-2015-01/ and https://doc.powerdns.com/3/security/powerdns-advisory-2015-02/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-02/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-03/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-04/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-05/"
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 {
{
// cout<<"Got question for nameserver IP "<<address.toString()<<endl;
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;
}
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 {
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;
}
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);
}
}
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);
}
dumpAddrPolicy(fp, pair.first, DNSName("rpz-ip.") + d_domain, pair.second);
}
}
+
+void mergePolicyTags(std::unordered_set<std::string>& tags, const std::unordered_set<std::string>& newTags)
+{
+ for (const auto& tag : newTags) {
+ tags.insert(tag);
+ }
+}
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<std::string> 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<std::string> name=nullptr, const std::vector<std::shared_ptr<DNSRecordContent>>& 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<PolicyZoneData> data=nullptr, const std::vector<std::shared_ptr<DNSRecordContent>>& custom={}): d_custom(custom), d_zoneData(data), d_ttl(ttl), d_kind(kind), d_type(type)
{
}
{
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 const 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<PolicyZoneData> newZoneData;
+ if (d_zoneData) {
+ newZoneData = std::make_shared<PolicyZoneData>(*d_zoneData);
+ }
+ else {
+ newZoneData = std::make_shared<PolicyZoneData>();
+ }
+ newZoneData->d_name = name;
+ d_zoneData = newZoneData;
+ }
+
+ const std::unordered_set<std::string>& getTags() const
+ {
+ static const std::unordered_set<std::string> 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<DNSRecord> getCustomRecords(const DNSName& qname, uint16_t qtype) const;
std::vector<DNSRecord> getRecords(const DNSName& qname) const;
std::vector<std::shared_ptr<DNSRecordContent>> d_custom;
- std::shared_ptr<std::string> d_name; // the name of the policy
+ std::shared_ptr<PolicyZoneData> 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;
class Zone {
public:
+ Zone(): d_zoneData(std::make_shared<PolicyZoneData>())
+ {
+ }
+
void clear()
{
d_qpolAddr.clear();
}
void setName(const std::string& name)
{
- d_name = std::make_shared<std::string>(name);
+ d_zoneData->d_name = name;
}
void setDomain(const DNSName& domain)
{
{
d_refresh = refresh;
}
- const std::shared_ptr<std::string> getName() const
+ void setTags(std::unordered_set<std::string>&& tags)
{
- return d_name;
+ d_zoneData->d_tags = std::move(tags);
+ }
+ const std::string& getName() const
+ {
+ return d_zoneData->d_name;
}
DNSName getDomain() const
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);
NetmaskTree<Policy> d_propolNSAddr; // NSIP (RPZ)
NetmaskTree<Policy> d_postpolAddr; // IP trigger (RPZ)
DNSName d_domain;
- std::shared_ptr<std::string> d_name;
+ std::shared_ptr<PolicyZoneData> d_zoneData{nullptr};
uint32_t d_serial{0};
uint32_t d_refresh{0};
- Priority d_priority{0};
};
DNSFilterEngine();
{
for (const auto zone : d_zones) {
const auto& zName = zone->getName();
- if (zName && *zName == name) {
+ if (zName == name) {
return zone;
}
}
// A few convenience methods for the unit test code
Policy getQueryPolicy(const DNSName& qname, const ComboAddress& nm, const std::unordered_map<std::string,bool>& discardedPolicies, Priority p) const {
Policy policy;
- policy.d_priority = p;
+ policy.d_zoneData = std::make_shared<PolicyZoneData>();
+ policy.d_zoneData->d_priority = p;
getQueryPolicy(qname, nm, discardedPolicies, policy);
return policy;
}
Policy getProcessingPolicy(const DNSName& qname, const std::unordered_map<std::string,bool>& discardedPolicies, Priority p) const {
Policy policy;
- policy.d_priority = p;
+ policy.d_zoneData = std::make_shared<PolicyZoneData>();
+ policy.d_zoneData->d_priority = p;
getProcessingPolicy(qname, discardedPolicies, policy);
return policy;
}
Policy getProcessingPolicy(const ComboAddress& address, const std::unordered_map<std::string,bool>& discardedPolicies, Priority p) const {
Policy policy;
- policy.d_priority = p;
+ policy.d_zoneData = std::make_shared<PolicyZoneData>();
+ policy.d_zoneData->d_priority = p;
getProcessingPolicy(address, discardedPolicies, policy);
return policy;
}
Policy getPostPolicy(const vector<DNSRecord>& records, const std::unordered_map<std::string,bool>& discardedPolicies, Priority p) const {
Policy policy;
- policy.d_priority = p;
+ policy.d_zoneData = std::make_shared<PolicyZoneData>();
+ policy.d_zoneData->d_priority = p;
getPostPolicy(records, discardedPolicies, policy);
return policy;
}
void assureZones(size_t zone);
vector<std::shared_ptr<Zone>> d_zones;
};
+
+void mergePolicyTags(std::unordered_set<std::string>& tags, const std::unordered_set<std::string>& newTags);
d_lw->registerMember("appliedPolicy", &DNSQuestion::appliedPolicy);
d_lw->registerMember<DNSFilterEngine::Policy, std::string>("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<std::string>(name);
+ pol.setName(name);
});
d_lw->registerMember("policyKind", &DNSFilterEngine::Policy::d_kind);
d_lw->registerMember("policyType", &DNSFilterEngine::Policy::d_type);
d_lw->registerFunction("getRecords", &DNSQuestion::getRecords);
d_lw->registerFunction("setRecords", &DNSQuestion::setRecords);
- d_lw->registerFunction<void(DNSQuestion::*)(const std::string&)>("addPolicyTag", [](DNSQuestion& dq, const std::string& tag) { if (dq.policyTags) { dq.policyTags->push_back(tag); } });
+ d_lw->registerFunction<void(DNSQuestion::*)(const std::string&)>("addPolicyTag", [](DNSQuestion& dq, const std::string& tag) { if (dq.policyTags) { dq.policyTags->insert(tag); } });
d_lw->registerFunction<void(DNSQuestion::*)(const std::vector<std::pair<int, std::string> >&)>("setPolicyTags", [](DNSQuestion& dq, const std::vector<std::pair<int, std::string> >& 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);
}
}
});
std::vector<std::pair<int, std::string> > ret;
if (dq.policyTags) {
int count = 1;
+ ret.reserve(dq.policyTags->size());
for (const auto& tag : *dq.policyTags) {
ret.push_back({count++, tag});
}
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<std::string>* policyTags, LuaContext::LuaObject& data, const EDNSOptionViewMap& ednsOptions, bool tcp, std::string& requestorId, std::string& deviceId, std::string& deviceName, const std::vector<ProxyProtocolValue>& proxyProtocolValues) const
+unsigned int RecursorLua4::gettag(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::unordered_set<std::string>* policyTags, LuaContext::LuaObject& data, const EDNSOptionViewMap& ednsOptions, bool tcp, std::string& requestorId, std::string& deviceId, std::string& deviceName, const std::vector<ProxyProtocolValue>& proxyProtocolValues) const
{
if(d_gettag) {
std::vector<std::pair<int, const ProxyProtocolValue*>> proxyProtocolValuesMap;
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);
}
}
}
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<std::string>& policyTags_, std::vector<DNSRecord>& records_, const EDNSOptionViewMap& ednsOptions_, const std::vector<ProxyProtocolValue>& proxyProtocolValues_, std::string& requestorId_, std::string& deviceId_, std::string& deviceName_, boost::optional<int>& 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<std::string>& policyTags_, std::vector<DNSRecord>& records_, const EDNSOptionViewMap& ednsOptions_, const std::vector<ProxyProtocolValue>& proxyProtocolValues_, std::string& requestorId_, std::string& deviceId_, std::string& deviceName_, boost::optional<int>& 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_)
{
}
const ComboAddress& local;
const ComboAddress& remote;
const Netmask& ednssubnet;
- std::vector<std::string>& policyTags;
+ std::unordered_set<std::string>& policyTags;
std::vector<DNSRecord>& records;
const EDNSOptionViewMap& ednsOptions;
const std::vector<ProxyProtocolValue>& proxyProtocolValues;
bool tcp;
};
-unsigned int RecursorLua4::gettag_ffi(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::vector<std::string>* policyTags, std::vector<DNSRecord>& records, LuaContext::LuaObject& data, const EDNSOptionViewMap& ednsOptions, bool tcp, const std::vector<ProxyProtocolValue>& proxyProtocolValues, std::string& requestorId, std::string& deviceId, std::string& deviceName, boost::optional<int>& 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<std::string>* policyTags, std::vector<DNSRecord>& records, LuaContext::LuaObject& data, const EDNSOptionViewMap& ednsOptions, bool tcp, const std::vector<ProxyProtocolValue>& proxyProtocolValues, std::string& requestorId, std::string& deviceId, std::string& deviceName, boost::optional<int>& 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);
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)
const uint16_t* ednsFlags{nullptr};
vector<DNSRecord>* currentRecords{nullptr};
DNSFilterEngine::Policy* appliedPolicy{nullptr};
- std::vector<std::string>* policyTags{nullptr};
+ std::unordered_set<std::string>* policyTags{nullptr};
const std::vector<ProxyProtocolValue>* proxyProtocolValues{nullptr};
std::unordered_map<std::string,bool>* discardedPolicies{nullptr};
std::string requestorId;
DNSName followupName;
};
- unsigned int gettag(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::vector<std::string>* policyTags, LuaContext::LuaObject& data, const EDNSOptionViewMap&, bool tcp, std::string& requestorId, std::string& deviceId, std::string& deviceName, const std::vector<ProxyProtocolValue>& proxyProtocolValues) const;
- unsigned int gettag_ffi(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::vector<std::string>* policyTags, std::vector<DNSRecord>& records, LuaContext::LuaObject& data, const EDNSOptionViewMap& ednsOptions, bool tcp, const std::vector<ProxyProtocolValue>& proxyProtocolValues, std::string& requestorId, std::string& deviceId, std::string& deviceName, boost::optional<int>& 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<std::string>* policyTags, LuaContext::LuaObject& data, const EDNSOptionViewMap&, bool tcp, std::string& requestorId, std::string& deviceId, std::string& deviceName, const std::vector<ProxyProtocolValue>& proxyProtocolValues) const;
+ unsigned int gettag_ffi(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::unordered_set<std::string>* policyTags, std::vector<DNSRecord>& records, LuaContext::LuaObject& data, const EDNSOptionViewMap& ednsOptions, bool tcp, const std::vector<ProxyProtocolValue>& proxyProtocolValues, std::string& requestorId, std::string& deviceId, std::string& deviceName, boost::optional<int>& rcode, uint32_t& ttlCap, bool& variable, bool& logQuery, bool& logResponse, bool& followCNAMERecords) const;
void maintenance() const;
bool prerpz(DNSQuestion& dq, int& ret) const;
{
}
- DNSComboWriter(const std::string& query, const struct timeval& now, std::vector<std::string>&& policyTags, LuaContext::LuaObject&& data, std::vector<DNSRecord>&& 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<std::string>&& policyTags, LuaContext::LuaObject&& data, std::vector<DNSRecord>&& 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))
{
}
struct timeval d_kernelTimestamp{0,0};
#endif
std::string d_query;
- std::vector<std::string> d_policyTags;
+ std::unordered_set<std::string> d_policyTags;
std::vector<DNSRecord> d_records;
LuaContext::LuaObject d_data;
EDNSSubnetOpts d_ednssubnet;
}
#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<std::string>& 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<std::string>& policyTags, const std::string& requestorId, const std::string& deviceId, const std::string& deviceName)
{
if (!t_protobufServers) {
return;
return rcode;
}
+enum class PolicyResult : uint8_t { NoAction, HaveAnswer, Drop };
+
+static PolicyResult handlePolicyHit(const DNSFilterEngine::Policy& appliedPolicy, const std::unique_ptr<DNSComboWriter>& dc, SyncRes& sr, int& res, vector<DNSRecord>& ret, DNSPacketWriter& pw)
+{
+ /* don't account truncate actions for TCP queries, since they are not applied */
+ if (appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::Truncate || !dc->d_tcp) {
+ ++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<DNSComboWriter>(reinterpret_cast<DNSComboWriter*>(p));
int res = RCode::NoError;
DNSFilterEngine::Policy appliedPolicy;
- std::vector<DNSRecord> 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;
// 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();
}
}
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) {
}
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;
}
}
}
}
#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);
uint32_t qhash = 0;
bool needECS = false;
bool needXPF = g_XPFAcl.match(fromaddr);
- std::vector<std::string> policyTags;
+ std::unordered_set<std::string> policyTags;
LuaContext::LuaObject data;
string requestorId;
string deviceId;
return iter->second;
}
-typedef std::unordered_map<std::string, boost::variant<bool, uint32_t, std::string > > rpzOptions_t;
+typedef std::unordered_map<std::string, boost::variant<bool, uint32_t, std::string, std::vector<std::pair<int, std::string>> > > rpzOptions_t;
-static void parseRPZParameters(rpzOptions_t& have, std::string& polName, boost::optional<DNSFilterEngine::Policy>& defpol, bool& defpolOverrideLocal, uint32_t& maxTTL, size_t& zoneSizeHint)
+static void parseRPZParameters(rpzOptions_t& have, std::string& polName, boost::optional<DNSFilterEngine::Policy>& defpol, bool& defpolOverrideLocal, uint32_t& maxTTL, size_t& zoneSizeHint, std::unordered_set<std::string>& tags)
{
if(have.count("policyName")) {
polName = boost::get<std::string>(have["policyName"]);
if(have.count("defpol")) {
defpol=DNSFilterEngine::Policy();
defpol->d_kind = (DNSFilterEngine::PolicyKind)boost::get<uint32_t>(have["defpol"]);
- defpol->d_name = std::make_shared<std::string>(polName);
+ defpol->setName(polName);
if(defpol->d_kind == DNSFilterEngine::PolicyKind::Custom) {
defpol->d_custom.push_back(DNSRecordContent::mastermake(QType::CNAME, QClass::IN,
boost::get<string>(have["defcontent"])));
if(have.count("zoneSizeHint")) {
zoneSizeHint = static_cast<size_t>(boost::get<uint32_t>(have["zoneSizeHint"]));
}
+ if (have.count("tags")) {
+ const auto tagsTable = boost::get<std::vector<std::pair<int, std::string>>>(have["tags"]);
+ for (const auto& tag : tagsTable) {
+ tags.insert(tag.second);
+ }
+ }
}
#if HAVE_PROTOBUF
if(options) {
auto& have = *options;
size_t zoneSizeHint = 0;
- parseRPZParameters(have, polName, defpol, defpolOverrideLocal, maxTTL, zoneSizeHint);
+ std::unordered_set<std::string> tags;
+ parseRPZParameters(have, polName, defpol, defpolOverrideLocal, maxTTL, zoneSizeHint, tags);
if (zoneSizeHint > 0) {
zone->reserve(zoneSizeHint);
}
+ zone->setTags(std::move(tags));
}
g_log<<Logger::Warning<<"Loading RPZ from file '"<<filename<<"'"<<endl;
zone->setName(polName);
if (options) {
auto& have = *options;
size_t zoneSizeHint = 0;
- parseRPZParameters(have, polName, defpol, defpolOverrideLocal, maxTTL, zoneSizeHint);
+ std::unordered_set<std::string> 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<string>(have["tsigname"])));
#endif /* HAVE_PROTOBUF */
}
-void RecProtoBufMessage::setPolicyTags(const std::vector<std::string>& policyTags)
+void RecProtoBufMessage::setPolicyTags(const std::unordered_set<std::string>& policyTags)
{
#ifdef HAVE_PROTOBUF
PBDNSMessage_DNSResponse* response = d_message.mutable_response();
#endif /* NOD_ENABLED */
void setAppliedPolicy(const std::string& policy);
void setAppliedPolicyType(const DNSFilterEngine::PolicyType& policyType);
- void setPolicyTags(const std::vector<std::string>& policyTags);
+ void setPolicyTags(const std::unordered_set<std::string>& policyTags);
void addPolicyTag(const std::string& policyTag);
void removePolicyTag(const std::string& policyTag);
std::string getAppliedPolicy() const;
^^^^^^^^^^
The name logged as 'appliedPolicy' in :doc:`protobuf <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.
std::string zoneName("Unit test policy 0");
auto zone = std::make_shared<DNSFilterEngine::Zone>();
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);
{
/* 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);
{
/* 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);
{
/* 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);
}
}
{
/* 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);
}
{
/* 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);
{
/* 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);
}
{
/* 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);
}
{
/* 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);
}
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);
}
time_t refresh;
DNSName zoneName = oldZone->getDomain();
- 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 */
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 "<<ns.first<<" was blocked by RPZ policy '"<<(d_appliedPolicy.d_name ? *d_appliedPolicy.d_name : "")<<"'"<<endl);
- return true;
+ if (match) {
+ mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
+ if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
+ LOG(", however nameserver "<<ns.first<<" was blocked by RPZ policy '"<<d_appliedPolicy.getName()<<"'"<<endl);
+ return true;
+ }
}
// Traverse all IP addresses for this NS to see if they have an RPN NSIP policy
for (auto const &address : ns.second.first) {
match = dfe.getProcessingPolicy(address, d_discardedPolicies, d_appliedPolicy);
- if (match && d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
- LOG(", however nameserver "<<ns.first<<" IP address "<<address.toString()<<" was blocked by RPZ policy '"<<(d_appliedPolicy.d_name ? *d_appliedPolicy.d_name : "")<<"'"<<endl);
- return true;
+ if (match) {
+ mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
+ if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
+ LOG(", however nameserver "<<ns.first<<" IP address "<<address.toString()<<" was blocked by RPZ policy '"<<d_appliedPolicy.getName()<<"'"<<endl);
+ return true;
+ }
}
}
}
*/
if (d_wantsRPZ && (d_appliedPolicy.d_type == DNSFilterEngine::PolicyType::None || d_appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction)) {
bool match = dfe.getProcessingPolicy(remoteIP, d_discardedPolicies, d_appliedPolicy);
- if (match && d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) {
- LOG(" (blocked by RPZ policy '"+(d_appliedPolicy.d_name ? *d_appliedPolicy.d_name : "")+"')");
- return true;
+ if (match) {
+ mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
+ if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) {
+ LOG(" (blocked by RPZ policy '" + d_appliedPolicy.getName() + "')");
+ return true;
+ }
}
}
return false;
for (auto const &nameserver : nsset) {
if (d_wantsRPZ && (d_appliedPolicy.d_type == DNSFilterEngine::PolicyType::None || d_appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction)) {
bool match = dfe.getProcessingPolicy(nameserver, d_discardedPolicies, d_appliedPolicy);
- if (match && d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
- LOG("however "<<nameserver<<" was blocked by RPZ policy '"<<(d_appliedPolicy.d_name ? *d_appliedPolicy.d_name : "")<<"'"<<endl);
- throw PolicyHitException();
+ if (match) {
+ mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
+ if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
+ LOG("however "<<nameserver<<" was blocked by RPZ policy '"<<d_appliedPolicy.getName()<<"'"<<endl);
+ throw PolicyHitException();
+ }
}
}
nameservers.insert({nameserver, {{}, false}});
std::unordered_map<std::string,bool> d_discardedPolicies;
DNSFilterEngine::Policy d_appliedPolicy;
+ std::unordered_set<std::string> d_policyTags;
unsigned int d_authzonequeries;
unsigned int d_outqueries;
unsigned int d_tcpoutqueries;
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 = {
{"last_update", (double)stats->d_lastUpdate},
{"serial", (double)stats->d_serial},
};
- ret[*name] = zoneInfo;
+ ret[name] = zoneInfo;
}
resp->setBody(ret);
}
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)
# 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)
# 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()
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()