]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
policy: follow RFC6303 more closely
authorVladimír Čunát <vladimir.cunat@nic.cz>
Tue, 11 Jul 2017 12:03:49 +0000 (14:03 +0200)
committerVladimír Čunát <vladimir.cunat@nic.cz>
Tue, 11 Jul 2017 12:03:49 +0000 (14:03 +0200)
In particular, try to make the locally-served zones valid,
including SOA and NS in apex, empty non-terminal vs. NXDOMAIN, etc.
I might've missed something, but it should certainly be closer to ideal.

daemon/lua/kres-gen.lua
daemon/lua/kres-gen.sh
modules/policy/policy.lua

index c0dc75e8a69d18ceb615631bde7ac6d68fe7f428..228f628590613f3fbe7ebd6b17307e94199c9996 100644 (file)
@@ -163,8 +163,11 @@ struct kr_context {
        char _stub[];
 };
 struct query_flag {static const int NO_MINIMIZE = 1; static const int NO_THROTTLE = 2; static const int NO_IPV6 = 4; static const int NO_IPV4 = 8; static const int TCP = 16; static const int RESOLVED = 32; static const int AWAIT_IPV4 = 64; static const int AWAIT_IPV6 = 128; static const int AWAIT_CUT = 256; static const int SAFEMODE = 512; static const int CACHED = 1024; static const int NO_CACHE = 2048; static const int EXPIRING = 4096; static const int ALLOW_LOCAL = 8192; static const int DNSSEC_WANT = 16384; static const int DNSSEC_BOGUS = 32768; static const int DNSSEC_INSECURE = 65536; static const int STUB = 131072; static const int ALWAYS_CUT = 262144; static const int DNSSEC_WEXPAND = 524288; static const int PERMISSIVE = 1048576; static const int STRICT = 2097152; static const int BADCOOKIE_AGAIN = 4194304; static const int CNAME = 8388608; static const int REORDER_RR = 16777216; static const int TRACE = 33554432; static const int NO_0X20 = 67108864; static const int DNSSEC_NODS = 134217728; static const int DNSSEC_OPTOUT = 268435456; static const int NONAUTH = 536870912; static const int FORWARD = 1073741824;};
-int knot_dname_size(const knot_dname_t *);
 knot_dname_t *knot_dname_from_str(uint8_t *, const char *, size_t);
+_Bool knot_dname_is_equal(const knot_dname_t *, const knot_dname_t *);
+_Bool knot_dname_is_sub(const knot_dname_t *, const knot_dname_t *);
+int knot_dname_labels(const uint8_t *, const uint8_t *);
+int knot_dname_size(const knot_dname_t *);
 char *knot_dname_to_str(char *, const knot_dname_t *, size_t);
 uint16_t knot_rdata_rdlen(const knot_rdata_t *);
 uint8_t *knot_rdata_data(const knot_rdata_t *);
index 40d501470555d0242ca59c50773a1001b4535e23..cbe3120f8dfd89616579c14bf0a37620c889a863 100755 (executable)
@@ -84,8 +84,11 @@ genResType "enum kr_query_flag" | sed -e 's/enum kr_query_flag/struct query_flag
 ## libknot API
 ./scripts/gen-cdefs.sh libknot functions <<-EOF
 # Domain names
-       knot_dname_size
        knot_dname_from_str
+       knot_dname_is_equal
+       knot_dname_is_sub
+       knot_dname_labels
+       knot_dname_size
        knot_dname_to_str
 # Resource records
        knot_rdata_rdlen
index 858e40369a8a6b5feb00b26f56c44e6105c6cc25..dfe37c30e9f26c6d4bf635c53a3bab2291ebbda7 100644 (file)
@@ -2,6 +2,8 @@ local kres = require('kres')
 local bit = require('bit')
 local ffi = require('ffi')
 
+local todname = kres.str2dname -- not available during module load otherwise
+
 -- Counter of unique rules
 local nextid = 0
 local function getruleid()
@@ -126,37 +128,100 @@ local function flags(opts_set, opts_clear)
        end
 end
 
+local function mkauth_soa(answer, dname, mname)
+       if mname == nil then
+               mname = dname
+       end
+       return answer:put(dname, 900, answer:qclass(), kres.type.SOA,
+               mname .. '\6nobody\7invalid\0\0\0\0\0\0\0\14\16\0\0\3\132\0\9\58\128\0\0\3\132')
+end
+
+local dname_localhost = todname('localhost.')
+
+-- Rule for localhost. zone; see RFC6303, sec. 3
 local function localhost(state, req)
        local qry = req:current()
        local answer = req.answer
        ffi.C.kr_pkt_make_auth_header(answer)
+
+       local is_exact = ffi.C.knot_dname_is_equal(qry.sname, dname_localhost)
+       if not is_exact then
+               answer:rcode(kres.rcode.NXDOMAIN)
+               answer:begin(kres.section.AUTHORITY)
+               mkauth_soa(answer, dname_localhost)
+               return kres.DONE
+       end
+
+       answer:rcode(kres.rcode.NOERROR)
+       answer:begin(kres.section.ANSWER)
        if qry.stype == kres.type.AAAA then
-               answer:begin(kres.section.ANSWER)
-               answer:put(qry.sname, 900, answer:qclass(), kres.type.AAAA, '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1')
+               answer:put(dname_localhost, 900, answer:qclass(), kres.type.AAAA,
+                       '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1')
        elseif qry.stype == kres.type.A then
-               answer:begin(kres.section.ANSWER)
-               answer:put(qry.sname, 900, answer:qclass(), kres.type.A, '\127\0\0\1')  
+               answer:put(dname_localhost, 900, answer:qclass(), kres.type.A, '\127\0\0\1')
+       elseif qry.stype == kres.type.SOA then
+               mkauth_soa(answer, dname_localhost)
+       elseif qry.stype == kres.type.NS then
+               answer:put(dname_localhost, 900, answer:qclass(), kres.type.NS,
+                       dname_localhost)
        else
-               answer:rcode(kres.rcode.NOERROR)
                answer:begin(kres.section.AUTHORITY)
-               answer:put('\7blocked', 900, answer:qclass(), kres.type.SOA,
-                       '\7blocked\0\0\0\0\0\0\0\0\14\16\0\0\3\132\0\9\58\128\0\0\3\132')
+               mkauth_soa(answer, dname_localhost)
        end
        return kres.DONE
 end
 
+local dname_rev4_localhost = todname('1.0.0.127.in-addr.arpa');
+local dname_rev4_localhost_apex = todname('127.in-addr.arpa');
+
+-- Rule for reverse localhost.
+-- Answer with locally served minimal 127.in-addr.arpa domain, only having
+-- a PTR record in 1.0.0.127.in-addr.arpa, and with 1.0...0.ip6.arpa. zone.
+-- TODO: much of this would better be left to the hints module (or coordinated).
 local function localhost_reversed(state, req)
        local qry = req:current()
        local answer = req.answer
+
+       -- classify qry.sname:
+       local is_exact   -- exact dname for localhost
+       local is_apex    -- apex of a locally-served localhost zone
+       local is_nonterm -- empty non-terminal name
+       if ffi.C.knot_dname_is_sub(qry.sname, todname('ip6.arpa.')) then
+               -- exact ::1 query (relying on the calling rule)
+               is_exact = true
+               is_apex = true
+       else
+               -- within 127.in-addr.arpa.
+               local labels = ffi.C.knot_dname_labels(qry.sname, nil)
+               if labels == 3 then
+                       is_exact = false
+                       is_apex = true
+               elseif labels == 4+2 and ffi.C.knot_dname_is_equal(
+                                       qry.sname, dname_rev4_localhost) then
+                       is_exact = true
+               else
+                       is_exact = false
+                       is_apex = false
+                       is_nonterm = ffi.C.knot_dname_is_sub(dname_rev4_localhost, qry.sname)
+               end
+       end
+
        ffi.C.kr_pkt_make_auth_header(answer)
-       if qry.stype == kres.type.PTR then
-               answer:begin(kres.section.ANSWER)
-               answer:put(qry.sname, 900, answer:qclass(), kres.type.PTR, todname('localhost'))        
+       answer:rcode(kres.rcode.NOERROR)
+       answer:begin(kres.section.ANSWER)
+       if is_exact and qry.stype == kres.type.PTR then
+               answer:put(qry.sname, 900, answer:qclass(), kres.type.PTR, dname_localhost)
+       elseif is_apex and qry.stype == kres.type.SOA then
+               mkauth_soa(answer, dname_rev4_localhost_apex, dname_localhost)
+       elseif is_apex and qry.stype == kres.type.NS then
+               answer:put(dname_rev4_localhost_apex, 900, answer:qclass(), kres.type.NS,
+                       dname_localhost)
        else
-               answer:rcode(kres.rcode.NOERROR)
+               if not is_nonterm then
+                       answer:rcode(kres.rcode.NXDOMAIN)
+               end
                answer:begin(kres.section.AUTHORITY)
-               answer:put('\7blocked', 900, answer:qclass(), kres.type.SOA,
-                       '\7blocked\0\0\0\0\0\0\0\0\14\16\0\0\3\132\0\9\58\128\0\0\3\132')
+               mkauth_soa(answer, dname_rev4_localhost_apex, dname_localhost)
        end
        return kres.DONE
 end
@@ -289,8 +354,7 @@ function policy.enforce(state, req, action)
                ffi.C.kr_pkt_make_auth_header(answer)
                answer:rcode(kres.rcode.NXDOMAIN)
                answer:begin(kres.section.AUTHORITY)
-               answer:put('\7blocked', 900, answer:qclass(), kres.type.SOA,
-                       '\7blocked\0\0\0\0\0\0\0\0\14\16\0\0\3\132\0\9\58\128\0\0\3\132')
+               mkauth_soa(answer, '\7blocked\0')
                return kres.DONE
        elseif action == policy.DROP then
                return kres.FAIL
@@ -468,6 +532,7 @@ local private_zones = {
        '127.100.in-addr.arpa.',
 
        -- RFC6303
+       -- localhost_reversed handles ::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.0.ip6.arpa.',
        'd.f.ip6.arpa.',
        '8.e.f.ip6.arpa.',
@@ -494,11 +559,11 @@ policy.special_names = {
                count=0
        },
        {
-               cb=policy.suffix(localhost, {todname('localhost.')}),
+               cb=policy.suffix(localhost, {dname_localhost}),
                count=0
        },
        {
-               cb=policy.suffix_common(localhost_reversed, { 
+               cb=policy.suffix_common(localhost_reversed, {
                        todname('127.in-addr.arpa.'),
                        todname('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.')},
                        todname('arpa.')),