[submodule "tests/config/tapered"]
path = tests/config/tapered
url = https://github.com/telemachus/tapered.git
+[submodule "contrib/ljdns"]
+ path = contrib/ljdns
+ url = https://github.com/vavrusa/ljdns.git
'reorder_RR',
'option',
'env',
+ 'dns',
'kres',
'trustanchor',
'libknot_SONAME',
--- /dev/null
+Subproject commit 15fd20225d3f782095a020e312431cff637321d1
# Installed FFI bindings
bindings-install: $(kresd_DIST) $(DESTDIR)$(MODULEDIR)
$(INSTALL) -m 0644 $(kresd_DIST) $(DESTDIR)$(MODULEDIR)
+ $(INSTALL) -d $(DESTDIR)/$(MODULEDIR)/dns
+ $(INSTALL) -m 0644 contrib/ljdns/dns/*.lua $(DESTDIR)/$(MODULEDIR)/dns/
LUA_HAS_SETFUNCS := \
$(shell pkg-config luajit --atleast-version=2.1.0-beta3 && echo 1 || echo 0)
local ffi = require('ffi')
--[[ This file is generated by ./kres-gen.sh ]] ffi.cdef[[
+/* stdlib */
+typedef long time_t;
+struct timeval {
+ time_t tv_sec;
+ time_t tv_usec;
+};
+struct sockaddr {
+ uint16_t sa_family;
+ uint8_t _stub[]; /* Do not touch */
+};
+
+/*
+ * libc APIs
+ */
+void * malloc(size_t size);
+void free(void *ptr);
+char *strdup(const char *s);
+void *calloc(size_t nmemb, size_t size);
+int memcmp(const void *a, const void *b, size_t len);
+int inet_pton(int af, const char *src, void *dst);
typedef struct knot_dump_style knot_dump_style_t;
extern const knot_dump_style_t KNOT_DUMP_STYLE_DEFAULT;
typedef void *(*map_alloc_f)(void *, size_t);
typedef void (*map_free_f)(void *baton, void *ptr);
typedef void (*trace_log_f) (const struct kr_query *, const char *, const char *);
+
+typedef struct knot_dname { uint8_t bytes[?]; } knot_dname_t;
+
typedef enum {KNOT_ANSWER, KNOT_AUTHORITY, KNOT_ADDITIONAL} knot_section_t;
typedef struct {
uint16_t pos;
uint16_t flags;
uint16_t compress_ptr[16];
} knot_rrinfo_t;
-typedef unsigned char knot_dname_t;
typedef unsigned char knot_rdata_t;
typedef struct knot_rdataset knot_rdataset_t;
struct knot_rdataset {
enum kr_rank {KR_RANK_INITIAL, KR_RANK_OMIT, KR_RANK_TRY, KR_RANK_INDET = 4, KR_RANK_BOGUS, KR_RANK_MISMATCH, KR_RANK_MISSING, KR_RANK_INSECURE, KR_RANK_AUTH = 16, KR_RANK_SECURE = 32};
struct knot_rrset {
knot_dname_t *_owner;
- uint16_t type;
+ uint16_t _type;
uint16_t rclass;
knot_rdataset_t rrs;
void *additional;
struct kr_zonecut root_hints;
char _stub[];
};
+const char *knot_strerror(int code);
+int knot_dname_unpack(uint8_t *dst, const knot_dname_t *src, size_t maxlen, const uint8_t *pkt);
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 *);
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 *);
+uint32_t knot_rdata_ttl(const knot_rdata_t *rr);
+void knot_rdata_set_ttl(knot_rdata_t *rr, uint32_t ttl);
size_t knot_rdata_array_size(uint16_t);
knot_rdata_t *knot_rdataset_at(const knot_rdataset_t *, size_t);
int knot_rrset_add_rdata(knot_rrset_t *, const uint8_t *, const uint16_t, const uint32_t, knot_mm_t *);
int knot_pkt_put_question(knot_pkt_t *, const knot_dname_t *, uint16_t, uint16_t);
const knot_rrset_t *knot_pkt_rr(const knot_pktsection_t *, uint16_t);
const knot_pktsection_t *knot_pkt_section(const knot_pkt_t *, knot_section_t);
+uint8_t knot_edns_get_version(const knot_rrset_t *opt_rr);
+void knot_edns_set_version(knot_rrset_t *opt_rr, uint8_t version);
+int knot_edns_add_option(knot_rrset_t *opt_rr, uint16_t code, uint16_t length, const uint8_t *data, void *mm);
+bool knot_edns_has_option(const knot_rrset_t *opt_rr, uint16_t code);
+uint8_t *knot_edns_get_option(const knot_rrset_t *opt_rr, uint16_t code);
+uint16_t knot_edns_opt_get_length(const uint8_t *opt);
+size_t knot_edns_wire_size(knot_rrset_t *opt_rr);
+void knot_edns_set_ext_rcode(knot_rrset_t *opt_rr, uint8_t ext_rcode);
+uint8_t knot_edns_get_ext_rcode(const knot_rrset_t *opt_rr);
struct kr_rplan *kr_resolve_plan(struct kr_request *);
knot_mm_t *kr_resolve_pool(struct kr_request *);
struct kr_query *kr_rplan_push(struct kr_rplan *, struct kr_query *, const knot_dname_t *, uint16_t, uint16_t);
printf -- "local ffi = require('ffi')\n"
printf -- "--[[ This file is generated by ./kres-gen.sh ]] ffi.cdef[[\n"
+## Standard library declarations
+printf "
+/* stdlib */
+typedef long time_t;
+struct timeval {
+ time_t tv_sec;
+ time_t tv_usec;
+};
+struct sockaddr {
+ uint16_t sa_family;
+ uint8_t _stub[]; /* Do not touch */
+};
+
+/*
+ * libc APIs
+ */
+void * malloc(size_t size);
+void free(void *ptr);
+char *strdup(const char *s);
+void *calloc(size_t nmemb, size_t size);
+int memcmp(const void *a, const void *b, size_t len);
+int inet_pton(int af, const char *src, void *dst);
+"
+
## Various types (mainly), from libknot and libkres
printf "
typedef void (*trace_log_f) (const struct kr_query *, const char *, const char *);
"
+# Several types are promoted to structs in order to be able to attach metatypes
+printf "
+typedef struct knot_dname { uint8_t bytes[?]; } knot_dname_t;
+"
+
./scripts/gen-cdefs.sh libkres types <<-EOF
knot_section_t
knot_rrinfo_t
- knot_dname_t
knot_rdata_t
knot_rdataset_t
struct knot_rdataset
}
# No simple way to fixup this rename in ./kres.lua AFAIK.
-genResType "struct knot_rrset" | sed 's/\<owner\>/_owner/'
+genResType "struct knot_rrset" | sed -e 's/\<owner\>/_owner/' -e 's/\<type\>/_type/'
## Some definitions would need too many deps, so shorten them.
## libknot API
./scripts/gen-cdefs.sh libknot functions <<-EOF
+ knot_strerror
# Domain names
+ knot_dname_unpack
knot_dname_from_str
knot_dname_is_equal
knot_dname_is_sub
knot_rdata_rdlen
knot_rdata_data
knot_rdata_array_size
+ knot_rdata_ttl
+ knot_rdata_set_ttl
knot_rdataset_at
knot_rrset_add_rdata
knot_rrset_init_empty
knot_pkt_put_question
knot_pkt_rr
knot_pkt_section
+# EDNS
+ knot_edns_get_version
+ knot_edns_set_version
+ knot_edns_add_option
+ knot_edns_has_option
+ knot_edns_get_option
+ knot_edns_opt_get_length
+ knot_edns_wire_size
+ knot_edns_set_ext_rcode
+ knot_edns_get_ext_rcode
EOF
## libkres API
local kres -- the module
local ffi = require('ffi')
-local bit = require('bit')
-local bor = bit.bor
-local band = bit.band
local C = ffi.C
local knot = ffi.load(libknot_SONAME)
-- Various declarations that are very stable.
-ffi.cdef[[
-/*
- * Data structures
- */
-
-/* stdlib */
-typedef long time_t;
-struct timeval {
- time_t tv_sec;
- time_t tv_usec;
-};
-struct sockaddr {
- uint16_t sa_family;
- uint8_t _stub[]; /* Do not touch */
-};
-
-/*
- * libc APIs
- */
-void * malloc(size_t size);
-void free(void *ptr);
-int inet_pton(int af, const char *src, void *dst);
-]]
-
require('kres-gen')
-- Constant tables
}
})
--- Metatype for RR set. Beware, the indexing is 0-based (rdata, get, tostring).
-local rrset_buflen = (64 + 1) * 1024
-local rrset_buf = ffi.new('char[?]', rrset_buflen)
-local knot_rrset_t = ffi.typeof('knot_rrset_t')
-ffi.metatype( knot_rrset_t, {
- -- beware: `owner` and `rdata` are typed as a plain lua strings
- -- and not the real types they represent.
- __tostring = function(rr) return rr:txt_dump() end,
- __index = {
- owner = function(rr) return ffi.string(rr._owner, knot.knot_dname_size(rr._owner)) end,
- ttl = function(rr) return tonumber(knot.knot_rrset_ttl(rr)) end,
- rdata = function(rr, i)
- local rdata = knot.knot_rdataset_at(rr.rrs, i)
- return ffi.string(knot.knot_rdata_data(rdata), knot.knot_rdata_rdlen(rdata))
- end,
- get = function(rr, i)
- return {owner = rr:owner(),
- ttl = rr:ttl(),
- class = tonumber(rr.rclass),
- type = tonumber(rr.type),
- rdata = rr:rdata(i)}
- end,
- tostring = function(rr, i)
- assert(ffi.istype(knot_rrset_t, rr))
- if rr.rrs.rr_count > 0 then
- local ret
- if i ~= nil then
- ret = knot.knot_rrset_txt_dump_data(rr, i, rrset_buf, rrset_buflen, knot.KNOT_DUMP_STYLE_DEFAULT)
- else
- ret = -1
- end
- return ret >= 0 and ffi.string(rrset_buf)
- end
- end,
-
- -- Dump the rrset in presentation format (dig-like).
- txt_dump = function(rr, style)
- local bufsize = 1024
- local dump = ffi.new('char *[1]', C.malloc(bufsize))
- -- ^ one pointer to a string
- local size = ffi.new('size_t[1]', { bufsize }) -- one size_t = bufsize
-
- local ret = knot.knot_rrset_txt_dump(rr, dump, size,
- style or knot.KNOT_DUMP_STYLE_DEFAULT)
- local result = nil
- if ret >= 0 then
- result = ffi.string(dump[0], ret)
- end
- C.free(dump[0])
- return result
- end,
- },
-})
-
--- Metatype for packet
-local knot_pkt_t = ffi.typeof('knot_pkt_t')
-ffi.metatype( knot_pkt_t, {
- __index = {
- qname = function(pkt)
- local qname = knot.knot_pkt_qname(pkt)
- return ffi.string(qname, knot.knot_dname_size(qname))
- end,
- qclass = function(pkt) return knot.knot_pkt_qclass(pkt) end,
- qtype = function(pkt) return knot.knot_pkt_qtype(pkt) end,
- rcode = function (pkt, val)
- pkt.wire[3] = (val) and bor(band(pkt.wire[3], 0xf0), val) or pkt.wire[3]
- return band(pkt.wire[3], 0x0f)
- end,
- tc = function (pkt, val)
- pkt.wire[2] = bor(pkt.wire[2], (val) and 0x02 or 0x00)
- return band(pkt.wire[2], 0x02)
- end,
- rrsets = function (pkt, section_id)
- local records = {}
- local section = knot.knot_pkt_section(pkt, section_id)
- for i = 1, section.count do
- local rrset = knot.knot_pkt_rr(section, i - 1)
- table.insert(records, rrset)
- end
- return records
- end,
- section = function (pkt, section_id)
- local records = {}
- local section = knot.knot_pkt_section(pkt, section_id)
- for i = 1, section.count do
- local rrset = knot.knot_pkt_rr(section, i - 1)
- for k = 1, rrset.rrs.rr_count do
- table.insert(records, rrset:get(k - 1))
- end
- end
- return records
- end,
- begin = function (pkt, section) return knot.knot_pkt_begin(pkt, section) end,
- put = function (pkt, owner, ttl, rclass, rtype, rdata)
- return C.kr_pkt_put(pkt, owner, ttl, rclass, rtype, rdata, #rdata)
- end,
- clear = function (pkt) return C.kr_pkt_recycle(pkt) end,
- question = function(pkt, qname, qclass, qtype)
- return C.knot_pkt_put_question(pkt, qname, qclass, qtype)
- end,
- },
-})
-- Metatype for query
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,
+ name = function(qry) return qry.sname end,
},
})
-- Metatype for request
}
})
--- Pretty print for domain name
+--- Pretty print for domain name
local function dname2str(dname)
return ffi.string(ffi.gc(C.knot_dname_to_str(nil, dname, 0), C.free))
end
print(string.format(fmt, ...))
end
+-- DNS library bindings
+libknot_CDEFS = 'kres-gen'
+dns = require('dns')
+
-- Resolver bindings
kres = require('kres')
if rawget(kres, 'str2dname') ~= nil then
end
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()
+ if pkt:qtype() == kres.type.AAAA and is_nodata and pkt:qname():equals(qry:name())
and (qry.flags.RESOLVED and qry.parent == nil) then
local extraFlags = kres.mk_qflags({})
extraFlags.DNSSEC_WANT = qry.flags.DNSSEC_WANT
local AC = require('ahocorasick')
local tree = AC.create(zone_list)
return function(_, query)
- local match = AC.match(tree, query:name(), false)
+ local match = AC.match(tree, query:name():towire(), false)
if match ~= nil then
return action
end
local suffix_count = #suffix_list
return function(_, query)
-- Preliminary check
- local qname = query:name()
+ local qname = query:name():towire()
if not string.find(qname, common_suffix, -common_len, true) then
return nil
end
-- Filter QNAME pattern
function policy.pattern(action, pattern)
return function(_, query)
- if string.find(query:name(), pattern) then
+ if string.find(query:name():towire(), pattern) then
return action
end
return nil
local rules = rpz_parse(action, path)
collectgarbage()
return function(_, query)
- local label = query:name()
+ local label = query:name():towire()
local rule = rules[label]
while rule == nil and string.len(label) > 0 do
label = string.sub(label, string.byte(label) + 2)
-- Module interface
local ffi = require('ffi')
-local knot = ffi.load(libknot_SONAME)
local priming = {}
priming.retry_time = 10 * sec -- retry time when priming fail
local function publish_hints(nsset)
local roothints = kres.context().root_hints
-- reset zone cut and clear address list
- ffi.C.kr_zonecut_set(roothints, kres.str2dname("."))
- for dname, addresses in pairs(nsset) do
+ ffi.C.kr_zonecut_set(roothints, dns.dname.parse("."))
+ for name, addresses in pairs(nsset) do
for _, rdata_addr in pairs(addresses) do
- ffi.C.kr_zonecut_add(roothints, dname, rdata_addr)
+ ffi.C.kr_zonecut_add(roothints, dns.dname(name), rdata_addr)
end
end
end
if pkt:rcode() ~= kres.rcode.NOERROR then
warn("[priming] cannot resolve address '%s', type: %d", kres.dname2str(pkt:qname()), pkt:qtype())
else
- local section = pkt:rrsets(kres.section.ANSWER)
- for i = 1, #section do
- local rr = section[i]
- if rr.type == kres.type.A or rr.type == kres.type.AAAA then
- for k = 0, rr.rrs.rr_count-1 do
- local rdata = knot.knot_rdataset_at(rr.rrs, k)
- rdata = ffi.string(rdata, knot.knot_rdata_array_size(knot.knot_rdata_rdlen(rdata)))
- table.insert(internal.nsset[rr:owner()], rdata)
+ for _, rr in ipairs(pkt) do
+ if rr:type() == kres.type.A or rr:type() == kres.type.AAAA then
+ local name = rr:owner():towire()
+ for _, rdata in ipairs(rr) do
+ table.insert(internal.nsset[name], tostring(rdata))
end
end
end
-- These new queries should be resolved from cache.
-- luacheck: no unused args
local function priming_callback(pkt, req)
- pkt = kres.pkt_t(pkt)
+ pkt = dns.topacket(pkt)
-- req = kres.request_t(req)
if pkt:rcode() ~= kres.rcode.NOERROR then
warn("[priming] cannot resolve '.' NS, next priming query in %d seconds", priming.retry_time / sec)
internal.event = event.after(priming.retry_time, internal.prime)
return nil
end
- local section = pkt:rrsets(kres.section.ANSWER)
- for i = 1, #section do
- local rr = section[i]
- if rr.type == kres.type.NS then
+ for i, rr in ipairs(pkt) do
+ if rr:type() == kres.type.NS then
internal.min_ttl = math.min(internal.min_ttl, rr:ttl())
internal.to_resolve = internal.to_resolve + 2 * rr.rrs.rr_count
- for k = 0, rr.rrs.rr_count-1 do
+ for k = 0, rr:count() - 1 do
+ local rdata_wire = rr:rdata(k)
local nsname_text = rr:tostring(k)
- local nsname_wire = rr:rdata(k)
- internal.nsset[nsname_wire] = {}
+ internal.nsset[rdata_wire] = {}
resolve(nsname_text, kres.type.A, kres.class.IN, 0, address_callback)
resolve(nsname_text, kres.type.AAAA, kres.class.IN, 0, address_callback)
end
req = kres.request_t(req)
local qry = req:current()
if qry.stype == kres.type.DNSKEY and not qry.flags.CACHED then
- send_ta_query(qry:name())
+ send_ta_query(qry.name():towire())
end
return state -- do not interfere with normal query processing
end