summaryrefslogtreecommitdiffstats
path: root/modules/ta_signal_query/ta_signal_query.lua
diff options
context:
space:
mode:
Diffstat (limited to 'modules/ta_signal_query/ta_signal_query.lua')
-rw-r--r--modules/ta_signal_query/ta_signal_query.lua64
1 files changed, 64 insertions, 0 deletions
diff --git a/modules/ta_signal_query/ta_signal_query.lua b/modules/ta_signal_query/ta_signal_query.lua
new file mode 100644
index 0000000..72c1568
--- /dev/null
+++ b/modules/ta_signal_query/ta_signal_query.lua
@@ -0,0 +1,64 @@
+-- SPDX-License-Identifier: GPL-3.0-or-later
+-- Module implementing RFC 8145 section 5
+-- Signaling Trust Anchor Knowledge in DNS using Key Tag Query
+local kres = require('kres')
+local ffi = require('ffi')
+
+local M = {}
+M.layer = {}
+
+-- transform trust anchor keyset structure for one domain name (in wire format)
+-- to signalling query name like _ta-keytag1-keytag2.example.com.
+-- Returns:
+-- string constructed from valid keytags
+-- nil if no valid keytag is present in keyset
+local function prepare_query_name(keyset, name)
+ if not keyset then return nil end
+ local keytags = {}
+ for _, key in ipairs(keyset) do
+ if key.state == "Valid" then
+ table.insert(keytags, key.key_tag)
+ end
+ end
+ if next(keytags) == nil then return nil end
+
+ table.sort(keytags)
+ local query = "_ta"
+ for _, tag in pairs(keytags) do
+ query = string.format("%s-%04x", query, tag)
+ end
+ if name == "\0" then
+ return query .. "."
+ else
+ return query .. "." .. kres.dname2str(name)
+ end
+end
+
+-- construct keytag query for valid keys and send it as asynchronous query
+-- (does nothing if no valid keys are present at given domain name)
+local function send_ta_query(domain)
+ local keyset = trust_anchors.keysets[domain]
+ local qname = prepare_query_name(keyset, domain)
+ if qname ~= nil then
+ log_info(ffi.C.LOG_GRP_TASIGNALING, "signalling query triggered: %s", qname)
+ -- asynchronous query
+ -- we do not care about result or from where it was obtained
+ event.after(0, function ()
+ resolve(qname, kres.type.NULL, kres.class.IN, "NONAUTH")
+ end)
+ end
+end
+
+-- act on DNSKEY queries which were not answered from cache
+function M.layer.consume(state, req, pkt)
+ -- First check for standard "cached packets": PKT_SIZE_NOWIRE, for efficiency.
+ if pkt.size ~= -1 and pkt:qtype() == kres.type.DNSKEY then
+ local qry = req:current()
+ if not qry.flags.CACHED then
+ send_ta_query(qry:name())
+ end
+ end
+ return state -- do not interfere with normal query processing
+end
+
+return M