]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
modules/block: reworked for new FFI bindings
authorMarek Vavruša <marek.vavrusa@nic.cz>
Mon, 3 Aug 2015 02:00:26 +0000 (04:00 +0200)
committerMarek Vavruša <marek.vavrusa@nic.cz>
Mon, 3 Aug 2015 10:27:12 +0000 (12:27 +0200)
daemon/engine.c
modules/block/README.rst
modules/block/block.lua

index f482f2e6d3da225d5a65203a149d21369cbb9a0e..bf47aa472d9bf2cd6066df90612f07060a192df0 100644 (file)
@@ -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();
index cd95296c1f42b7a90e210b8030bccc77a597d235..0802ba38a6fc14970dc77f14074bf45b879ab03b 100644 (file)
@@ -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
index bdad894e51159bce88e8778187baa389d8331199..399b1af4d12746c8092a873b3062ab6ebf62f0c6 100644 (file)
@@ -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