-- (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`
"``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, ...)``
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
local function rpz_parse(action, path)
local rules = {}
+ local new_actions = {}
local action_map = {
-- RPZ Policy Actions
['\0'] = action,
['\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
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