From: Marek VavruĊĦa Date: Mon, 3 Aug 2015 02:00:26 +0000 (+0200) Subject: modules/block: reworked for new FFI bindings X-Git-Tag: v1.0.0-beta1~62^2~5 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=0ee2d1d7;p=thirdparty%2Fknot-resolver.git modules/block: reworked for new FFI bindings --- diff --git a/daemon/engine.c b/daemon/engine.c index f482f2e6d..bf47aa472 100644 --- a/daemon/engine.c +++ b/daemon/engine.c @@ -401,7 +401,7 @@ int engine_start(struct engine *engine) /* Clean up stack and restart GC */ lua_settop(engine->L, 0); lua_gc(engine->L, LUA_GCCOLLECT, 0); - lua_gc(engine->L, LUA_GCSETSTEPMUL, 99); + lua_gc(engine->L, LUA_GCSETSTEPMUL, 50); lua_gc(engine->L, LUA_GCSETPAUSE, 400); lua_gc(engine->L, LUA_GCRESTART, 0); return kr_ok(); diff --git a/modules/block/README.rst b/modules/block/README.rst index cd95296c1..0802ba38a 100644 --- a/modules/block/README.rst +++ b/modules/block/README.rst @@ -21,6 +21,8 @@ There are three action: * ``DENY`` - return NXDOMAIN answer * ``DROP`` - terminate query resolution, returns SERVFAIL to requestor +.. note:: The module (and ``kres``) treats domain names as wire, not textual representation. So each label in name is prefixed with its length, e.g. "example.com" equals to "\7example\3com". + Example configuration ^^^^^^^^^^^^^^^^^^^^^ @@ -29,18 +31,18 @@ Example configuration -- Load default block rules modules = { 'block' } -- Whitelist 'www[0-9].badboy.cz' - block:add(block.pattern(block.PASS, 'www[0-9].badboy.cz')) + block:add(block.pattern(block.PASS, '\4www[0-9]\6badboy\2cz')) -- Block all names below badboy.cz - block:add(block.suffix(block.DENY, {'badboy.cz'})) + block:add(block.suffix(block.DENY, {'\6badboy\2cz'})) -- Custom rule - block:add(function (pkt, qname) - if qname:find('%d.%d.%d.224.in-addr.arpa.') then - return block.DENY, '224.in-addr.arpa.' + block:add(function (req, query) + if query:qname():find('%d.%d.%d.224\7in-addr\4arpa') then + return block.DENY end end) -- Disallow ANY queries - block:add(function (pkt, qname) - if pkt:qtype() == kres.rrtype.ANY then + block:add(function (req, query) + if query.type == kres.type.ANY then return block.DROP end end) @@ -51,11 +53,10 @@ Properties .. envvar:: block.PASS (number) .. envvar:: block.DENY (number) .. envvar:: block.DROP (number) -.. envvar:: block.private_zones (table of private zones) .. function:: block:add(rule) - :param rule: added rule, i.e. ``block.pattern(block.DENY, '[0-9]+.cz')`` + :param rule: added rule, i.e. ``block.pattern(block.DENY, '[0-9]+\2cz')`` :param pattern: regular expression Policy to block queries based on the QNAME regex matching. @@ -81,8 +82,7 @@ Properties :param common_suffix: common suffix of entries in suffix_table Like suffix match, but you can also provide a common suffix of all matches for faster processing (nil otherwise). - -.. tip:: If you want to match suffixes only, prefix the strings with `.`, e.g. `.127.in-addr.arpa.` instead of `127.in-addr.arpa`. + This function is faster for small suffix tables (in the order of "hundreds"). .. _`Aho-Corasick`: https://en.wikipedia.org/wiki/Aho%E2%80%93Corasick_string_matching_algorithm .. _`@jgrahamc`: https://github.com/jgrahamc/aho-corasick-lua diff --git a/modules/block/block.lua b/modules/block/block.lua index bdad894e5..399b1af4d 100644 --- a/modules/block/block.lua +++ b/modules/block/block.lua @@ -1,55 +1,17 @@ +local kres = require('kres') local block = { -- Policies PASS = 1, DENY = 2, DROP = 3, -- Special values ANY = 0, - -- Private, local, broadcast, test and special zones - private_zones = { - -- RFC1918 - '.10.in-addr.arpa.', - '.16.172.in-addr.arpa.', - '.17.172.in-addr.arpa.', - '.18.172.in-addr.arpa.', - '.19.172.in-addr.arpa.', - '.20.172.in-addr.arpa.', - '.21.172.in-addr.arpa.', - '.22.172.in-addr.arpa.', - '.23.172.in-addr.arpa.', - '.24.172.in-addr.arpa.', - '.25.172.in-addr.arpa.', - '.26.172.in-addr.arpa.', - '.27.172.in-addr.arpa.', - '.28.172.in-addr.arpa.', - '.29.172.in-addr.arpa.', - '.30.172.in-addr.arpa.', - '.31.172.in-addr.arpa.', - '.168.192.in-addr.arpa.', - -- RFC5735, RFC5737 - '.0.in-addr.arpa.', - '.127.in-addr.arpa.', - '.254.169.in-addr.arpa.', - '.2.0.192.in-addr.arpa.', - '.100.51.198.in-addr.arpa.', - '.113.0.203.in-addr.arpa.', - '255.255.255.255.in-addr.arpa.', - -- IPv6 local, example - '0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.', - '1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.', - '.d.f.ip6.arpa.', - '.8.e.f.ip6.arpa.', - '.9.e.f.ip6.arpa.', - '.a.e.f.ip6.arpa.', - '.b.e.f.ip6.arpa.', - '.8.b.d.0.1.0.0.2.ip6.arpa', - } } -- @function Block requests which QNAME matches given zone list (i.e. suffix match) function block.suffix(action, zone_list) local AC = require('aho-corasick') local tree = AC.build(zone_list) - return function(pkt, qname) - local match = AC.match(tree, qname, false) + return function(req, query) + local match = AC.match(tree, query:name(), false) if match[1] ~= nil then return action end @@ -61,15 +23,15 @@ end function block.suffix_common(action, suffix_list, common_suffix) local common_len = string.len(common_suffix) local suffix_count = #suffix_list - return function(pkt, qname) + return function(req, query) -- Preliminary check + local qname = query:name() if not string.find(qname, common_suffix, -common_len, true) then return nil end -- String match - local zone = nil for i = 1, suffix_count do - zone = suffix_list[i] + local zone = suffix_list[i] if string.find(qname, zone, -string.len(zone), true) then return action end @@ -80,8 +42,8 @@ end -- @function Block QNAME pattern function block.pattern(action, pattern) - return function(pkt, qname) - if string.find(qname, pattern) then + return function(req, query) + if string.find(query:name(), pattern) then return action end return nil @@ -89,9 +51,9 @@ function block.pattern(action, pattern) end -- @function Evaluate packet in given rules to determine block action -function block.evaluate(block, pkt, qname) - for i = 1, block.rules_count do - local action = block.rules[i](pkt, qname) +function block.evaluate(block, req, query) + for i = 1, #block.rules do + local action = block.rules[i](req, query) if action ~= nil then return action end @@ -101,46 +63,79 @@ end -- @function Block layer implementation block.layer = { - produce = function(state, req, pkt) - -- Check only for first iteration of a query - if state == kres.DONE then - return state - end - local qry = kres.query_current(req) - if kres.query.flag(qry, kres.query.AWAIT_CUT) then - return state - end - local qname = kres.query.qname(qry) - local action = block:evaluate(pkt, qname) + begin = function(state, req) + req = kres.request_t(req) + local action = block:evaluate(req, req:current()) if action == block.DENY then - -- Answer full question - local qclass = kres.query.qclass(qry) - local qtype = kres.query.qtype(qry) - kres.query.flag(qry, kres.query.NO_MINIMIZE + kres.query.CACHED) - pkt:question(qname, qtype, qclass) - pkt:flag(kres.wire.QR) -- Write authority information - pkt:rcode(kres.rcode.NXDOMAIN) - pkt:begin(kres.AUTHORITY) - pkt:add('block.', qclass, kres.type.SOA, 900, + local answer = req.answer + answer:rcode(kres.rcode.NXDOMAIN) + answer:begin(kres.section.AUTHORITY) + answer:put('\5block', 900, answer:qclass(), kres.type.SOA, '\5block\0\0\0\0\0\0\0\0\14\16\0\0\3\132\0\9\58\128\0\0\3\132') return kres.DONE elseif action == block.DROP then return kres.FAIL - else - return state end + return state end } --- @var Default rules -block.rules_count = 1 -block.rules = { block.suffix_common(block.DENY, block.private_zones, 'arpa.') } - -- @function Add rule to block list function block.add(block, rule) - block.rules_count = block.rules_count + 1 return table.insert(block.rules, rule) end +-- @function Convert list of string names to domain names +function block.to_domains(names) + for i, v in ipairs(names) do + names[i] = v:gsub('([^.]*%.)', function (x) + return string.format('%s%s', string.char(x:len()-1), x:sub(1,-2)) + end) + end +end + +-- RFC1918 Private, local, broadcast, test and special zones +local private_zones = { + '10.in-addr.arpa.', + '16.172.in-addr.arpa.', + '17.172.in-addr.arpa.', + '18.172.in-addr.arpa.', + '19.172.in-addr.arpa.', + '20.172.in-addr.arpa.', + '21.172.in-addr.arpa.', + '22.172.in-addr.arpa.', + '23.172.in-addr.arpa.', + '24.172.in-addr.arpa.', + '25.172.in-addr.arpa.', + '26.172.in-addr.arpa.', + '27.172.in-addr.arpa.', + '28.172.in-addr.arpa.', + '29.172.in-addr.arpa.', + '30.172.in-addr.arpa.', + '31.172.in-addr.arpa.', + '168.192.in-addr.arpa.', + -- RFC5735, RFC5737 + '0.in-addr.arpa.', + '127.in-addr.arpa.', + '254.169.in-addr.arpa.', + '2.0.192.in-addr.arpa.', + '100.51.198.in-addr.arpa.', + '113.0.203.in-addr.arpa.', + '255.255.255.255.in-addr.arpa.', + -- IPv6 local, example + '0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.', + '1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.', + 'd.f.ip6.arpa.', + '8.e.f.ip6.arpa.', + '9.e.f.ip6.arpa.', + 'a.e.f.ip6.arpa.', + 'b.e.f.ip6.arpa.', + '8.b.d.0.1.0.0.2.ip6.arpa', +} +block.to_domains(private_zones) + +-- @var Default rules +block.rules = { block.suffix_common(block.DENY, private_zones, '\4arpa') } + return block