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 *);
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 *);
--- /dev/null
+-- 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