From 21927c6ce9279003518f9cc149caa82c308e0445 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Marek=20Vavru=C5=A1a?= Date: Mon, 4 Dec 2017 16:51:11 -0800 Subject: [PATCH] wip --- .gitmodules | 3 + .luacheckrc | 1 + contrib/ljdns | 1 + daemon/daemon.mk | 2 + daemon/lua/kres-gen.lua | 39 +++++- daemon/lua/kres-gen.sh | 46 ++++++- daemon/lua/kres.lua | 133 +------------------- daemon/lua/sandbox.lua | 4 + modules/dns64/dns64.lua | 2 +- modules/policy/policy.lua | 8 +- modules/priming/priming.lua | 34 +++-- modules/ta_signal_query/ta_signal_query.lua | 2 +- 12 files changed, 114 insertions(+), 161 deletions(-) create mode 160000 contrib/ljdns diff --git a/.gitmodules b/.gitmodules index 2c60e9d04..706a3e060 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [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 diff --git a/.luacheckrc b/.luacheckrc index e45db7d05..4b2d9b4de 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -35,6 +35,7 @@ new_read_globals = { 'reorder_RR', 'option', 'env', + 'dns', 'kres', 'trustanchor', 'libknot_SONAME', diff --git a/contrib/ljdns b/contrib/ljdns new file mode 160000 index 000000000..15fd20225 --- /dev/null +++ b/contrib/ljdns @@ -0,0 +1 @@ +Subproject commit 15fd20225d3f782095a020e312431cff637321d1 diff --git a/daemon/daemon.mk b/daemon/daemon.mk index d1cef6c22..64b4d435b 100644 --- a/daemon/daemon.mk +++ b/daemon/daemon.mk @@ -24,6 +24,8 @@ endif # 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) diff --git a/daemon/lua/kres-gen.lua b/daemon/lua/kres-gen.lua index 44919113f..aae5bdd65 100644 --- a/daemon/lua/kres-gen.lua +++ b/daemon/lua/kres-gen.lua @@ -1,5 +1,25 @@ 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; @@ -11,13 +31,15 @@ typedef struct knot_mm { 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 { @@ -165,7 +187,7 @@ struct kr_request { 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; @@ -204,6 +226,8 @@ struct kr_context { 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 *); @@ -212,6 +236,8 @@ 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 *); +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 *); @@ -226,6 +252,15 @@ int knot_pkt_begin(knot_pkt_t *, knot_section_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); diff --git a/daemon/lua/kres-gen.sh b/daemon/lua/kres-gen.sh index 7db4f6f83..54fb0f240 100755 --- a/daemon/lua/kres-gen.sh +++ b/daemon/lua/kres-gen.sh @@ -17,6 +17,30 @@ 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 " @@ -35,10 +59,14 @@ typedef void (*map_free_f)(void *baton, void *ptr); 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 @@ -66,7 +94,7 @@ genResType() { } # No simple way to fixup this rename in ./kres.lua AFAIK. -genResType "struct knot_rrset" | sed 's/\/_owner/' +genResType "struct knot_rrset" | sed -e 's/\/_owner/' -e 's/\/_type/' ## Some definitions would need too many deps, so shorten them. @@ -80,7 +108,9 @@ printf "\tchar _stub[];\n};\n" ## 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 @@ -91,6 +121,8 @@ printf "\tchar _stub[];\n};\n" 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 @@ -105,6 +137,16 @@ printf "\tchar _stub[];\n};\n" 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 diff --git a/daemon/lua/kres.lua b/daemon/lua/kres.lua index 5e7340a82..cd7516c97 100644 --- a/daemon/lua/kres.lua +++ b/daemon/lua/kres.lua @@ -4,37 +4,10 @@ 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 @@ -160,113 +133,11 @@ ffi.metatype( sockaddr_t, { } }) --- 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 @@ -326,7 +197,7 @@ ffi.metatype(ranked_rr_array_t, { } }) --- 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 diff --git a/daemon/lua/sandbox.lua b/daemon/lua/sandbox.lua index e3fd530ab..5852e6c78 100644 --- a/daemon/lua/sandbox.lua +++ b/daemon/lua/sandbox.lua @@ -21,6 +21,10 @@ function log(fmt, ...) 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 diff --git a/modules/dns64/dns64.lua b/modules/dns64/dns64.lua index 2f14f2017..009506833 100644 --- a/modules/dns64/dns64.lua +++ b/modules/dns64/dns64.lua @@ -50,7 +50,7 @@ mod.layer = { 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 diff --git a/modules/policy/policy.lua b/modules/policy/policy.lua index 2e975a673..4ab869687 100644 --- a/modules/policy/policy.lua +++ b/modules/policy/policy.lua @@ -251,7 +251,7 @@ function policy.suffix(action, zone_list) 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 @@ -265,7 +265,7 @@ function policy.suffix_common(action, suffix_list, common_suffix) 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 @@ -283,7 +283,7 @@ 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 @@ -320,7 +320,7 @@ local function rpz_zonefile(action, path) 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) diff --git a/modules/priming/priming.lua b/modules/priming/priming.lua index 6f05d7fdb..7cfb76b5d 100644 --- a/modules/priming/priming.lua +++ b/modules/priming/priming.lua @@ -1,6 +1,5 @@ -- Module interface local ffi = require('ffi') -local knot = ffi.load(libknot_SONAME) local priming = {} priming.retry_time = 10 * sec -- retry time when priming fail @@ -19,10 +18,10 @@ internal.event = nil -- stores event id 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 @@ -46,14 +45,11 @@ local function address_callback(pkt, req) 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 @@ -78,23 +74,21 @@ 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 diff --git a/modules/ta_signal_query/ta_signal_query.lua b/modules/ta_signal_query/ta_signal_query.lua index f14117313..86f9600f9 100644 --- a/modules/ta_signal_query/ta_signal_query.lua +++ b/modules/ta_signal_query/ta_signal_query.lua @@ -54,7 +54,7 @@ function M.layer.consume(state, req, _) 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 -- 2.47.2