Mercurial > prosody-hg
comparison util/dns.lua @ 11200:bf8f2da84007
Merge 0.11->trunk
| author | Kim Alvefur <zash@zash.se> |
|---|---|
| date | Thu, 05 Nov 2020 22:31:25 +0100 |
| parents | b3773b1b90a1 |
| children | d0dfd48806f9 |
comparison
equal
deleted
inserted
replaced
| 11199:6c7c50a4de32 | 11200:bf8f2da84007 |
|---|---|
| 1 -- libunbound based net.adns replacement for Prosody IM | |
| 2 -- Copyright (C) 2012-2015 Kim Alvefur | |
| 3 -- Copyright (C) 2012 Waqas Hussain | |
| 4 -- | |
| 5 -- This file is MIT licensed. | |
| 6 | |
| 7 local setmetatable = setmetatable; | |
| 8 local table = table; | |
| 9 local t_concat = table.concat; | |
| 10 local t_insert = table.insert; | |
| 11 local s_byte = string.byte; | |
| 12 local s_char = string.char; | |
| 13 local s_format = string.format; | |
| 14 local s_gsub = string.gsub; | |
| 15 local s_sub = string.sub; | |
| 16 local s_match = string.match; | |
| 17 local s_gmatch = string.gmatch; | |
| 18 | |
| 19 local have_net, net_util = pcall(require, "util.net"); | |
| 20 | |
| 21 if have_net and not net_util.ntop then -- Added in Prosody 0.11 | |
| 22 have_net = false; | |
| 23 end | |
| 24 | |
| 25 local chartohex = {}; | |
| 26 | |
| 27 for c = 0, 255 do | |
| 28 chartohex[s_char(c)] = s_format("%02X", c); | |
| 29 end | |
| 30 | |
| 31 local function tohex(s) | |
| 32 return (s_gsub(s, ".", chartohex)); | |
| 33 end | |
| 34 | |
| 35 -- Converted from | |
| 36 -- http://www.iana.org/assignments/dns-parameters | |
| 37 -- 2020-06-25 | |
| 38 | |
| 39 local classes = { | |
| 40 IN = 1; "IN"; | |
| 41 nil; | |
| 42 CH = 3; "CH"; | |
| 43 HS = 4; "HS"; | |
| 44 }; | |
| 45 | |
| 46 local types = { | |
| 47 "A";"NS";"MD";"MF";"CNAME";"SOA";"MB";"MG";"MR";"NULL";"WKS";"PTR";"HINFO"; | |
| 48 "MINFO";"MX";"TXT";"RP";"AFSDB";"X25";"ISDN";"RT";"NSAP";"NSAP-PTR";"SIG"; | |
| 49 "KEY";"PX";"GPOS";"AAAA";"LOC";"NXT";"EID";"NIMLOC";"SRV";"ATMA";"NAPTR"; | |
| 50 "KX";"CERT";"A6";"DNAME";"SINK";"OPT";"APL";"DS";"SSHFP";"IPSECKEY";"RRSIG"; | |
| 51 "NSEC";"DNSKEY";"DHCID";"NSEC3";"NSEC3PARAM";"TLSA";"SMIMEA";[55]="HIP"; | |
| 52 [56]="NINFO";[57]="RKEY";[58]="TALINK";[59]="CDS";[60]="CDNSKEY";[61]="OPENPGPKEY"; | |
| 53 [62]="CSYNC";[63]="ZONEMD";[99]="SPF";[100]="UINFO";[101]="UID";[102]="GID"; | |
| 54 [103]="UNSPEC";[104]="NID";[105]="L32";[106]="L64";[107]="LP";[108]="EUI48"; | |
| 55 [109]="EUI64";["CSYNC"]=62;["TXT"]=16;["NAPTR"]=35;["A6"]=38;["RP"]=17; | |
| 56 ["TALINK"]=58;["NXT"]=30;["MR"]=9;["UINFO"]=100;["X25"]=19;["TKEY"]=249; | |
| 57 ["CERT"]=37;["SMIMEA"]=53;[252]="AXFR";[253]="MAILB";["CDS"]=59;[32769]="DLV"; | |
| 58 ["RT"]=21;["WKS"]=11;[249]="TKEY";["LP"]=107;[250]="TSIG";["SSHFP"]=44;["DS"]=43; | |
| 59 ["ISDN"]=20;["ATMA"]=34;["NS"]=2;[257]="CAA";["PX"]=26;["MX"]=15;["TSIG"]=250; | |
| 60 ["EID"]=31;["TLSA"]=52;["GID"]=102;["KX"]=36;["SPF"]=99;["DOA"]=259;["GPOS"]=27; | |
| 61 ["IPSECKEY"]=45;["NIMLOC"]=32;["RRSIG"]=46;["UID"]=101;["DNAME"]=39;["NSAP"]=22; | |
| 62 ["DNSKEY"]=48;["SINK"]=40;["DHCID"]=49;[32768]="TA";["NSAP-PTR"]=23;["AAAA"]=28; | |
| 63 ["PTR"]=12;["MINFO"]=14;["TA"]=32768;["EUI64"]=109;[260]="AMTRELAY"; | |
| 64 ["AMTRELAY"]=260;["CDNSKEY"]=60;[259]="DOA";["LOC"]=29;[258]="AVC";["AVC"]=258; | |
| 65 ["CAA"]=257;["MB"]=7;["*"]=255;[256]="URI";["URI"]=256;["SRV"]=33;["EUI48"]=108; | |
| 66 [255]="*";[254]="MAILA";["MAILA"]=254;["MAILB"]=253;["CNAME"]=5;[251]="IXFR"; | |
| 67 ["APL"]=42;["OPENPGPKEY"]=61;["MD"]=3;["NINFO"]=56;["ZONEMD"]=63;["RKEY"]=57; | |
| 68 ["L32"]=105;["NID"]=104;["HIP"]=55;["NSEC"]=47;["DLV"]=32769;["UNSPEC"]=103; | |
| 69 ["NSEC3PARAM"]=51;["MF"]=4;["MG"]=8;["AFSDB"]=18;["A"]=1;["SIG"]=24;["NSEC3"]=50; | |
| 70 ["HINFO"]=13;["IXFR"]=251;["NULL"]=10;["AXFR"]=252;["KEY"]=25;["OPT"]=41; | |
| 71 ["SOA"]=6;["L64"]=106; | |
| 72 } | |
| 73 | |
| 74 local errors = { | |
| 75 NoError = "No Error"; [0] = "NoError"; | |
| 76 FormErr = "Format Error"; "FormErr"; | |
| 77 ServFail = "Server Failure"; "ServFail"; | |
| 78 NXDomain = "Non-Existent Domain"; "NXDomain"; | |
| 79 NotImp = "Not Implemented"; "NotImp"; | |
| 80 Refused = "Query Refused"; "Refused"; | |
| 81 YXDomain = "Name Exists when it should not"; "YXDomain"; | |
| 82 YXRRSet = "RR Set Exists when it should not"; "YXRRSet"; | |
| 83 NXRRSet = "RR Set that should exist does not"; "NXRRSet"; | |
| 84 NotAuth = "Server Not Authoritative for zone"; "NotAuth"; | |
| 85 NotZone = "Name not contained in zone"; "NotZone"; | |
| 86 }; | |
| 87 | |
| 88 -- Simplified versions of Waqas DNS parsers | |
| 89 -- Only the per RR parsers are needed and only feed a single RR | |
| 90 | |
| 91 local parsers = {}; | |
| 92 | |
| 93 -- No support for pointers, but libunbound appears to take care of that. | |
| 94 local function readDnsName(packet, pos) | |
| 95 if s_byte(packet, pos) == 0 then return "."; end | |
| 96 local pack_len, r, len = #packet, {}; | |
| 97 pos = pos or 1; | |
| 98 repeat | |
| 99 len = s_byte(packet, pos) or 0; | |
| 100 t_insert(r, s_sub(packet, pos + 1, pos + len)); | |
| 101 pos = pos + len + 1; | |
| 102 until len == 0 or pos >= pack_len; | |
| 103 return t_concat(r, "."), pos; | |
| 104 end | |
| 105 | |
| 106 -- These are just simple names. | |
| 107 parsers.CNAME = readDnsName; | |
| 108 parsers.NS = readDnsName | |
| 109 parsers.PTR = readDnsName; | |
| 110 | |
| 111 local soa_mt = { | |
| 112 __tostring = function(rr) | |
| 113 return s_format("%s %s %d %d %d %d %d", rr.mname, rr.rname, rr.serial, rr.refresh, rr.retry, rr.expire, rr.minimum); | |
| 114 end; | |
| 115 }; | |
| 116 function parsers.SOA(packet) | |
| 117 local mname, rname, offset; | |
| 118 | |
| 119 mname, offset = readDnsName(packet, 1); | |
| 120 rname, offset = readDnsName(packet, offset); | |
| 121 | |
| 122 -- Extract all the bytes of these fields in one call | |
| 123 local | |
| 124 s1, s2, s3, s4, -- serial | |
| 125 r1, r2, r3, r4, -- refresh | |
| 126 t1, t2, t3, t4, -- retry | |
| 127 e1, e2, e3, e4, -- expire | |
| 128 m1, m2, m3, m4 -- minimum | |
| 129 = s_byte(packet, offset, offset + 19); | |
| 130 | |
| 131 return setmetatable({ | |
| 132 mname = mname; | |
| 133 rname = rname; | |
| 134 serial = s1*0x1000000 + s2*0x10000 + s3*0x100 + s4; | |
| 135 refresh = r1*0x1000000 + r2*0x10000 + r3*0x100 + r4; | |
| 136 retry = t1*0x1000000 + t2*0x10000 + t3*0x100 + t4; | |
| 137 expire = e1*0x1000000 + e2*0x10000 + e3*0x100 + e4; | |
| 138 minimum = m1*0x1000000 + m2*0x10000 + m3*0x100 + m4; | |
| 139 }, soa_mt); | |
| 140 end | |
| 141 | |
| 142 function parsers.A(packet) | |
| 143 return s_format("%d.%d.%d.%d", s_byte(packet, 1, 4)); | |
| 144 end | |
| 145 | |
| 146 local aaaa = { nil, nil, nil, nil, nil, nil, nil, nil, }; | |
| 147 function parsers.AAAA(packet) | |
| 148 local hi, lo, ip, len, token; | |
| 149 for i = 1, 8 do | |
| 150 hi, lo = s_byte(packet, i * 2 - 1, i * 2); | |
| 151 aaaa[i] = s_format("%x", hi * 256 + lo); -- skips leading zeros | |
| 152 end | |
| 153 ip = t_concat(aaaa, ":", 1, 8); | |
| 154 len = (s_match(ip, "^0:[0:]+()") or 1) - 1; | |
| 155 for s in s_gmatch(ip, ":0:[0:]+") do | |
| 156 if len < #s then len, token = #s, s; end -- find longest sequence of zeros | |
| 157 end | |
| 158 return (s_gsub(ip, token or "^0:[0:]+", "::", 1)); | |
| 159 end | |
| 160 | |
| 161 if have_net then | |
| 162 parsers.A = net_util.ntop; | |
| 163 parsers.AAAA = net_util.ntop; | |
| 164 end | |
| 165 | |
| 166 local mx_mt = { | |
| 167 __tostring = function(rr) | |
| 168 return s_format("%d %s", rr.pref, rr.mx) | |
| 169 end | |
| 170 }; | |
| 171 function parsers.MX(packet) | |
| 172 local name = readDnsName(packet, 3); | |
| 173 local b1,b2 = s_byte(packet, 1, 2); | |
| 174 return setmetatable({ | |
| 175 pref = b1*256+b2; | |
| 176 mx = name; | |
| 177 }, mx_mt); | |
| 178 end | |
| 179 | |
| 180 local srv_mt = { | |
| 181 __tostring = function(rr) | |
| 182 return s_format("%d %d %d %s", rr.priority, rr.weight, rr.port, rr.target); | |
| 183 end | |
| 184 }; | |
| 185 function parsers.SRV(packet) | |
| 186 local name = readDnsName(packet, 7); | |
| 187 local b1, b2, b3, b4, b5, b6 = s_byte(packet, 1, 6); | |
| 188 return setmetatable({ | |
| 189 priority = b1*256+b2; | |
| 190 weight = b3*256+b4; | |
| 191 port = b5*256+b6; | |
| 192 target = name; | |
| 193 }, srv_mt); | |
| 194 end | |
| 195 | |
| 196 local txt_mt = { __tostring = t_concat }; | |
| 197 function parsers.TXT(packet) | |
| 198 local pack_len = #packet; | |
| 199 local r, pos, len = {}, 1; | |
| 200 repeat | |
| 201 len = s_byte(packet, pos) or 0; | |
| 202 t_insert(r, s_sub(packet, pos + 1, pos + len)); | |
| 203 pos = pos + len + 1; | |
| 204 until pos >= pack_len; | |
| 205 return setmetatable(r, txt_mt); | |
| 206 end | |
| 207 | |
| 208 parsers.SPF = parsers.TXT; | |
| 209 | |
| 210 -- Acronyms from RFC 7218 | |
| 211 local tlsa_usages = { | |
| 212 [0] = "PKIX-CA"; | |
| 213 [1] = "PKIX-EE"; | |
| 214 [2] = "DANE-TA"; | |
| 215 [3] = "DANE-EE"; | |
| 216 [255] = "PrivCert"; | |
| 217 }; | |
| 218 local tlsa_selectors = { | |
| 219 [0] = "Cert", | |
| 220 [1] = "SPKI", | |
| 221 [255] = "PrivSel", | |
| 222 }; | |
| 223 local tlsa_match_types = { | |
| 224 [0] = "Full", | |
| 225 [1] = "SHA2-256", | |
| 226 [2] = "SHA2-512", | |
| 227 [255] = "PrivMatch", | |
| 228 }; | |
| 229 local tlsa_mt = { | |
| 230 __tostring = function(rr) | |
| 231 return s_format("%s %s %s %s", | |
| 232 tlsa_usages[rr.use] or rr.use, | |
| 233 tlsa_selectors[rr.select] or rr.select, | |
| 234 tlsa_match_types[rr.match] or rr.match, | |
| 235 tohex(rr.data)); | |
| 236 end; | |
| 237 __index = { | |
| 238 getUsage = function(rr) return tlsa_usages[rr.use] end; | |
| 239 getSelector = function(rr) return tlsa_selectors[rr.select] end; | |
| 240 getMatchType = function(rr) return tlsa_match_types[rr.match] end; | |
| 241 } | |
| 242 }; | |
| 243 function parsers.TLSA(packet) | |
| 244 local use, select, match = s_byte(packet, 1,3); | |
| 245 return setmetatable({ | |
| 246 use = use; | |
| 247 select = select; | |
| 248 match = match; | |
| 249 data = s_sub(packet, 4); | |
| 250 }, tlsa_mt); | |
| 251 end | |
| 252 | |
| 253 local params = { | |
| 254 TLSA = { | |
| 255 use = tlsa_usages; | |
| 256 select = tlsa_selectors; | |
| 257 match = tlsa_match_types; | |
| 258 }; | |
| 259 }; | |
| 260 | |
| 261 local fallback_mt = { | |
| 262 __tostring = function(rr) | |
| 263 return s_format([[\# %d %s]], #rr.raw, tohex(rr.raw)); | |
| 264 end; | |
| 265 }; | |
| 266 local function fallback_parser(packet) | |
| 267 return setmetatable({ raw = packet },fallback_mt); | |
| 268 end | |
| 269 setmetatable(parsers, { __index = function() return fallback_parser end }); | |
| 270 | |
| 271 return { | |
| 272 parsers = parsers; | |
| 273 classes = classes; | |
| 274 types = types; | |
| 275 errors = errors; | |
| 276 params = params; | |
| 277 }; |
