Commit 91c995e3 authored by Vladimír Čunát's avatar Vladimír Čunát

qflags: WIP refactor - lua-related fixups

parent 2f0b8aed
......@@ -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
......
......@@ -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 }
};
......
......@@ -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 *);
......
......@@ -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\>/_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@\<QUERY_\([A-Z_0-9]*\) = \([0-9]*\)@static const int \1 = \2;@g' \
-e 's/,//g'
## libknot API
./scripts/gen-cdefs.sh libknot functions <<-EOF
# Domain names
......@@ -132,6 +126,8 @@ EOF
kr_family_len
kr_straddr_socket
kr_ranked_rrarray_add
kr_qflags_set
kr_qflags_clear
# Trust anchors
kr_ta_get
kr_ta_add
......
......@@ -116,9 +116,6 @@ int inet_pton(int af, const char *src, void *dst);
require('kres-gen')
-- Constants
local query_flag = ffi.new('struct query_flag')
-- Metatype for sockaddr
local addr_buf = ffi.new('char[16]')
local sockaddr_t = ffi.typeof('struct sockaddr')
......@@ -255,12 +252,14 @@ local kr_query_t = ffi.typeof('struct kr_query')
ffi.metatype( kr_query_t, {
__index = {
name = function(qry) return ffi.string(qry.sname, knot.knot_dname_size(qry.sname)) end,
hasflag = function(qry, flag)
return band(qry.flags, flag) ~= 0
-- legacy functions
hasflag = function(qry, flag) -- Note: `flag` is now a string - flag name.
return qry.flags[flag]
end,
resolved = function(qry)
return qry:hasflag(query_flag.RESOLVED)
return qry.flags.RESOLVED
end,
final = function(qry)
return qry:resolved() and (qry.parent == nil)
end,
......@@ -294,10 +293,11 @@ ffi.metatype( kr_request_t, {
end,
push = function(req, qname, qtype, qclass, flags, parent)
assert(req)
flags = kres.mk_qflags(flags) -- compatibility
local rplan = C.kr_resolve_plan(req)
local qry = C.kr_rplan_push(rplan, parent, qname, qclass, qtype)
if qry ~= nil and flags ~= nil then
qry.flags = bor(qry.flags, flags)
C.kr_qflags_set(qry.flags, flags)
end
return qry
end,
......@@ -346,7 +346,29 @@ kres = {
type = ffi.new('struct rr_type'),
section = ffi.new('struct pkt_section'),
rcode = ffi.new('struct pkt_rcode'),
query = query_flag,
-- Create a struct kr_qflags from a single flag name or a list of names.
mk_qflags = function (names)
local kr_qflags = ffi.typeof('struct kr_qflags')
-- compat. hacks
if names == 0 or names == nil then
names = {}
elseif type(names) == 'string' then
names = {names}
elseif ffi.istype(kr_qflags, names) then
return names
end
local fs = ffi.new(kr_qflags)
for k_, name in pairs(names) do
fs[name] = true
end
return fs
end,
-- Don't use anymore - compat. hack: :hasflag() etc. now accept string names
-- but bit operations won't work.
query = setmetatable({}, { __index = function (t, name) return name end }),
CONSUME = 1, PRODUCE = 2, DONE = 4, FAIL = 8, YIELD = 16,
-- Metatypes. Beware that any pointer will be cast silently...
pkt_t = function (udata) return ffi.cast('knot_pkt_t *', udata) end,
......
......@@ -24,11 +24,17 @@ end
-- Resolver bindings
kres = require('kres')
trust_anchors = require('trust_anchors')
resolve = worker.resolve
if rawget(kres, 'str2dname') ~= nil then
todname = kres.str2dname
end
-- Compat. wrapper for query flags.
worker.resolve = function (p1, p2, p3, options, p5)
options = kres.mk_qflags(options)
return worker.resolve_unwrapped (p1, p2, p3, options, p5)
end
resolve = worker.resolve
-- Shorthand for aggregated per-worker information
worker.info = function ()
local t = worker.stats()
......
......@@ -176,7 +176,7 @@ local active_refresh, update -- forwards
local function refresh_plan(keyset, delay, priming, is_initial)
local owner_str = kres.dname2str(keyset.owner) -- maybe fix converting back and forth?
keyset.refresh_ev = event.after(delay, function (ev)
resolve(owner_str, kres.type.DNSKEY, kres.class.IN, kres.query.NO_CACHE,
resolve(owner_str, kres.type.DNSKEY, kres.class.IN, 'NO_CACHE',
function (pkt)
-- Schedule itself with updated timeout
local delay_new = active_refresh(keyset, kres.pkt_t(pkt), is_initial)
......
......@@ -1054,7 +1054,7 @@ int worker_resolve(struct worker_ctx *worker, knot_pkt_t *query, struct kr_qflag
int ret = qr_task_start(task, query);
/* Set options late, as qr_task_start() -> kr_resolve_begin() rewrite it. */
task->req.options |= options;
kr_qflags_set(&task->req.options, options);
if (ret != 0) {
qr_task_unref(task);
......
......@@ -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)
......
......@@ -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)
{
......
......@@ -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.
......
......@@ -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
......
......@@ -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 <mod-renumber>` 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.
......
......@@ -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)
......
......@@ -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
......
......@@ -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
......
......@@ -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)"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment