prosody/net/resolvers/service.lua
Kim Alvefur 07ef92dbd8 net.resolvers.service: Only do DANE with secure SRV records
If this seems backwards, that' because it is but the API isn't really
designed to easily pass along details from each resolution step onto the
next.
2021-07-18 23:25:45 +02:00

98 lines
2.4 KiB
Lua

local adns = require "net.adns";
local basic = require "net.resolvers.basic";
local inet_pton = require "util.net".pton;
local idna_to_ascii = require "util.encodings".idna.to_ascii;
local unpack = table.unpack or unpack; -- luacheck: ignore 113
local methods = {};
local resolver_mt = { __index = methods };
-- Find the next target to connect to, and
-- pass it to cb()
function methods:next(cb)
if self.targets then
if not self.resolver then
if #self.targets == 0 then
cb(nil);
return;
end
local next_target = table.remove(self.targets, 1);
self.resolver = basic.new(unpack(next_target, 1, 4));
end
self.resolver:next(function (...)
if ... == nil then
self.resolver = nil;
self:next(cb);
else
cb(...);
end
end);
return;
end
if not self.hostname then
-- FIXME report IDNA error
cb(nil);
return;
end
local targets = {};
local function ready()
self.targets = targets;
self:next(cb);
end
-- Resolve DNS to target list
local dns_resolver = adns.resolver();
dns_resolver:lookup(function (answer, err)
if not answer and not err then
-- net.adns returns nil if there are zero records or nxdomain
answer = {};
end
if answer then
if self.extra and not answer.secure then
self.extra.use_dane = false;
end
if #answer == 0 then
if self.extra and self.extra.default_port then
table.insert(targets, { self.hostname, self.extra.default_port, self.conn_type, self.extra });
end
ready();
return;
end
if #answer == 1 and answer[1].srv.target == "." then -- No service here
ready();
return;
end
table.sort(answer, function (a, b) return a.srv.priority < b.srv.priority end);
for _, record in ipairs(answer) do
table.insert(targets, { record.srv.target, record.srv.port, self.conn_type, self.extra });
end
end
ready();
end, "_" .. self.service .. "._" .. self.conn_type .. "." .. self.hostname, "SRV", "IN");
end
local function new(hostname, service, conn_type, extra)
local is_ip = inet_pton(hostname);
if not is_ip and hostname:sub(1,1) == '[' then
is_ip = inet_pton(hostname:sub(2,-2));
end
if is_ip and extra and extra.default_port then
return basic.new(hostname, extra.default_port, conn_type, extra);
end
return setmetatable({
hostname = idna_to_ascii(hostname);
service = service;
conn_type = conn_type or "tcp";
extra = extra;
}, resolver_mt);
end
return {
new = new;
};