From: Vítězslav Kříž Date: Wed, 1 Nov 2017 17:26:54 +0000 (+0100) Subject: priming: implement priming queries as module X-Git-Tag: v1.5.1~11^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9e90f9fb20edd2d657d3c967aa143d7c7642947e;p=thirdparty%2Fknot-resolver.git priming: implement priming queries as module --- diff --git a/daemon/lua/kres-gen.lua b/daemon/lua/kres-gen.lua index 7d41beb38..8410f21bf 100644 --- a/daemon/lua/kres-gen.lua +++ b/daemon/lua/kres-gen.lua @@ -210,6 +210,7 @@ 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 *); +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 *); void knot_rrset_init_empty(knot_rrset_t *); @@ -244,6 +245,8 @@ 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); +int kr_zonecut_add(struct kr_zonecut *, const knot_dname_t *, const knot_rdata_t *); +void kr_zonecut_set(struct kr_zonecut *, const knot_dname_t *); 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 43ca87f5e..865cd1ce6 100755 --- a/daemon/lua/kres-gen.sh +++ b/daemon/lua/kres-gen.sh @@ -89,6 +89,7 @@ printf "\tchar _stub[];\n};\n" # Resource records knot_rdata_rdlen knot_rdata_data + knot_rdata_array_size knot_rdataset_at knot_rrset_add_rdata knot_rrset_init_empty @@ -132,6 +133,8 @@ EOF kr_ranked_rrarray_add kr_qflags_set kr_qflags_clear + kr_zonecut_add + kr_zonecut_set # Trust anchors kr_ta_get kr_ta_add diff --git a/doc/modules.rst b/doc/modules.rst index 4b463fff9..d3c5eec1c 100644 --- a/doc/modules.rst +++ b/doc/modules.rst @@ -26,3 +26,4 @@ Knot DNS Resolver modules .. include:: ../modules/workarounds/README.rst .. include:: ../modules/dnstap/README.rst .. include:: ../modules/ta_signal_query/README.rst +.. include:: ../modules/priming/README.rst diff --git a/modules/modules.mk b/modules/modules.mk index 157648df6..6734b6c3f 100644 --- a/modules/modules.mk +++ b/modules/modules.mk @@ -33,7 +33,8 @@ modules_TARGETS += ketcd \ daf \ workarounds \ version \ - ta_signal_query + ta_signal_query \ + priming endif # Make C module diff --git a/modules/priming/README.rst b/modules/priming/README.rst new file mode 100644 index 000000000..3a1d3e7b8 --- /dev/null +++ b/modules/priming/README.rst @@ -0,0 +1,17 @@ +.. _mod-priming: + +Priming module +-------------- + +The module for Initializing a DNS Resolver with Priming Queries implemented +according to RFC 8109. Purpose of the module is to keep up-to-date list of +root DNS servers and associated IP addresses. + +Result of successful priming query replaces root hints distributed with +the resolver software. Unlike other DNS resolvers, Knot Resolver caches +result of priming query on disk and keeps the data between restarts until +TTL expires. + +This module is enabled by default and it is not recommended to disable it. +For debugging purposes you may disable the module by appending +`modules.unload('priming')` to your configuration. diff --git a/modules/priming/priming.lua b/modules/priming/priming.lua new file mode 100644 index 000000000..6f05d7fdb --- /dev/null +++ b/modules/priming/priming.lua @@ -0,0 +1,128 @@ +-- Module interface +local ffi = require('ffi') +local knot = ffi.load(libknot_SONAME) + +local priming = {} +priming.retry_time = 10 * sec -- retry time when priming fail + +-- internal state variables and functions +local internal = {} +internal.nsset = {} -- set of resolved nameservers +internal.min_ttl = 0 -- minimal TTL of NS records +internal.to_resolve = 0 -- number of pending queries to A or AAAA +internal.prime = {} -- function triggering priming query +internal.event = nil -- stores event id + +-- Copy hints from nsset table to resolver engine +-- These addresses replace root hints loaded by default from file. +-- They are stored outside cache and cache flush will not affect them. +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 + for _, rdata_addr in pairs(addresses) do + ffi.C.kr_zonecut_add(roothints, dname, rdata_addr) + end + end +end + +-- Count A and AAAA addresses in nsset +local function count_addresses(nsset) + local count = 0 + for _, addresses in pairs(nsset) do + count = count + #addresses + end + return count +end + +-- Callback for response from A or AAAA query for root nameservers +-- address is added to table internal.nsset. +-- When all response is processed internal.nsset is published in resolver engine +-- luacheck: no unused args +local function address_callback(pkt, req) + pkt = kres.pkt_t(pkt) + -- req = kres.request_t(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) + end + end + end + end + internal.to_resolve = internal.to_resolve - 1 + if internal.to_resolve == 0 then + if count_addresses(internal.nsset) == 0 then + warn("[priming] cannot resolve any root server address, next priming query in %d seconds", priming.retry_time / sec) + internal.event = event.after(priming.retry_time, internal.prime) + else + publish_hints(internal.nsset) + if verbose() then + log("[priming] triggered priming query, next in %d seconds", internal.min_ttl) + end + internal.event = event.after(internal.min_ttl * sec, internal.prime) + end + end +end + +-- Callback for priming query ('.' NS) +-- For every NS record creates two separate queries for A and AAAA. +-- These new queries should be resolved from cache. +-- luacheck: no unused args +local function priming_callback(pkt, req) + pkt = kres.pkt_t(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 + 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 + local nsname_text = rr:tostring(k) + local nsname_wire = rr:rdata(k) + internal.nsset[nsname_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 + end + end +end + +-- trigger priming query +function internal.prime() + internal.min_ttl = math.max(1, cache.max_ttl()) -- sanity check for disabled cache + internal.nsset = {} + internal.to_resolve = 0 + resolve(".", kres.type.NS, kres.class.IN, 0, priming_callback) +end + +function priming.init() + if internal.event then + error("Priming module is already loaded.") + else + internal.event = event.after(0 , internal.prime) + end +end + +function priming.deinit() + if internal.event then + event.cancel(internal.event) + internal.event = nil + end +end + +return priming diff --git a/modules/priming/priming.mk b/modules/priming/priming.mk new file mode 100644 index 000000000..b5043b139 --- /dev/null +++ b/modules/priming/priming.mk @@ -0,0 +1,2 @@ +priming_SOURCES := priming.lua +$(call make_lua_module,priming)