summaryrefslogtreecommitdiffstats
path: root/modules/ta_signal_query/ta_signal_query.lua
blob: 72c15683a8f7df35e74d71f0b3e1738521f4cffd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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