From: Lukáš Ježek Date: Wed, 1 Apr 2020 10:49:45 +0000 (+0200) Subject: modules/policy: Take multiple RR types from rpz file for a single domain name X-Git-Tag: v5.1.0~14^2~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f3b4c4fe4549282e5ce726cfd4e6dda9a2b7d193;p=thirdparty%2Fknot-resolver.git modules/policy: Take multiple RR types from rpz file for a single domain name --- diff --git a/NEWS b/NEWS index 93e996a57..355efbd95 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,8 @@ Improvements - cache garbage collector: reduce filesystem operations when idle (!946) - policy.DEBUG_ALWAYS and policy.DEBUG_IF for limited verbose logging (!957) - daemon: improve TCP query latency under heavy TCP load (!968) +- add policy.ANSWER action (!964, #192) +- policy.rpz support fake A/AAAA (!964, #194) Bugfixes -------- diff --git a/modules/policy/README.rst b/modules/policy/README.rst index a11aa7db6..db9ed4dcb 100644 --- a/modules/policy/README.rst +++ b/modules/policy/README.rst @@ -146,6 +146,15 @@ Following actions stop the policy matching on the query, i.e. other rules are no -- (the "true" at the end of policy.add) policy.add(policy.REROUTE({'192.0.2.0/24', '127.0.0.0'}), true) +.. function:: ANSWER({ type = { ttl=ttl, rdata=data} }, nodata) + + Overwrite rr data in response. ``rdata`` takes just IP address. If `nodata` is `true` policy return `NODATA` when requested type from client isn't specified (default: ``nodata=false``). + + .. code-block:: lua + + -- this policy change IPv4 adress and TTL for `exmaple.com` + policy.add(policy.suffix(policy.ANSWER({ [kres.type.A] = { ttl=300, rdata='\192\0\2\7' } }), { todname('example.com') })) + More complex non-chain actions are described in their own chapters, namely: * :ref:`forwarding` @@ -541,7 +550,8 @@ Response policy zones "``rpz-passthru.``", ":func:`policy.PASS`", "yes" "``rpz-tcp-only.``", ":func:`policy.TC`", "yes" "``rpz-drop.``", ":func:`policy.DROP`", "no [#]_" - "fake A/AAAA", "*not supported*", "no" + "fake A/AAAA", ":func:`policy.ANSWER`", "yes" + "fake CNAME", "not supported", "no" .. [#] RPZ action ``*.`` in BIND causes *NODATA* answer but typically our users configure ``policy.rpz(policy.DENY, ...)`` diff --git a/modules/policy/policy.lua b/modules/policy/policy.lua index d20891374..a75d5fdf1 100644 --- a/modules/policy/policy.lua +++ b/modules/policy/policy.lua @@ -210,16 +210,26 @@ function policy.FLAGS(opts_set, opts_clear) end -- Create answer with passed arguments -function policy.ANSWER(rtype, rdata, ttl) +function policy.ANSWER(rtable, nodata) return function(_, req) local qry = req:current() local answer = req.answer + local data = rtable[qry.stype] + ffi.C.kr_pkt_make_auth_header(answer) - if (rtype == qry.stype) then + if data == nil then + if nodata == true then + answer:rcode(kres.rcode.NOERROR) + return kres.DONE + end + else + local ttl = data.ttl or 1 + answer:rcode(kres.rcode.NOERROR) answer:begin(kres.section.ANSWER) - answer:put(qry.sname, ttl, qry.sclass, rtype, rdata) + answer:put(qry.sname, ttl, qry.sclass, qry.stype, data.rdata) + return kres.DONE end end @@ -367,6 +377,7 @@ end local function rpz_parse(action, path) local rules = {} + local new_actions = {} local action_map = { -- RPZ Policy Actions ['\0'] = action, @@ -376,6 +387,22 @@ local function rpz_parse(action, path) ['\012rpz-tcp-only\0'] = policy.TC, -- Policy triggers @NYI@ } + local unsupp_rrs = function (rtype) + local set = { + kres.type.DNAME, + kres.type.NS, + kres.type.SOA, + kres.type.DNSKEY, + kres.type.DS, + kres.type.RRSIG, + kres.type.NSEC, + kres.type.NSEC3, + } + for _, l in pairs(set) do + if rtype == l then return true end + end + return false + end local parser = require('zonefile').new() local ok, errstr = parser:open(path) if not ok then @@ -393,10 +420,22 @@ local function rpz_parse(action, path) rules[name] = action_map[name_action] -- Warn when NYI if #name > 1 and not action_map[name_action] then - rules[name] = policy.ANSWER(parser.r_type, name_action, parser.r_ttl) + + if parser.r_type == kres.type.CNAME then + log('[poli] RPZ %s:%d: CNAME in RPZ is not supported', path, tonumber(parser.line_counter)) + elseif unsupp_rrs(parser.r_type) then + log('[poli] RPZ %s:%d: RR type %s is not allowed in RPZ', path, tonumber(parser.line_counter), + kres.tostring.type[parser.r_type]) + else + if new_actions[name] == nil then new_actions[name] = {} end + new_actions[name][parser.r_type] = { ttl=parser.r_ttl, rdata=name_action } + end end end collectgarbage() + for k, v in pairs(new_actions) do + rules[k] = policy.ANSWER(v, true) + end return rules end