From: Vladimír Čunát Date: Tue, 25 Apr 2017 19:00:53 +0000 (+0200) Subject: qflags: WIP refactor - lua-related fixups X-Git-Tag: v1.4.0~20^2~6 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=91c995e33a8368b4c0a3f774967666382b6a68c5;p=thirdparty%2Fknot-resolver.git qflags: WIP refactor - lua-related fixups --- diff --git a/daemon/README.rst b/daemon/README.rst index f8e70b5f1..42e60676a 100644 --- a/daemon/README.rst +++ b/daemon/README.rst @@ -472,7 +472,7 @@ Environment .. code-block:: lua -- Send query for root DNSKEY, ignore cache - resolve('.', kres.type.DNSKEY, kres.class.IN, kres.query.NO_CACHE) + resolve('.', kres.type.DNSKEY, kres.class.IN, 'NO_CACHE') -- Query for AAAA record resolve('example.com', kres.type.AAAA, kres.class.IN, 0, @@ -1149,7 +1149,7 @@ Example: $ kresd-query.lua www.sub.nic.cz 'assert(kres.dname2str(req:resolved().zone_cut.name) == "nic.cz.")' && echo "yes" yes - $ kresd-query.lua -C 'trust_anchors.config("root.keys")' nic.cz 'assert(req:resolved():hasflag(kres.query.DNSSEC_WANT))' + $ kresd-query.lua -C 'trust_anchors.config("root.keys")' nic.cz 'assert(req:resolved().flags.DNSSEC_WANT)' $ echo $? 0 diff --git a/daemon/bindings.c b/daemon/bindings.c index 853deb867..5930f48ae 100644 --- a/daemon/bindings.c +++ b/daemon/bindings.c @@ -1208,17 +1208,21 @@ static int wrk_resolve(lua_State *L) pkt->opt_rr = knot_rrset_copy(worker->engine->resolver.opt_rr, NULL); if (!pkt->opt_rr) { return kr_error(ENOMEM); - } + } /* Add completion callback */ int ret = 0; - unsigned options = lua_tointeger(L, 4); + const struct kr_qflags *options = lua_topointer(L, 4); + if (!options) { /* but we rely on the lua wrapper when dereferencing non-NULL */ + lua_pushstring(L, "invalid options"); + lua_error(L); + } if (lua_isfunction(L, 5)) { /* Store callback in registry */ lua_pushvalue(L, 5); int cb = luaL_ref(L, LUA_REGISTRYINDEX); - ret = worker_resolve(worker, pkt, options, resolve_callback, (void *) (intptr_t)cb); + ret = worker_resolve(worker, pkt, *options, resolve_callback, (void *) (intptr_t)cb); } else { - ret = worker_resolve(worker, pkt, options, NULL, NULL); + ret = worker_resolve(worker, pkt, *options, NULL, NULL); } knot_rrset_free(&pkt->opt_rr, NULL); @@ -1282,7 +1286,7 @@ static int wrk_stats(lua_State *L) int lib_worker(lua_State *L) { static const luaL_Reg lib[] = { - { "resolve", wrk_resolve }, + { "resolve_unwrapped", wrk_resolve }, { "stats", wrk_stats }, { NULL, NULL } }; diff --git a/daemon/lua/kres-gen.lua b/daemon/lua/kres-gen.lua index 0346fe99d..44cb9b37a 100644 --- a/daemon/lua/kres-gen.lua +++ b/daemon/lua/kres-gen.lua @@ -57,6 +57,40 @@ typedef struct { map_free_f free; void *baton; } map_t; +struct kr_qflags { + _Bool NO_MINIMIZE : 1; + _Bool NO_THROTTLE : 1; + _Bool NO_IPV6 : 1; + _Bool NO_IPV4 : 1; + _Bool TCP : 1; + _Bool RESOLVED : 1; + _Bool AWAIT_IPV4 : 1; + _Bool AWAIT_IPV6 : 1; + _Bool AWAIT_CUT : 1; + _Bool SAFEMODE : 1; + _Bool CACHED : 1; + _Bool NO_CACHE : 1; + _Bool EXPIRING : 1; + _Bool ALLOW_LOCAL : 1; + _Bool DNSSEC_WANT : 1; + _Bool DNSSEC_BOGUS : 1; + _Bool DNSSEC_INSECURE : 1; + _Bool STUB : 1; + _Bool ALWAYS_CUT : 1; + _Bool DNSSEC_WEXPAND : 1; + _Bool PERMISSIVE : 1; + _Bool STRICT : 1; + _Bool BADCOOKIE_AGAIN : 1; + _Bool CNAME : 1; + _Bool REORDER_RR : 1; + _Bool TRACE : 1; + _Bool NO_0X20 : 1; + _Bool DNSSEC_NODS : 1; + _Bool DNSSEC_OPTOUT : 1; + _Bool NONAUTH : 1; + _Bool FORWARD : 1; + _Bool DNS64_MARK : 1; +}; typedef struct { knot_rrset_t **at; size_t len; @@ -113,7 +147,7 @@ struct kr_request { unsigned int rtt; const struct sockaddr *addr; } upstream; - uint32_t options; + struct kr_qflags options; int state; ranked_rr_array_t answ_selected; ranked_rr_array_t auth_selected; @@ -145,7 +179,8 @@ struct kr_query { uint16_t stype; uint16_t sclass; uint16_t id; - uint32_t flags; + struct kr_qflags flags; + struct kr_qflags forward_flags; uint32_t secret; uint16_t fails; uint16_t reorder; @@ -154,18 +189,16 @@ struct kr_query { struct kr_nsrep ns; struct kr_layer_pickle *deferred; uint32_t uid; - /* ^hidden stub^ */ - char _stub[]; + struct kr_query *cname_parent; }; struct kr_context { - uint32_t options; + struct kr_qflags options; knot_rrset_t *opt_rr; map_t trust_anchors; map_t negative_anchors; struct kr_zonecut root_hints; 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; static const int DNS64_MARK = 2147483648;}; int knot_dname_size(const knot_dname_t *); knot_dname_t *knot_dname_from_str(uint8_t *, const char *, size_t); char *knot_dname_to_str(char *, const knot_dname_t *, size_t); @@ -203,6 +236,8 @@ int kr_bitcmp(const char *, const char *, int); int kr_family_len(int); struct sockaddr *kr_straddr_socket(const char *, int); int kr_ranked_rrarray_add(ranked_rr_array_t *, const knot_rrset_t *, uint8_t, _Bool, uint32_t, knot_mm_t *); +void kr_qflags_set(struct kr_qflags *, struct kr_qflags); +void kr_qflags_clear(struct kr_qflags *, struct kr_qflags); knot_rrset_t *kr_ta_get(map_t *, const knot_dname_t *); int kr_ta_add(map_t *, const knot_dname_t *, uint16_t, uint32_t, const uint8_t *, uint16_t); int kr_ta_del(map_t *, const knot_dname_t *); diff --git a/daemon/lua/kres-gen.sh b/daemon/lua/kres-gen.sh index 46b7c119f..95bc497bb 100755 --- a/daemon/lua/kres-gen.sh +++ b/daemon/lua/kres-gen.sh @@ -47,6 +47,7 @@ typedef void (*map_free_f)(void *baton, void *ptr); # generics map_t # libkres + struct kr_qflags rr_array_t struct ranked_rr_array_entry ranked_rr_array_entry_t @@ -70,18 +71,11 @@ genResType "struct knot_rrset" | sed 's/\/_owner/' genResType "struct kr_nsrep" | sed '/union/,$ d' printf "\t/* beware: hidden stub */\n};\n" -genResType "struct kr_query" | sed '/uint32_t forward_flags/,$ d' -printf "\t/* ^hidden stub^ */\n\tchar _stub[];\n};\n" +genResType "struct kr_query" genResType "struct kr_context" | sed '/struct kr_cache/,$ d' printf "\tchar _stub[];\n};\n" -# Getting struct query_flag is a bit complex. -genResType "enum kr_query_flag" | sed -e 's/enum kr_query_flag/struct query_flag/' \ - -e 's/QUERY_NO_THROTTLE/& = 2/' `# a special case for consecutive integers` \ - -e 's@\ kr_resolve_begin() rewrite it. */ - task->req.options |= options; + kr_qflags_set(&task->req.options, options); if (ret != 0) { qr_task_unref(task); diff --git a/lib/README.rst b/lib/README.rst index ff1ee61d5..0e844669a 100644 --- a/lib/README.rst +++ b/lib/README.rst @@ -44,7 +44,7 @@ Layers can also change course of resolution, for example by appending additional if answer:qtype() == kres.type.NS then req = kres.request_t(req) local qry = req:push(answer:qname(), kres.type.SOA, kres.class.IN) - qry.flags = kres.query.AWAIT_CUT + qry.flags.AWAIT_CUT = true end return state end @@ -62,7 +62,7 @@ This **doesn't** block currently processed query, and the newly created sub-requ if answer:qtype() == kres.type.NS then req = kres.request_t(req) local qry = req:push(answer:qname(), kres.type.SOA, kres.class.IN) - qry.flags = kres.query.AWAIT_CUT + qry.flags.AWAIT_CUT = true print('planned SOA query, yielding') return kres.YIELD end @@ -268,7 +268,7 @@ As described in the layers, you can not only retrieve information about current -- Push new query local qry = req:push(pkt:qname(), kres.type.SOA, kres.class.IN) - qry.flags = kres.query.AWAIT_CUT + qry.flags.AWAIT_CUT = true -- Pop the query, this will erase it from resolution plan req:pop(qry) diff --git a/lib/rplan.c b/lib/rplan.c index 50afcdbd3..b78e41835 100644 --- a/lib/rplan.c +++ b/lib/rplan.c @@ -27,6 +27,38 @@ #define QUERY_PROVIDES(q, name, cls, type) \ ((q)->sclass == (cls) && (q)->stype == type && knot_dname_is_equal((q)->sname, name)) +void kr_qflags_set(struct kr_qflags *fl1, struct kr_qflags fl2) +{ + if (!fl1) abort(); + typedef uint32_t ui; + union { + struct kr_qflags flags; + ui uints[sizeof(struct kr_qflags) / sizeof(ui)]; + } tmp1, tmp2; + /* The compiler should be able to optimize all this into simple ORs. */ + tmp1.flags = *fl1; + tmp2.flags = fl2; + for (size_t i = 0; i < sizeof(tmp1.uints) / sizeof(tmp1.uints[0]); ++i) { + tmp1.uints[i] |= tmp2.uints[i]; + } + *fl1 = tmp1.flags; +} +void kr_qflags_clear(struct kr_qflags *fl1, struct kr_qflags fl2) +{ + if (!fl1) abort(); + typedef uint32_t ui; + union { + struct kr_qflags flags; + ui uints[sizeof(struct kr_qflags) / sizeof(ui)]; + } tmp1, tmp2; + /* The compiler should be able to optimize all this into simple ORs. */ + tmp1.flags = *fl1; + tmp2.flags = fl2; + for (size_t i = 0; i < sizeof(tmp1.uints) / sizeof(tmp1.uints[0]); ++i) { + tmp1.uints[i] &= ~tmp2.uints[i]; + } + *fl1 = tmp1.flags; +} static struct kr_query *query_create(knot_mm_t *pool, const knot_dname_t *name, uint32_t uid) { diff --git a/lib/rplan.h b/lib/rplan.h index 3098e4671..633ae4875 100644 --- a/lib/rplan.h +++ b/lib/rplan.h @@ -67,6 +67,13 @@ struct kr_qflags { #undef X }; +/** Combine flags together. This means set union for simple flags. */ +KR_EXPORT +void kr_qflags_set(struct kr_qflags *fl1, struct kr_qflags fl2); + +/** Remove flags. This means set-theoretic difference. */ +KR_EXPORT +void kr_qflags_clear(struct kr_qflags *fl1, struct kr_qflags fl2); /** * Single query representation. diff --git a/modules/dns64/dns64.lua b/modules/dns64/dns64.lua index d0aade245..fd299b77f 100644 --- a/modules/dns64/dns64.lua +++ b/modules/dns64/dns64.lua @@ -22,7 +22,7 @@ mod.layer = { end -- Synthetic AAAA from marked A responses local answer = pkt:section(kres.section.ANSWER) - if bit.band(qry.flags, kres.query.DNS64_MARK) ~= 0 then -- Marked request + if qry.flags.DNS64_MARK then -- Marked request local section = ffi.C.knot_pkt_section(pkt, kres.section.ANSWER) for i = 1, section.count do local orig = ffi.C.knot_pkt_rr(section, i - 1) @@ -52,10 +52,10 @@ mod.layer = { else -- Observe AAAA NODATA responses local is_nodata = (pkt:rcode() == kres.rcode.NOERROR) and (#answer == 0) if pkt:qtype() == kres.type.AAAA and is_nodata and pkt:qname() == qry:name() and qry:final() then - local extraFlags = bit.bor( - bit.band(qry.flags, kres.query.DNSSEC_WANT), - bit.bor(kres.query.DNS64_MARK, kres.query.AWAIT_CUT) - ) + local extraFlags = kres.mk_qflags({}) + extraFlags.DNSSEC_WANT = qry.flags.DNSSEC_WANT + extraFlags.AWAIT_CUT = true + extraFlags.DNS64_MARK = true local next = req:push(pkt:qname(), kres.type.A, kres.class.IN, extraFlags, qry) end end diff --git a/modules/policy/README.rst b/modules/policy/README.rst index a0dca4323..2746497b3 100644 --- a/modules/policy/README.rst +++ b/modules/policy/README.rst @@ -31,7 +31,7 @@ There are several defined actions: * ``MIRROR(ip)`` - mirror query to given IP and continue solving it (useful for partial snooping) * ``REROUTE({{subnet,target}, ...})`` - reroute addresses in response matching given subnet to given target, e.g. ``{'192.0.2.0/24', '127.0.0.0'}`` will rewrite '192.0.2.55' to '127.0.0.55', see :ref:`renumber module ` for more information. * ``QTRACE`` - pretty-print DNS response packets into the log (useful for debugging weird DNS servers). -* ``FLAGS(set, clear)`` - set and/or clear some flags for the query. There can be multiple flags to set/clear, combined by ``bit.bor`` from ``kres.query.*`` values. +* ``FLAGS(set, clear)`` - set and/or clear some flags for the query. There can be multiple flags to set/clear. You can just pass a single flag name (string) or a set of names. .. warning:: The policy module currently only looks at whole DNS requests. The rules won't be re-applied e.g. when following CNAMEs. diff --git a/modules/policy/policy.lua b/modules/policy/policy.lua index dafbdf3ea..c9585ef6e 100644 --- a/modules/policy/policy.lua +++ b/modules/policy/policy.lua @@ -77,7 +77,8 @@ local function stub(target) return function(state, req) local qry = req:current() -- Switch mode to stub resolver, do not track origin zone cut since it's not real authority NS - qry.flags = bit.band(bit.bor(qry.flags, kres.query.STUB), bit.bnot(kres.query.ALWAYS_CUT)) + qry.flags.STUB = true + qry.flags.ALWAYS_CUT = false qry:nslist(list) return state end @@ -96,10 +97,12 @@ local function forward(target) end return function(state, req) local qry = req:current() - req.options = bit.bor(bit.bor(req.options, kres.query.FORWARD), kres.query.NO_MINIMIZE) - qry.flags = bit.band(bit.bor(qry.flags, kres.query.FORWARD), bit.bnot(kres.query.ALWAYS_CUT)) - qry.flags = bit.bor(qry.flags, kres.query.NO_MINIMIZE) - qry.flags = bit.bor(qry.flags, kres.query.AWAIT_CUT) + req.options.FORWARD = true + req.options.NO_MINIMIZE = true + qry.flags.FORWARD = true + qry.flags.ALWAYS_CUT = false + qry.flags.NO_MINIMIZE = true + qry.flags.AWAIT_CUT = true qry:nslist(list) return state end @@ -121,7 +124,8 @@ end local function flags(opts_set, opts_clear) return function(state, req) local qry = req:current() - qry.flags = bit.band(bit.bor(qry.flags, opts_set or 0), bit.bnot(opts_clear or 0)) + ffi.C.kr_qflags_set (qry.flags, kres.mk_qflags(opts_set or {})) + ffi.C.kr_qflags_clear(qry.flags, kres.mk_qflags(opts_clear or {})) return nil -- chain rule end end @@ -267,8 +271,8 @@ function policy.enforce(state, req, action) end elseif action == policy.QTRACE then local qry = req:current() - req.options = bit.bor(req.options, kres.query.TRACE) - qry.flags = bit.bor(qry.flags, kres.query.TRACE) + req.options.TRACE = true + qry.flags.TRACE = true return -- this allows to continue iterating over policy list elseif type(action) == 'function' then return action(state, req) diff --git a/modules/predict/predict.lua b/modules/predict/predict.lua index da0a7a837..79c52f481 100644 --- a/modules/predict/predict.lua +++ b/modules/predict/predict.lua @@ -31,7 +31,7 @@ function predict.drain(ev) local deleted = 0 for key, val in pairs(predict.queue) do local qtype, qname = key:match('(%S*)%s(.*)') - worker.resolve(qname, kres.type[qtype], kres.class.IN, kres.query.NO_CACHE) + worker.resolve(qname, kres.type[qtype], kres.class.IN, 'NO_CACHE') predict.queue[key] = nil deleted = deleted + 1 if deleted >= predict.batch then @@ -187,8 +187,8 @@ predict.layer = { local qrys = req.rplan.resolved for i = 0, (tonumber(qrys.len) - 1) do -- size_t doesn't work for some reason local qry = qrys.at[i] - if bit.band(qry.flags, kres.query.EXPIRING) ~= 0 then - worker.resolve(kres.dname2str(qry.sname), qry.stype, qry.sclass, kres.query.NO_CACHE) + if qry.flags.EXPIRING == true then + worker.resolve(kres.dname2str(qry.sname), qry.stype, qry.sclass, 'NO_CACHE') end end end diff --git a/modules/workarounds/workarounds.lua b/modules/workarounds/workarounds.lua index 48dd5f8c1..d910471f5 100644 --- a/modules/workarounds/workarounds.lua +++ b/modules/workarounds/workarounds.lua @@ -4,7 +4,7 @@ if not policy then modules.load('policy') end local M = {} -- the module function M.config() - policy.add(policy.suffix(policy.FLAGS(kres.query.NO_0X20), { + policy.add(policy.suffix(policy.FLAGS('NO_0X20'), { -- https://github.com/DNS-OARC/dns-violations/blob/master/2017/DVE-2017-0003.md todname('avqs.mcafee.com'), todname('avts.mcafee.com'), @@ -28,7 +28,7 @@ M.layer = { or bit.band(state, bit.bor(kres.FAIL, kres.DONE)) ~= 0 then return state -- quick exit in most cases end - if qry:hasflag(kres.query.AWAIT_CUT) or qry.ns.name == nil + if qry.flags.AWAIT_CUT or qry.ns.name == nil then return state end local name = kres.dname2str(qry.ns.name) if not name then return state end @@ -36,14 +36,14 @@ M.layer = { -- The problematic nameservers: -- (1) rdnsN.turktelekom.com.tr. if string.sub(name, 6) == '.turktelekom.com.tr.' then - qry.flags = bit.bor(qry.flags, - bit.bor(kres.query.NO_0X20, kres.query.NO_MINIMIZE)) + qry.flags.NO_0X20 = true + qry.flags.NO_MINIMIZE = true -- ^ NO_MINIMIZE isn't required for success, as kresd will retry -- after getting refused, but it will speed things up. -- (2) elseif name == 'dns1.edatel.net.co.' then - qry.flags = bit.bor(qry.flags, kres.query.NO_0X20) + qry.flags.NO_0X20 = true end return state diff --git a/scripts/kresd-host.lua b/scripts/kresd-host.lua index 0ca73a138..43244008d 100755 --- a/scripts/kresd-host.lua +++ b/scripts/kresd-host.lua @@ -90,8 +90,7 @@ for i, qtype in ipairs(qtypes) do local rdata = rr:tostring(k - 1) local owner = kres.dname2str(rr:owner()) if qverbose then - if not qry:hasflag(kres.query.DNSSEC_WANT) or - qry:hasflag(kres.query.DNSSEC_INSECURE) then + if not qry.flags.DNSSEC_WANT or qry.flags.DNSSEC_INSECURE then rdata = rdata .. " (insecure)" else rdata = rdata .. " (secure)"