diff options
author | Kim Alvefur <zash@zash.se> | 2020-11-05 22:31:25 +0100 |
---|---|---|
committer | Kim Alvefur <zash@zash.se> | 2020-11-05 22:31:25 +0100 |
commit | 238b2bfc1cdb65ae7d051c2f29c1427149317795 (patch) | |
tree | 58547de6e7795740633c1b93e67c217eb621fe8f /util/dns.lua | |
parent | ce3e3808f5359f481f3ea063220ba71428b26ad5 (diff) | |
parent | 48521ba1538f797f5bef64f5fe5f3a9fb6e68f7f (diff) | |
download | prosody-238b2bfc1cdb65ae7d051c2f29c1427149317795.tar.gz prosody-238b2bfc1cdb65ae7d051c2f29c1427149317795.zip |
Merge 0.11->trunk
Diffstat (limited to 'util/dns.lua')
-rw-r--r-- | util/dns.lua | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/util/dns.lua b/util/dns.lua new file mode 100644 index 00000000..282c4d42 --- /dev/null +++ b/util/dns.lua @@ -0,0 +1,277 @@ +-- 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_char = string.char; +local s_format = string.format; +local s_gsub = string.gsub; +local s_sub = string.sub; +local s_match = string.match; +local s_gmatch = string.gmatch; + +local have_net, net_util = pcall(require, "util.net"); + +if have_net and not net_util.ntop then -- Added in Prosody 0.11 + have_net = false; +end + +local chartohex = {}; + +for c = 0, 255 do + chartohex[s_char(c)] = s_format("%02X", c); +end + +local function tohex(s) + return (s_gsub(s, ".", chartohex)); +end + +-- Converted from +-- http://www.iana.org/assignments/dns-parameters +-- 2020-06-25 + +local classes = { + IN = 1; "IN"; + nil; + CH = 3; "CH"; + HS = 4; "HS"; +}; + +local types = { +"A";"NS";"MD";"MF";"CNAME";"SOA";"MB";"MG";"MR";"NULL";"WKS";"PTR";"HINFO"; +"MINFO";"MX";"TXT";"RP";"AFSDB";"X25";"ISDN";"RT";"NSAP";"NSAP-PTR";"SIG"; +"KEY";"PX";"GPOS";"AAAA";"LOC";"NXT";"EID";"NIMLOC";"SRV";"ATMA";"NAPTR"; +"KX";"CERT";"A6";"DNAME";"SINK";"OPT";"APL";"DS";"SSHFP";"IPSECKEY";"RRSIG"; +"NSEC";"DNSKEY";"DHCID";"NSEC3";"NSEC3PARAM";"TLSA";"SMIMEA";[55]="HIP"; +[56]="NINFO";[57]="RKEY";[58]="TALINK";[59]="CDS";[60]="CDNSKEY";[61]="OPENPGPKEY"; +[62]="CSYNC";[63]="ZONEMD";[99]="SPF";[100]="UINFO";[101]="UID";[102]="GID"; +[103]="UNSPEC";[104]="NID";[105]="L32";[106]="L64";[107]="LP";[108]="EUI48"; +[109]="EUI64";["CSYNC"]=62;["TXT"]=16;["NAPTR"]=35;["A6"]=38;["RP"]=17; +["TALINK"]=58;["NXT"]=30;["MR"]=9;["UINFO"]=100;["X25"]=19;["TKEY"]=249; +["CERT"]=37;["SMIMEA"]=53;[252]="AXFR";[253]="MAILB";["CDS"]=59;[32769]="DLV"; +["RT"]=21;["WKS"]=11;[249]="TKEY";["LP"]=107;[250]="TSIG";["SSHFP"]=44;["DS"]=43; +["ISDN"]=20;["ATMA"]=34;["NS"]=2;[257]="CAA";["PX"]=26;["MX"]=15;["TSIG"]=250; +["EID"]=31;["TLSA"]=52;["GID"]=102;["KX"]=36;["SPF"]=99;["DOA"]=259;["GPOS"]=27; +["IPSECKEY"]=45;["NIMLOC"]=32;["RRSIG"]=46;["UID"]=101;["DNAME"]=39;["NSAP"]=22; +["DNSKEY"]=48;["SINK"]=40;["DHCID"]=49;[32768]="TA";["NSAP-PTR"]=23;["AAAA"]=28; +["PTR"]=12;["MINFO"]=14;["TA"]=32768;["EUI64"]=109;[260]="AMTRELAY"; +["AMTRELAY"]=260;["CDNSKEY"]=60;[259]="DOA";["LOC"]=29;[258]="AVC";["AVC"]=258; +["CAA"]=257;["MB"]=7;["*"]=255;[256]="URI";["URI"]=256;["SRV"]=33;["EUI48"]=108; +[255]="*";[254]="MAILA";["MAILA"]=254;["MAILB"]=253;["CNAME"]=5;[251]="IXFR"; +["APL"]=42;["OPENPGPKEY"]=61;["MD"]=3;["NINFO"]=56;["ZONEMD"]=63;["RKEY"]=57; +["L32"]=105;["NID"]=104;["HIP"]=55;["NSEC"]=47;["DLV"]=32769;["UNSPEC"]=103; +["NSEC3PARAM"]=51;["MF"]=4;["MG"]=8;["AFSDB"]=18;["A"]=1;["SIG"]=24;["NSEC3"]=50; +["HINFO"]=13;["IXFR"]=251;["NULL"]=10;["AXFR"]=252;["KEY"]=25;["OPT"]=41; +["SOA"]=6;["L64"]=106; +} + +local errors = { + NoError = "No Error"; [0] = "NoError"; + FormErr = "Format Error"; "FormErr"; + ServFail = "Server Failure"; "ServFail"; + NXDomain = "Non-Existent Domain"; "NXDomain"; + NotImp = "Not Implemented"; "NotImp"; + Refused = "Query Refused"; "Refused"; + YXDomain = "Name Exists when it should not"; "YXDomain"; + YXRRSet = "RR Set Exists when it should not"; "YXRRSet"; + NXRRSet = "RR Set that should exist does not"; "NXRRSet"; + NotAuth = "Server Not Authoritative for zone"; "NotAuth"; + NotZone = "Name not contained in zone"; "NotZone"; +}; + +-- 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 "."; 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 + +function parsers.A(packet) + return s_format("%d.%d.%d.%d", s_byte(packet, 1, 4)); +end + +local aaaa = { nil, nil, nil, nil, nil, nil, nil, nil, }; +function parsers.AAAA(packet) + local hi, lo, ip, len, token; + for i = 1, 8 do + hi, lo = s_byte(packet, i * 2 - 1, i * 2); + aaaa[i] = s_format("%x", hi * 256 + lo); -- skips leading zeros + end + ip = t_concat(aaaa, ":", 1, 8); + len = (s_match(ip, "^0:[0:]+()") or 1) - 1; + for s in s_gmatch(ip, ":0:[0:]+") do + if len < #s then len, token = #s, s; end -- find longest sequence of zeros + end + return (s_gsub(ip, token or "^0:[0:]+", "::", 1)); +end + +if have_net then + parsers.A = net_util.ntop; + parsers.AAAA = net_util.ntop; +end + +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 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 = classes; + types = types; + errors = errors; + params = params; +}; |