.. 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,
$ 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
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);
int lib_worker(lua_State *L)
{
static const luaL_Reg lib[] = {
- { "resolve", wrk_resolve },
+ { "resolve_unwrapped", wrk_resolve },
{ "stats", wrk_stats },
{ NULL, NULL }
};
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;
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;
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;
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);
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 *);
# generics
map_t
# libkres
+ struct kr_qflags
rr_array_t
struct ranked_rr_array_entry
ranked_rr_array_entry_t
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
kr_family_len
kr_straddr_socket
kr_ranked_rrarray_add
+ kr_qflags_set
+ kr_qflags_clear
# Trust anchors
kr_ta_get
kr_ta_add
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')
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,
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,
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,
-- 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()
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)
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);
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
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
-- 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)
#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)
{
#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.
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)
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
* ``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.
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
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
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
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)
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
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
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'),
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
-- 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
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)"