/* shared by all the policies from a single zone */
std::unordered_set<std::string> d_tags;
std::string d_name;
+ std::string d_extendedErrorExtra;
+ boost::optional<uint16_t> d_extendedErrorCode{boost::none};
Priority d_priority{maximumPriority};
bool d_policyOverridesGettag{true};
};
{
d_zoneData->d_policyOverridesGettag = flag;
}
+ void setExtendedErrorCode(uint16_t code)
+ {
+ d_zoneData->d_extendedErrorCode = code;
+ }
+ void setExtendedErrorExtra(const std::string& extra)
+ {
+ d_zoneData->d_extendedErrorExtra = extra;
+ }
+
const std::string& getName() const
{
return d_zoneData->d_name;
g_log << Logger::Warning << dc->d_mdp.d_qname << "|" << QType(dc->d_mdp.d_qtype).getName() << appliedPolicy.getLogString() << endl;
}
+ if (appliedPolicy.d_zoneData && appliedPolicy.d_zoneData->d_extendedErrorCode) {
+ dc->d_extendedErrorCode = *appliedPolicy.d_zoneData->d_extendedErrorCode;
+ dc->d_extendedErrorExtra = appliedPolicy.d_zoneData->d_extendedErrorExtra;
+ }
+
switch (appliedPolicy.d_kind) {
case DNSFilterEngine::PolicyKind::NoAction:
dq.validationState = sr.getValidationState();
appliedPolicy = sr.d_appliedPolicy;
dc->d_policyTags = std::move(sr.d_policyTags);
+ if (appliedPolicy.d_type != DNSFilterEngine::PolicyType::None && appliedPolicy.d_zoneData && appliedPolicy.d_zoneData->d_extendedErrorCode) {
+ dc->d_extendedErrorCode = *appliedPolicy.d_zoneData->d_extendedErrorCode;
+ dc->d_extendedErrorExtra = appliedPolicy.d_zoneData->d_extendedErrorExtra;
+ }
// 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.
return iter->second;
}
-typedef std::unordered_map<std::string, boost::variant<bool, uint32_t, std::string, std::vector<std::pair<int, std::string>> > > rpzOptions_t;
+typedef std::unordered_map<std::string, boost::variant<bool, uint16_t, 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, std::unordered_set<std::string>& tags, bool& overridesGettag)
+static void parseRPZParameters(rpzOptions_t& have, std::shared_ptr<DNSFilterEngine::Zone>& zone, std::string& polName, boost::optional<DNSFilterEngine::Policy>& defpol, bool& defpolOverrideLocal, uint32_t& maxTTL)
{
- if(have.count("policyName")) {
+ if (have.count("policyName")) {
polName = boost::get<std::string>(have["policyName"]);
}
- if(have.count("defpol")) {
+ if (have.count("defpol")) {
defpol=DNSFilterEngine::Policy();
defpol->d_kind = (DNSFilterEngine::PolicyKind)boost::get<uint32_t>(have["defpol"]);
defpol->setName(polName);
defpolOverrideLocal = boost::get<bool>(have["defpolOverrideLocalData"]);
}
}
- if(have.count("maxTTL")) {
+ if (have.count("maxTTL")) {
maxTTL = boost::get<uint32_t>(have["maxTTL"]);
}
- if(have.count("zoneSizeHint")) {
- zoneSizeHint = static_cast<size_t>(boost::get<uint32_t>(have["zoneSizeHint"]));
+ if (have.count("zoneSizeHint")) {
+ auto zoneSizeHint = static_cast<size_t>(boost::get<uint32_t>(have["zoneSizeHint"]));
+ if (zoneSizeHint > 0) {
+ zone->reserve(zoneSizeHint);
+ }
}
if (have.count("tags")) {
const auto tagsTable = boost::get<std::vector<std::pair<int, std::string>>>(have["tags"]);
+ std::unordered_set<std::string> tags;
for (const auto& tag : tagsTable) {
tags.insert(tag.second);
}
+ zone->setTags(std::move(tags));
}
if (have.count("overridesGettag")) {
- overridesGettag = boost::get<bool>(have["overridesGettag"]);
+ zone->setPolicyOverridesGettag(boost::get<bool>(have["overridesGettag"]));
+ }
+ if (have.count("extendedErrorCode")) {
+ zone->setExtendedErrorCode(boost::get<uint16_t>(have["extendedErrorCode"]));
+ if (have.count("extendedErrorExtra")) {
+ zone->setExtendedErrorExtra(boost::get<std::string>(have["extendedErrorExtra"]));
+ }
}
}
std::string polName("rpzFile");
std::shared_ptr<DNSFilterEngine::Zone> zone = std::make_shared<DNSFilterEngine::Zone>();
uint32_t maxTTL = std::numeric_limits<uint32_t>::max();
- bool overridesGettag = true;
- if(options) {
+ zone->setPolicyOverridesGettag(true);
+ if (options) {
auto& have = *options;
- size_t zoneSizeHint = 0;
- std::unordered_set<std::string> tags;
- parseRPZParameters(have, polName, defpol, defpolOverrideLocal, maxTTL, zoneSizeHint, tags, overridesGettag);
- if (zoneSizeHint > 0) {
- zone->reserve(zoneSizeHint);
- }
- zone->setTags(std::move(tags));
+ parseRPZParameters(have, zone, polName, defpol, defpolOverrideLocal, maxTTL);
}
g_log<<Logger::Warning<<"Loading RPZ from file '"<<filename<<"'"<<endl;
zone->setName(polName);
- zone->setPolicyOverridesGettag(overridesGettag);
loadRPZFromFile(filename, zone, defpol, defpolOverrideLocal, maxTTL);
lci.dfe.addZone(zone);
g_log<<Logger::Warning<<"Done loading RPZ from file '"<<filename<<"'"<<endl;
if (options) {
auto& have = *options;
- size_t zoneSizeHint = 0;
- std::unordered_set<std::string> tags;
- bool overridesGettag = true;
- parseRPZParameters(have, polName, defpol, defpolOverrideLocal, maxTTL, zoneSizeHint, tags, overridesGettag);
- if (zoneSizeHint > 0) {
- zone->reserve(zoneSizeHint);
- }
- zone->setTags(std::move(tags));
- zone->setPolicyOverridesGettag(overridesGettag);
+ zone->setPolicyOverridesGettag(true);
+ parseRPZParameters(have, zone, polName, defpol, defpolOverrideLocal, maxTTL);
if(have.count("tsigname")) {
tt.name=DNSName(toLower(boost::get<string>(have["tsigname"])));
the TTL of the CNAME field to be synthesized for the default policy.
The default is to use the zone's TTL,
+extendedErrorCode
+^^^^^^^^^^^^^^^^^
+.. versionadded:: 4.5.0
+
+An extended error code (:rfc:`8914`) to set on RPZ hits. See :ref:`extended-errors`.
+
+extendedErrorExtra
+^^^^^^^^^^^^^^^^^^
+.. versionadded:: 4.5.0
+
+An extended error extra text (:rfc:`8914`) to set on RPZ hits. See :ref:`extended-errors`.
+
maxTTL
^^^^^^
The maximum TTL value of the synthesized records, overriding a higher value from ``defttl`` or the zone. Default is unlimited.
An object that contains everything about the current query.
This object has the following attributes:
+ .. attribute:: DNSQuestion.extendedErrorCode
+
+ .. versionadded:: 4.5.0
+
+ The current extended error code, if any. See :ref:`extended-errors`.
+
+ .. attribute:: DNSQuestion.extendedErrorExtra
+
+ .. versionadded:: 4.5.0
+
+ The current extended error extra text, as a string, if any. See :ref:`extended-errors`.
+
+ .. attribute:: DNSQuestion.qname
+
+ :class:`DNSName` of the name this query is for.
+
.. attribute:: DNSQuestion.qname
:class:`DNSName` of the name this query is for.
- Boolean
- Default: no
-If set, the recursor will add an EDNS Extended Error to responses failing DNSSEC validation, explaining the failure.
+If set, the recursor will add an EDNS Extended Error (:rfc:`8914`) to responses failing DNSSEC validation, explaining the failure. Enabling this setting will also allow setting custom error codes from Lua or from a RPZ hit.
.. _setting-forward-zones:
_config_template = """
extended-errors=yes
"""
+ _lua_config_file = """
+ rpzFile('configs/%s/zone.rpz', { policyName="zone.rpz.", extendedErrorCode=15, extendedErrorExtra='Blocked by RPZ!'})
+ """ % (_confdir)
_lua_dns_script_file = """
function preresolve(dq)
if dq.qname == newDN('fromlua.extended.') then
@classmethod
def generateRecursorConfig(cls, confdir):
+ rpzFilePath = os.path.join(confdir, 'zone.rpz')
+ with open(rpzFilePath, 'w') as rpzZone:
+ rpzZone.write("""$ORIGIN zone.rpz.
+@ 3600 IN SOA {soa}
+*.rpz.extended.zone.rpz. 60 IN CNAME .
+""".format(soa=cls._SOA))
+
super(ExtendedErrorsRecursorTest, cls).generateRecursorConfig(confdir)
def testNotIncepted(self):
self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(7, b''))
def testBogus(self):
- qname = 'unknownalgorithm.bad-dnssec.wb.sidnlabs.nl.'
+ qname = 'bogussig.ok.bad-dnssec.wb.sidnlabs.nl.'
query = dns.message.make_query(qname, 'A', want_dnssec=True)
for method in ("sendUDPQuery", "sendTCPQuery"):
self.assertEqual(res.options[0].otype, 15)
self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(10, b'Extra text from Lua FFI!'))
+ def testRPZ(self):
+ qname = 'sub.rpz.extended.'
+ query = dns.message.make_query(qname, 'A', want_dnssec=True)
+
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(query, timeout=5.0)
+ self.assertRcodeEqual(res, dns.rcode.NXDOMAIN)
+ self.assertEqual(res.edns, 0)
+ self.assertEqual(len(res.options), 1)
+ self.assertEqual(res.options[0].otype, 15)
+ self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(15, b'Blocked by RPZ!'))
+
def testTooLarge(self):
qname = 'toolarge.extended.'
query = dns.message.make_query(qname, 'A', want_dnssec=True, payload=512)