mirror of
https://github.com/bjc/prosody.git
synced 2025-04-01 20:27:39 +03:00
242 lines
6.2 KiB
Lua
242 lines
6.2 KiB
Lua
-- libunbound based net.adns replacement for Prosody IM
|
|
-- Copyright (C) 2012-2015 Kim Alvefur
|
|
-- Copyright (C) 2012 Waqas Hussain
|
|
--
|
|
-- This file is MIT licensed.
|
|
|
|
local setmetatable = setmetatable;
|
|
local table = table;
|
|
local t_concat = table.concat;
|
|
local t_insert = table.insert;
|
|
local s_byte = string.byte;
|
|
local s_format = string.format;
|
|
local s_sub = string.sub;
|
|
|
|
local iana_data = require "prosody.util.dnsregistry";
|
|
local tohex = require "prosody.util.hex".encode;
|
|
local inet_ntop = require "prosody.util.net".ntop;
|
|
|
|
-- Simplified versions of Waqas DNS parsers
|
|
-- Only the per RR parsers are needed and only feed a single RR
|
|
|
|
local parsers = {};
|
|
|
|
-- No support for pointers, but libunbound appears to take care of that.
|
|
local function readDnsName(packet, pos)
|
|
if s_byte(packet, pos) == 0 then return ".", pos+1; end
|
|
local pack_len, r, len = #packet, {};
|
|
pos = pos or 1;
|
|
repeat
|
|
len = s_byte(packet, pos) or 0;
|
|
t_insert(r, s_sub(packet, pos + 1, pos + len));
|
|
pos = pos + len + 1;
|
|
until len == 0 or pos >= pack_len;
|
|
return t_concat(r, "."), pos;
|
|
end
|
|
|
|
-- These are just simple names.
|
|
parsers.CNAME = readDnsName;
|
|
parsers.NS = readDnsName
|
|
parsers.PTR = readDnsName;
|
|
|
|
local soa_mt = {
|
|
__tostring = function(rr)
|
|
return s_format("%s %s %d %d %d %d %d", rr.mname, rr.rname, rr.serial, rr.refresh, rr.retry, rr.expire, rr.minimum);
|
|
end;
|
|
};
|
|
function parsers.SOA(packet)
|
|
local mname, rname, offset;
|
|
|
|
mname, offset = readDnsName(packet, 1);
|
|
rname, offset = readDnsName(packet, offset);
|
|
|
|
-- Extract all the bytes of these fields in one call
|
|
local
|
|
s1, s2, s3, s4, -- serial
|
|
r1, r2, r3, r4, -- refresh
|
|
t1, t2, t3, t4, -- retry
|
|
e1, e2, e3, e4, -- expire
|
|
m1, m2, m3, m4 -- minimum
|
|
= s_byte(packet, offset, offset + 19);
|
|
|
|
return setmetatable({
|
|
mname = mname;
|
|
rname = rname;
|
|
serial = s1*0x1000000 + s2*0x10000 + s3*0x100 + s4;
|
|
refresh = r1*0x1000000 + r2*0x10000 + r3*0x100 + r4;
|
|
retry = t1*0x1000000 + t2*0x10000 + t3*0x100 + t4;
|
|
expire = e1*0x1000000 + e2*0x10000 + e3*0x100 + e4;
|
|
minimum = m1*0x1000000 + m2*0x10000 + m3*0x100 + m4;
|
|
}, soa_mt);
|
|
end
|
|
|
|
parsers.A = inet_ntop;
|
|
parsers.AAAA = inet_ntop;
|
|
|
|
local mx_mt = {
|
|
__tostring = function(rr)
|
|
return s_format("%d %s", rr.pref, rr.mx)
|
|
end
|
|
};
|
|
function parsers.MX(packet)
|
|
local name = readDnsName(packet, 3);
|
|
local b1,b2 = s_byte(packet, 1, 2);
|
|
return setmetatable({
|
|
pref = b1*256+b2;
|
|
mx = name;
|
|
}, mx_mt);
|
|
end
|
|
|
|
local srv_mt = {
|
|
__tostring = function(rr)
|
|
return s_format("%d %d %d %s", rr.priority, rr.weight, rr.port, rr.target);
|
|
end
|
|
};
|
|
function parsers.SRV(packet)
|
|
local name = readDnsName(packet, 7);
|
|
local b1, b2, b3, b4, b5, b6 = s_byte(packet, 1, 6);
|
|
return setmetatable({
|
|
priority = b1*256+b2;
|
|
weight = b3*256+b4;
|
|
port = b5*256+b6;
|
|
target = name;
|
|
}, srv_mt);
|
|
end
|
|
|
|
local txt_mt = { __tostring = t_concat };
|
|
function parsers.TXT(packet)
|
|
local pack_len = #packet;
|
|
local r, pos, len = {}, 1;
|
|
repeat
|
|
len = s_byte(packet, pos) or 0;
|
|
t_insert(r, s_sub(packet, pos + 1, pos + len));
|
|
pos = pos + len + 1;
|
|
until pos >= pack_len;
|
|
return setmetatable(r, txt_mt);
|
|
end
|
|
|
|
parsers.SPF = parsers.TXT;
|
|
|
|
-- Acronyms from RFC 7218
|
|
local tlsa_usages = {
|
|
[0] = "PKIX-CA";
|
|
[1] = "PKIX-EE";
|
|
[2] = "DANE-TA";
|
|
[3] = "DANE-EE";
|
|
[255] = "PrivCert";
|
|
};
|
|
local tlsa_selectors = {
|
|
[0] = "Cert",
|
|
[1] = "SPKI",
|
|
[255] = "PrivSel",
|
|
};
|
|
local tlsa_match_types = {
|
|
[0] = "Full",
|
|
[1] = "SHA2-256",
|
|
[2] = "SHA2-512",
|
|
[255] = "PrivMatch",
|
|
};
|
|
local tlsa_mt = {
|
|
__tostring = function(rr)
|
|
return s_format("%s %s %s %s",
|
|
tlsa_usages[rr.use] or rr.use,
|
|
tlsa_selectors[rr.select] or rr.select,
|
|
tlsa_match_types[rr.match] or rr.match,
|
|
tohex(rr.data));
|
|
end;
|
|
__index = {
|
|
getUsage = function(rr) return tlsa_usages[rr.use] end;
|
|
getSelector = function(rr) return tlsa_selectors[rr.select] end;
|
|
getMatchType = function(rr) return tlsa_match_types[rr.match] end;
|
|
}
|
|
};
|
|
function parsers.TLSA(packet)
|
|
local use, select, match = s_byte(packet, 1,3);
|
|
return setmetatable({
|
|
use = use;
|
|
select = select;
|
|
match = match;
|
|
data = s_sub(packet, 4);
|
|
}, tlsa_mt);
|
|
end
|
|
|
|
local svcb_params = {"alpn"; "no-default-alpn"; "port"; "ipv4hint"; "ech"; "ipv6hint"};
|
|
setmetatable(svcb_params, {__index = function(_, n) return "key" .. tostring(n); end});
|
|
|
|
local svcb_mt = {
|
|
__tostring = function (rr)
|
|
local kv = {};
|
|
for i = 1, #rr.fields do
|
|
t_insert(kv, s_format("%s=%q", svcb_params[rr.fields[i].key], tostring(rr.fields[i].value)));
|
|
-- FIXME the =value part may be omitted when the value is "empty"
|
|
end
|
|
return s_format("%d %s %s", rr.prio, rr.name, t_concat(kv, " "));
|
|
end;
|
|
};
|
|
local svbc_ip_mt = {__tostring = function(ip) return t_concat(ip, ", "); end}
|
|
|
|
function parsers.SVCB(packet)
|
|
local prio_h, prio_l = packet:byte(1,2);
|
|
local prio = prio_h*256+prio_l;
|
|
local name, pos = readDnsName(packet, 3);
|
|
local fields = {};
|
|
while #packet > pos do
|
|
local key_h, key_l = packet:byte(pos+0,pos+1);
|
|
local len_h, len_l = packet:byte(pos+2,pos+3);
|
|
local key = key_h*256+key_l;
|
|
local len = len_h*256+len_l;
|
|
local value = packet:sub(pos+4,pos+4-1+len)
|
|
if key == 1 then
|
|
value = setmetatable(parsers.TXT(value), svbc_ip_mt);
|
|
elseif key == 3 then
|
|
local port_h, port_l = value:byte(1,2);
|
|
local port = port_h*256+port_l;
|
|
value = port;
|
|
elseif key == 4 then
|
|
local ip = {};
|
|
for i = 1, #value, 4 do
|
|
t_insert(ip, parsers.A(value:sub(i, i+3)));
|
|
end
|
|
value = setmetatable(ip, svbc_ip_mt);
|
|
elseif key == 6 then
|
|
local ip = {};
|
|
for i = 1, #value, 16 do
|
|
t_insert(ip, parsers.AAAA(value:sub(i, i+15)));
|
|
end
|
|
value = setmetatable(ip, svbc_ip_mt);
|
|
end
|
|
t_insert(fields, { key = key, value = value, len = len });
|
|
pos = pos+len+4;
|
|
end
|
|
return setmetatable({
|
|
prio = prio, name = name, fields = fields,
|
|
}, svcb_mt);
|
|
end
|
|
|
|
parsers.HTTPS = parsers.SVCB;
|
|
|
|
local params = {
|
|
TLSA = {
|
|
use = tlsa_usages;
|
|
select = tlsa_selectors;
|
|
match = tlsa_match_types;
|
|
};
|
|
};
|
|
|
|
local fallback_mt = {
|
|
__tostring = function(rr)
|
|
return s_format([[\# %d %s]], #rr.raw, tohex(rr.raw));
|
|
end;
|
|
};
|
|
local function fallback_parser(packet)
|
|
return setmetatable({ raw = packet },fallback_mt);
|
|
end
|
|
setmetatable(parsers, { __index = function() return fallback_parser end });
|
|
|
|
return {
|
|
parsers = parsers;
|
|
classes = iana_data.classes;
|
|
types = iana_data.types;
|
|
errors = iana_data.errors;
|
|
params = params;
|
|
};
|