util.x509: Nameprep commonName once
[prosody.git] / net / dns.lua
blob3902f95cf7c177d1320d930049594ef5419728d0
1 -- Prosody IM
2 -- This file is included with Prosody IM. It has modifications,
3 -- which are hereby placed in the public domain.
6 -- todo: quick (default) header generation
7 -- todo: nxdomain, error handling
8 -- todo: cache results of encodeName
11 -- reference: http://tools.ietf.org/html/rfc1035
12 -- reference: http://tools.ietf.org/html/rfc1876 (LOC)
15 local socket = require "socket";
16 local timer = require "util.timer";
17 local new_ip = require "util.ip".new_ip;
18 local have_util_net, util_net = pcall(require, "util.net");
20 local _, windows = pcall(require, "util.windows");
21 local is_windows = (_ and windows) or os.getenv("WINDIR");
23 local coroutine, io, math, string, table =
24 coroutine, io, math, string, table;
26 local ipairs, next, pairs, print, setmetatable, tostring, assert, error, select, type =
27 ipairs, next, pairs, print, setmetatable, tostring, assert, error, select, type;
29 local ztact = { -- public domain 20080404 lua@ztact.com
30 get = function(parent, ...)
31 local len = select('#', ...);
32 for i=1,len do
33 parent = parent[select(i, ...)];
34 if parent == nil then break; end
35 end
36 return parent;
37 end;
38 set = function(parent, ...)
39 local len = select('#', ...);
40 local key, value = select(len-1, ...);
41 local cutpoint, cutkey;
43 for i=1,len-2 do
44 local key = select (i, ...)
45 local child = parent[key]
47 if value == nil then
48 if child == nil then
49 return;
50 elseif next(child, next(child)) then
51 cutpoint = nil; cutkey = nil;
52 elseif cutpoint == nil then
53 cutpoint = parent; cutkey = key;
54 end
55 elseif child == nil then
56 child = {};
57 parent[key] = child;
58 end
59 parent = child
60 end
62 if value == nil and cutpoint then
63 cutpoint[cutkey] = nil;
64 else
65 parent[key] = value;
66 return value;
67 end
68 end;
70 local get, set = ztact.get, ztact.set;
72 local default_timeout = 15;
74 -------------------------------------------------- module dns
75 local _ENV = nil;
76 -- luacheck: std none
77 local dns = {};
80 -- dns type & class codes ------------------------------ dns type & class codes
83 local append = table.insert
86 local function highbyte(i) -- - - - - - - - - - - - - - - - - - - highbyte
87 return (i-(i%0x100))/0x100;
88 end
91 local function augment (t, prefix) -- - - - - - - - - - - - - - - - - augment
92 local a = {};
93 for i,s in pairs(t) do
94 a[i] = s;
95 a[s] = s;
96 a[string.lower(s)] = s;
97 end
98 setmetatable(a, {
99 __index = function (_, i)
100 if type(i) == "number" then
101 return ("%s%d"):format(prefix, i);
102 elseif type(i) == "string" then
103 return i:upper();
105 end;
107 return a;
111 local function encode (t) -- - - - - - - - - - - - - - - - - - - - - encode
112 local code = {};
113 for i,s in pairs(t) do
114 local word = string.char(highbyte(i), i%0x100);
115 code[i] = word;
116 code[s] = word;
117 code[string.lower(s)] = word;
119 return code;
123 dns.types = {
124 [1] = "A", -- a host address,[RFC1035],,
125 [2] = "NS", -- an authoritative name server,[RFC1035],,
126 [3] = "MD", -- a mail destination (OBSOLETE - use MX),[RFC1035],,
127 [4] = "MF", -- a mail forwarder (OBSOLETE - use MX),[RFC1035],,
128 [5] = "CNAME", -- the canonical name for an alias,[RFC1035],,
129 [6] = "SOA", -- marks the start of a zone of authority,[RFC1035],,
130 [7] = "MB", -- a mailbox domain name (EXPERIMENTAL),[RFC1035],,
131 [8] = "MG", -- a mail group member (EXPERIMENTAL),[RFC1035],,
132 [9] = "MR", -- a mail rename domain name (EXPERIMENTAL),[RFC1035],,
133 [10] = "NULL", -- a null RR (EXPERIMENTAL),[RFC1035],,
134 [11] = "WKS", -- a well known service description,[RFC1035],,
135 [12] = "PTR", -- a domain name pointer,[RFC1035],,
136 [13] = "HINFO", -- host information,[RFC1035],,
137 [14] = "MINFO", -- mailbox or mail list information,[RFC1035],,
138 [15] = "MX", -- mail exchange,[RFC1035],,
139 [16] = "TXT", -- text strings,[RFC1035],,
140 [17] = "RP", -- for Responsible Person,[RFC1183],,
141 [18] = "AFSDB", -- for AFS Data Base location,[RFC1183][RFC5864],,
142 [19] = "X25", -- for X.25 PSDN address,[RFC1183],,
143 [20] = "ISDN", -- for ISDN address,[RFC1183],,
144 [21] = "RT", -- for Route Through,[RFC1183],,
145 [22] = "NSAP", -- "for NSAP address, NSAP style A record",[RFC1706],,
146 [23] = "NSAP-PTR", -- "for domain name pointer, NSAP style",[RFC1348][RFC1637][RFC1706],,
147 [24] = "SIG", -- for security signature,[RFC4034][RFC3755][RFC2535][RFC2536][RFC2537][RFC2931][RFC3110][RFC3008],,
148 [25] = "KEY", -- for security key,[RFC4034][RFC3755][RFC2535][RFC2536][RFC2537][RFC2539][RFC3008][RFC3110],,
149 [26] = "PX", -- X.400 mail mapping information,[RFC2163],,
150 [27] = "GPOS", -- Geographical Position,[RFC1712],,
151 [28] = "AAAA", -- IP6 Address,[RFC3596],,
152 [29] = "LOC", -- Location Information,[RFC1876],,
153 [30] = "NXT", -- Next Domain (OBSOLETE),[RFC3755][RFC2535],,
154 [31] = "EID", -- Endpoint Identifier,[Michael_Patton][http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt],,1995-06
155 [32] = "NIMLOC", -- Nimrod Locator,[1][Michael_Patton][http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt],,1995-06
156 [33] = "SRV", -- Server Selection,[1][RFC2782],,
157 [34] = "ATMA", -- ATM Address,"[ ATM Forum Technical Committee, ""ATM Name System, V2.0"", Doc ID: AF-DANS-0152.000, July 2000. Available from and held in escrow by IANA.]",,
158 [35] = "NAPTR", -- Naming Authority Pointer,[RFC2915][RFC2168][RFC3403],,
159 [36] = "KX", -- Key Exchanger,[RFC2230],,
160 [37] = "CERT", -- CERT,[RFC4398],,
161 [38] = "A6", -- A6 (OBSOLETE - use AAAA),[RFC3226][RFC2874][RFC6563],,
162 [39] = "DNAME", -- DNAME,[RFC6672],,
163 [40] = "SINK", -- SINK,[Donald_E_Eastlake][http://tools.ietf.org/html/draft-eastlake-kitchen-sink],,1997-11
164 [41] = "OPT", -- OPT,[RFC6891][RFC3225],,
165 [42] = "APL", -- APL,[RFC3123],,
166 [43] = "DS", -- Delegation Signer,[RFC4034][RFC3658],,
167 [44] = "SSHFP", -- SSH Key Fingerprint,[RFC4255],,
168 [45] = "IPSECKEY", -- IPSECKEY,[RFC4025],,
169 [46] = "RRSIG", -- RRSIG,[RFC4034][RFC3755],,
170 [47] = "NSEC", -- NSEC,[RFC4034][RFC3755],,
171 [48] = "DNSKEY", -- DNSKEY,[RFC4034][RFC3755],,
172 [49] = "DHCID", -- DHCID,[RFC4701],,
173 [50] = "NSEC3", -- NSEC3,[RFC5155],,
174 [51] = "NSEC3PARAM", -- NSEC3PARAM,[RFC5155],,
175 [52] = "TLSA", -- TLSA,[RFC6698],,
176 [53] = "SMIMEA", -- S/MIME cert association,[RFC8162],SMIMEA/smimea-completed-template,2015-12-01
177 -- [54] = "Unassigned", -- ,,,
178 [55] = "HIP", -- Host Identity Protocol,[RFC8005],,
179 [56] = "NINFO", -- NINFO,[Jim_Reid],NINFO/ninfo-completed-template,2008-01-21
180 [57] = "RKEY", -- RKEY,[Jim_Reid],RKEY/rkey-completed-template,2008-01-21
181 [58] = "TALINK", -- Trust Anchor LINK,[Wouter_Wijngaards],TALINK/talink-completed-template,2010-02-17
182 [59] = "CDS", -- Child DS,[RFC7344],CDS/cds-completed-template,2011-06-06
183 [60] = "CDNSKEY", -- DNSKEY(s) the Child wants reflected in DS,[RFC7344],,2014-06-16
184 [61] = "OPENPGPKEY", -- OpenPGP Key,[RFC7929],OPENPGPKEY/openpgpkey-completed-template,2014-08-12
185 [62] = "CSYNC", -- Child-To-Parent Synchronization,[RFC7477],,2015-01-27
186 -- [63 .. 98] = "Unassigned", -- ,,,
187 [99] = "SPF", -- ,[RFC7208],,
188 [100] = "UINFO", -- ,[IANA-Reserved],,
189 [101] = "UID", -- ,[IANA-Reserved],,
190 [102] = "GID", -- ,[IANA-Reserved],,
191 [103] = "UNSPEC", -- ,[IANA-Reserved],,
192 [104] = "NID", -- ,[RFC6742],ILNP/nid-completed-template,
193 [105] = "L32", -- ,[RFC6742],ILNP/l32-completed-template,
194 [106] = "L64", -- ,[RFC6742],ILNP/l64-completed-template,
195 [107] = "LP", -- ,[RFC6742],ILNP/lp-completed-template,
196 [108] = "EUI48", -- an EUI-48 address,[RFC7043],EUI48/eui48-completed-template,2013-03-27
197 [109] = "EUI64", -- an EUI-64 address,[RFC7043],EUI64/eui64-completed-template,2013-03-27
198 -- [110 .. 248] = "Unassigned", -- ,,,
199 [249] = "TKEY", -- Transaction Key,[RFC2930],,
200 [250] = "TSIG", -- Transaction Signature,[RFC2845],,
201 [251] = "IXFR", -- incremental transfer,[RFC1995],,
202 [252] = "AXFR", -- transfer of an entire zone,[RFC1035][RFC5936],,
203 [253] = "MAILB", -- "mailbox-related RRs (MB, MG or MR)",[RFC1035],,
204 [254] = "MAILA", -- mail agent RRs (OBSOLETE - see MX),[RFC1035],,
205 [255] = "*", -- A request for all records the server/cache has available,[RFC1035][RFC6895],,
206 [256] = "URI", -- URI,[RFC7553],URI/uri-completed-template,2011-02-22
207 [257] = "CAA", -- Certification Authority Restriction,[RFC6844],CAA/caa-completed-template,2011-04-07
208 [258] = "AVC", -- Application Visibility and Control,[Wolfgang_Riedel],AVC/avc-completed-template,2016-02-26
209 [259] = "DOA", -- Digital Object Architecture,[draft-durand-doa-over-dns],DOA/doa-completed-template,2017-08-30
210 -- [260 .. 32767] = "Unassigned", -- ,,,
211 [32768] = "TA", -- DNSSEC Trust Authorities,"[Sam_Weiler][http://cameo.library.cmu.edu/][ Deploying DNSSEC Without a Signed Root. Technical Report 1999-19, Information Networking Institute, Carnegie Mellon University, April 2004.]",,2005-12-13
212 [32769] = "DLV", -- DNSSEC Lookaside Validation,[RFC4431],,
213 -- [32770 .. 65279] = "Unassigned", -- ,,,
214 -- [65280 .. 65534] = "Private use", -- ,,,
215 -- [65535] = "Reserved", -- ,,,
218 dns.classes = { 'IN', 'CS', 'CH', 'HS', [255] = '*' };
221 dns.type = augment (dns.types, "TYPE");
222 dns.class = augment (dns.classes, "CLASS");
223 dns.typecode = encode (dns.types);
224 dns.classcode = encode (dns.classes);
228 local function standardize(qname, qtype, qclass) -- - - - - - - standardize
229 if string.byte(qname, -1) ~= 0x2E then qname = qname..'.'; end
230 qname = string.lower(qname);
231 return qname, dns.type[qtype or 'A'], dns.class[qclass or 'IN'];
235 local function prune(rrs, time, soft) -- - - - - - - - - - - - - - - prune
236 time = time or socket.gettime();
237 for i,rr in ipairs(rrs) do
238 if rr.tod then
239 if rr.tod < time then
240 rrs[rr[rr.type:lower()]] = nil;
241 table.remove(rrs, i);
242 return prune(rrs, time, soft); -- Re-iterate
244 elseif soft == 'soft' then -- What is this? I forget!
245 assert(rr.ttl == 0);
246 rrs[rr[rr.type:lower()]] = nil;
247 table.remove(rrs, i);
253 -- metatables & co. ------------------------------------------ metatables & co.
256 local resolver = {};
257 resolver.__index = resolver;
259 resolver.timeout = default_timeout;
261 local function default_rr_tostring(rr)
262 local rr_val = rr.type and rr[rr.type:lower()];
263 if type(rr_val) ~= "string" then
264 return "<UNKNOWN RDATA TYPE>";
266 return rr_val;
269 local special_tostrings = {
270 LOC = resolver.LOC_tostring;
271 MX = function (rr)
272 return string.format('%2i %s', rr.pref, rr.mx);
273 end;
274 SRV = function (rr)
275 local s = rr.srv;
276 return string.format('%5d %5d %5d %s', s.priority, s.weight, s.port, s.target);
277 end;
280 local rr_metatable = {}; -- - - - - - - - - - - - - - - - - - - rr_metatable
281 function rr_metatable.__tostring(rr)
282 local rr_string = (special_tostrings[rr.type] or default_rr_tostring)(rr);
283 return string.format('%2s %-5s %6i %-28s %s', rr.class, rr.type, rr.ttl, rr.name, rr_string);
287 local rrs_metatable = {}; -- - - - - - - - - - - - - - - - - - rrs_metatable
288 function rrs_metatable.__tostring(rrs)
289 local t = {};
290 for _, rr in ipairs(rrs) do
291 append(t, tostring(rr)..'\n');
293 return table.concat(t);
297 local cache_metatable = {}; -- - - - - - - - - - - - - - - - cache_metatable
298 function cache_metatable.__tostring(cache)
299 local time = socket.gettime();
300 local t = {};
301 for class,types in pairs(cache) do
302 for type,names in pairs(types) do
303 for name,rrs in pairs(names) do
304 prune(rrs, time);
305 append(t, tostring(rrs));
309 return table.concat(t);
313 -- packet layer -------------------------------------------------- packet layer
316 function dns.random(...) -- - - - - - - - - - - - - - - - - - - dns.random
317 math.randomseed(math.floor(10000*socket.gettime()) % 0x80000000);
318 dns.random = math.random;
319 return dns.random(...);
323 local function encodeHeader(o) -- - - - - - - - - - - - - - - encodeHeader
324 o = o or {};
325 o.id = o.id or dns.random(0, 0xffff); -- 16b (random) id
327 o.rd = o.rd or 1; -- 1b 1 recursion desired
328 o.tc = o.tc or 0; -- 1b 1 truncated response
329 o.aa = o.aa or 0; -- 1b 1 authoritative response
330 o.opcode = o.opcode or 0; -- 4b 0 query
331 -- 1 inverse query
332 -- 2 server status request
333 -- 3-15 reserved
334 o.qr = o.qr or 0; -- 1b 0 query, 1 response
336 o.rcode = o.rcode or 0; -- 4b 0 no error
337 -- 1 format error
338 -- 2 server failure
339 -- 3 name error
340 -- 4 not implemented
341 -- 5 refused
342 -- 6-15 reserved
343 o.z = o.z or 0; -- 3b 0 resvered
344 o.ra = o.ra or 0; -- 1b 1 recursion available
346 o.qdcount = o.qdcount or 1; -- 16b number of question RRs
347 o.ancount = o.ancount or 0; -- 16b number of answers RRs
348 o.nscount = o.nscount or 0; -- 16b number of nameservers RRs
349 o.arcount = o.arcount or 0; -- 16b number of additional RRs
351 -- string.char() rounds, so prevent roundup with -0.4999
352 local header = string.char(
353 highbyte(o.id), o.id %0x100,
354 o.rd + 2*o.tc + 4*o.aa + 8*o.opcode + 128*o.qr,
355 o.rcode + 16*o.z + 128*o.ra,
356 highbyte(o.qdcount), o.qdcount %0x100,
357 highbyte(o.ancount), o.ancount %0x100,
358 highbyte(o.nscount), o.nscount %0x100,
359 highbyte(o.arcount), o.arcount %0x100
362 return header, o.id;
366 local function encodeName(name) -- - - - - - - - - - - - - - - - encodeName
367 local t = {};
368 for part in string.gmatch(name, '[^.]+') do
369 append(t, string.char(string.len(part)));
370 append(t, part);
372 append(t, string.char(0));
373 return table.concat(t);
377 local function encodeQuestion(qname, qtype, qclass) -- - - - encodeQuestion
378 qname = encodeName(qname);
379 qtype = dns.typecode[qtype or 'a'];
380 qclass = dns.classcode[qclass or 'in'];
381 return qname..qtype..qclass;
385 function resolver:byte(len) -- - - - - - - - - - - - - - - - - - - - - byte
386 len = len or 1;
387 local offset = self.offset;
388 local last = offset + len - 1;
389 if last > #self.packet then
390 error(string.format('out of bounds: %i>%i', last, #self.packet));
392 self.offset = offset + len;
393 return string.byte(self.packet, offset, last);
397 function resolver:word() -- - - - - - - - - - - - - - - - - - - - - - word
398 local b1, b2 = self:byte(2);
399 return 0x100*b1 + b2;
403 function resolver:dword () -- - - - - - - - - - - - - - - - - - - - - dword
404 local b1, b2, b3, b4 = self:byte(4);
405 --print('dword', b1, b2, b3, b4);
406 return 0x1000000*b1 + 0x10000*b2 + 0x100*b3 + b4;
410 function resolver:sub(len) -- - - - - - - - - - - - - - - - - - - - - - sub
411 len = len or 1;
412 local s = string.sub(self.packet, self.offset, self.offset + len - 1);
413 self.offset = self.offset + len;
414 return s;
418 function resolver:header(force) -- - - - - - - - - - - - - - - - - - header
419 local id = self:word();
420 --print(string.format(':header id %x', id));
421 if not self.active[id] and not force then return nil; end
423 local h = { id = id };
425 local b1, b2 = self:byte(2);
427 h.rd = b1 %2;
428 h.tc = b1 /2%2;
429 h.aa = b1 /4%2;
430 h.opcode = b1 /8%16;
431 h.qr = b1 /128;
433 h.rcode = b2 %16;
434 h.z = b2 /16%8;
435 h.ra = b2 /128;
437 h.qdcount = self:word();
438 h.ancount = self:word();
439 h.nscount = self:word();
440 h.arcount = self:word();
442 for k,v in pairs(h) do h[k] = v-v%1; end
444 return h;
448 function resolver:name() -- - - - - - - - - - - - - - - - - - - - - - name
449 local remember, pointers = nil, 0;
450 local len = self:byte();
451 local n = {};
452 if len == 0 then return "." end -- Root label
453 while len > 0 do
454 if len >= 0xc0 then -- name is "compressed"
455 pointers = pointers + 1;
456 if pointers >= 20 then error('dns error: 20 pointers'); end;
457 local offset = ((len-0xc0)*0x100) + self:byte();
458 remember = remember or self.offset;
459 self.offset = offset + 1; -- +1 for lua
460 else -- name is not compressed
461 append(n, self:sub(len)..'.');
463 len = self:byte();
465 self.offset = remember or self.offset;
466 return table.concat(n);
470 function resolver:question() -- - - - - - - - - - - - - - - - - - question
471 local q = {};
472 q.name = self:name();
473 q.type = dns.type[self:word()];
474 q.class = dns.class[self:word()];
475 return q;
479 function resolver:A(rr) -- - - - - - - - - - - - - - - - - - - - - - - - A
480 local b1, b2, b3, b4 = self:byte(4);
481 rr.a = string.format('%i.%i.%i.%i', b1, b2, b3, b4);
484 if have_util_net and util_net.ntop then
485 function resolver:A(rr)
486 rr.a = util_net.ntop(self:sub(4));
490 function resolver:AAAA(rr)
491 local addr = {};
492 for _ = 1, rr.rdlength, 2 do
493 local b1, b2 = self:byte(2);
494 table.insert(addr, ("%02x%02x"):format(b1, b2));
496 addr = table.concat(addr, ":"):gsub("%f[%x]0+(%x)","%1");
497 local zeros = {};
498 for item in addr:gmatch(":[0:]+:[0:]+:") do
499 table.insert(zeros, item)
501 if #zeros == 0 then
502 rr.aaaa = addr;
503 return
504 elseif #zeros > 1 then
505 table.sort(zeros, function(a, b) return #a > #b end);
507 rr.aaaa = addr:gsub(zeros[1], "::", 1):gsub("^0::", "::"):gsub("::0$", "::");
510 if have_util_net and util_net.ntop then
511 function resolver:AAAA(rr)
512 rr.aaaa = util_net.ntop(self:sub(16));
516 function resolver:CNAME(rr) -- - - - - - - - - - - - - - - - - - - - CNAME
517 rr.cname = self:name();
521 function resolver:MX(rr) -- - - - - - - - - - - - - - - - - - - - - - - MX
522 rr.pref = self:word();
523 rr.mx = self:name();
527 function resolver:LOC_nibble_power() -- - - - - - - - - - LOC_nibble_power
528 local b = self:byte();
529 --print('nibbles', ((b-(b%0x10))/0x10), (b%0x10));
530 return ((b-(b%0x10))/0x10) * (10^(b%0x10));
534 function resolver:LOC(rr) -- - - - - - - - - - - - - - - - - - - - - - LOC
535 rr.version = self:byte();
536 if rr.version == 0 then
537 rr.loc = rr.loc or {};
538 rr.loc.size = self:LOC_nibble_power();
539 rr.loc.horiz_pre = self:LOC_nibble_power();
540 rr.loc.vert_pre = self:LOC_nibble_power();
541 rr.loc.latitude = self:dword();
542 rr.loc.longitude = self:dword();
543 rr.loc.altitude = self:dword();
548 local function LOC_tostring_degrees(f, pos, neg) -- - - - - - - - - - - - -
549 f = f - 0x80000000;
550 if f < 0 then pos = neg; f = -f; end
551 local deg, min, msec;
552 msec = f%60000;
553 f = (f-msec)/60000;
554 min = f%60;
555 deg = (f-min)/60;
556 return string.format('%3d %2d %2.3f %s', deg, min, msec/1000, pos);
560 function resolver.LOC_tostring(rr) -- - - - - - - - - - - - - LOC_tostring
561 local t = {};
563 --[[
564 for k,name in pairs { 'size', 'horiz_pre', 'vert_pre', 'latitude', 'longitude', 'altitude' } do
565 append(t, string.format('%4s%-10s: %12.0f\n', '', name, rr.loc[name]));
567 --]]
569 append(t, string.format(
570 '%s %s %.2fm %.2fm %.2fm %.2fm',
571 LOC_tostring_degrees (rr.loc.latitude, 'N', 'S'),
572 LOC_tostring_degrees (rr.loc.longitude, 'E', 'W'),
573 (rr.loc.altitude - 10000000) / 100,
574 rr.loc.size / 100,
575 rr.loc.horiz_pre / 100,
576 rr.loc.vert_pre / 100
579 return table.concat(t);
583 function resolver:NS(rr) -- - - - - - - - - - - - - - - - - - - - - - - NS
584 rr.ns = self:name();
588 function resolver:SOA(rr) -- - - - - - - - - - - - - - - - - - - - - - SOA
592 function resolver:SRV(rr) -- - - - - - - - - - - - - - - - - - - - - - SRV
593 rr.srv = {};
594 rr.srv.priority = self:word();
595 rr.srv.weight = self:word();
596 rr.srv.port = self:word();
597 rr.srv.target = self:name();
600 function resolver:PTR(rr)
601 rr.ptr = self:name();
604 function resolver:TXT(rr) -- - - - - - - - - - - - - - - - - - - - - - TXT
605 rr.txt = self:sub (self:byte());
609 function resolver:rr() -- - - - - - - - - - - - - - - - - - - - - - - - rr
610 local rr = {};
611 setmetatable(rr, rr_metatable);
612 rr.name = self:name(self);
613 rr.type = dns.type[self:word()] or rr.type;
614 rr.class = dns.class[self:word()] or rr.class;
615 rr.ttl = 0x10000*self:word() + self:word();
616 rr.rdlength = self:word();
618 rr.tod = self.time + math.max(rr.ttl, 1);
620 local remember = self.offset;
621 local rr_parser = self[dns.type[rr.type]];
622 if rr_parser then rr_parser(self, rr); end
623 self.offset = remember;
624 rr.rdata = self:sub(rr.rdlength);
625 return rr;
629 function resolver:rrs (count) -- - - - - - - - - - - - - - - - - - - - - rrs
630 local rrs = {};
631 for _ = 1, count do append(rrs, self:rr()); end
632 return rrs;
636 function resolver:decode(packet, force) -- - - - - - - - - - - - - - decode
637 self.packet, self.offset = packet, 1;
638 local header = self:header(force);
639 if not header then return nil; end
640 local response = { header = header };
642 response.question = {};
643 local offset = self.offset;
644 for _ = 1, response.header.qdcount do
645 append(response.question, self:question());
647 response.question.raw = string.sub(self.packet, offset, self.offset - 1);
649 if not force then
650 if not self.active[response.header.id] or not self.active[response.header.id][response.question.raw] then
651 self.active[response.header.id] = nil;
652 return nil;
656 response.answer = self:rrs(response.header.ancount);
657 response.authority = self:rrs(response.header.nscount);
658 response.additional = self:rrs(response.header.arcount);
660 return response;
664 -- socket layer -------------------------------------------------- socket layer
667 resolver.delays = { 1, 3 };
670 function resolver:addnameserver(address) -- - - - - - - - - - addnameserver
671 self.server = self.server or {};
672 append(self.server, address);
676 function resolver:setnameserver(address) -- - - - - - - - - - setnameserver
677 self.server = {};
678 self:addnameserver(address);
682 function resolver:adddefaultnameservers() -- - - - - adddefaultnameservers
683 if is_windows then
684 if windows and windows.get_nameservers then
685 for _, server in ipairs(windows.get_nameservers()) do
686 self:addnameserver(server);
689 if not self.server or #self.server == 0 then
690 -- TODO log warning about no nameservers, adding opendns servers as fallback
691 self:addnameserver("208.67.222.222");
692 self:addnameserver("208.67.220.220");
694 else -- posix
695 local resolv_conf = io.open("/etc/resolv.conf");
696 if resolv_conf then
697 for line in resolv_conf:lines() do
698 line = line:gsub("#.*$", "")
699 :match('^%s*nameserver%s+([%x:%.]*%%?%S*)%s*$');
700 if line then
701 local ip = new_ip(line);
702 if ip then
703 self:addnameserver(ip.addr);
707 resolv_conf:close();
709 if not self.server or #self.server == 0 then
710 -- TODO log warning about no nameservers, adding localhost as the default nameserver
711 self:addnameserver("127.0.0.1");
717 function resolver:getsocket(servernum) -- - - - - - - - - - - - - getsocket
718 self.socket = self.socket or {};
719 self.socketset = self.socketset or {};
721 local sock = self.socket[servernum];
722 if sock then return sock; end
724 local ok, err;
725 local peer = self.server[servernum];
726 if peer:find(":") then
727 sock, err = socket.udp6();
728 else
729 sock, err = (socket.udp4 or socket.udp)();
731 if sock and self.socket_wrapper then sock, err = self.socket_wrapper(sock, self); end
732 if not sock then
733 return nil, err;
735 sock:settimeout(0);
736 -- todo: attempt to use a random port, fallback to 0
737 self.socket[servernum] = sock;
738 self.socketset[sock] = servernum;
739 -- set{sock,peer}name can fail, eg because of local routing table
740 -- if so, try the next server
741 ok, err = sock:setsockname('*', 0);
742 if not ok then return self:servfail(sock, err); end
743 ok, err = sock:setpeername(peer, 53);
744 if not ok then return self:servfail(sock, err); end
745 return sock;
748 function resolver:voidsocket(sock)
749 if self.socket[sock] then
750 self.socketset[self.socket[sock]] = nil;
751 self.socket[sock] = nil;
752 elseif self.socketset[sock] then
753 self.socket[self.socketset[sock]] = nil;
754 self.socketset[sock] = nil;
756 sock:close();
759 function resolver:socket_wrapper_set(func) -- - - - - - - socket_wrapper_set
760 self.socket_wrapper = func;
764 function resolver:closeall () -- - - - - - - - - - - - - - - - - - closeall
765 for i,sock in ipairs(self.socket) do
766 self.socket[i] = nil;
767 self.socketset[sock] = nil;
768 sock:close();
773 function resolver:remember(rr, type) -- - - - - - - - - - - - - - remember
774 --print ('remember', type, rr.class, rr.type, rr.name)
775 local qname, qtype, qclass = standardize(rr.name, rr.type, rr.class);
777 if type ~= '*' then
778 type = qtype;
779 local all = get(self.cache, qclass, '*', qname);
780 --print('remember all', all);
781 if all then append(all, rr); end
784 self.cache = self.cache or setmetatable({}, cache_metatable);
785 local rrs = get(self.cache, qclass, type, qname) or
786 set(self.cache, qclass, type, qname, setmetatable({}, rrs_metatable));
787 if rr[qtype:lower()] and not rrs[rr[qtype:lower()]] then
788 rrs[rr[qtype:lower()]] = true;
789 append(rrs, rr);
792 if type == 'MX' then self.unsorted[rrs] = true; end
796 local function comp_mx(a, b) -- - - - - - - - - - - - - - - - - - - comp_mx
797 return (a.pref == b.pref) and (a.mx < b.mx) or (a.pref < b.pref);
801 function resolver:peek (qname, qtype, qclass, n) -- - - - - - - - - - - - peek
802 qname, qtype, qclass = standardize(qname, qtype, qclass);
803 local rrs = get(self.cache, qclass, qtype, qname);
804 if not rrs then
805 if n then if n <= 0 then return end else n = 3 end
806 rrs = get(self.cache, qclass, "CNAME", qname);
807 if not (rrs and rrs[1]) then return end
808 return self:peek(rrs[1].cname, qtype, qclass, n - 1);
810 if prune(rrs, socket.gettime()) and qtype == '*' or not next(rrs) then
811 set(self.cache, qclass, qtype, qname, nil);
812 return nil;
814 if self.unsorted[rrs] then table.sort (rrs, comp_mx); self.unsorted[rrs] = nil; end
815 return rrs;
819 function resolver:purge(soft) -- - - - - - - - - - - - - - - - - - - purge
820 if soft == 'soft' then
821 self.time = socket.gettime();
822 for class,types in pairs(self.cache or {}) do
823 for type,names in pairs(types) do
824 for name,rrs in pairs(names) do
825 prune(rrs, self.time, 'soft')
829 else self.cache = setmetatable({}, cache_metatable); end
833 function resolver:query(qname, qtype, qclass) -- - - - - - - - - - -- query
834 qname, qtype, qclass = standardize(qname, qtype, qclass)
836 local co = coroutine.running();
837 local q = get(self.wanted, qclass, qtype, qname);
838 if co and q then
839 -- We are already waiting for a reply to an identical query.
840 set(self.wanted, qclass, qtype, qname, co, true);
841 return true;
844 if not self.server then self:adddefaultnameservers(); end
846 local question = encodeQuestion(qname, qtype, qclass);
847 local peek = self:peek (qname, qtype, qclass);
848 if peek then return peek; end
850 local header, id = encodeHeader();
851 --print ('query id', id, qclass, qtype, qname)
852 local o = {
853 packet = header..question,
854 server = self.best_server,
855 delay = 1,
856 retry = socket.gettime() + self.delays[1]
859 -- remember the query
860 self.active[id] = self.active[id] or {};
861 self.active[id][question] = o;
863 local conn, err = self:getsocket(o.server)
864 if not conn then
865 return nil, err;
867 conn:send (o.packet)
869 -- remember which coroutine wants the answer
870 if co then
871 set(self.wanted, qclass, qtype, qname, co, true);
874 if timer and self.timeout then
875 local num_servers = #self.server;
876 local i = 1;
877 timer.add_task(self.timeout, function ()
878 if get(self.wanted, qclass, qtype, qname, co) then
879 if i < num_servers then
880 i = i + 1;
881 self:servfail(conn);
882 o.server = self.best_server;
883 conn, err = self:getsocket(o.server);
884 if conn then
885 conn:send(o.packet);
886 return self.timeout;
889 -- Tried everything, failed
890 self:cancel(qclass, qtype, qname);
892 end)
894 return true;
897 function resolver:servfail(sock, err)
898 -- Resend all queries for this server
900 local num = self.socketset[sock]
902 -- Socket is dead now
903 sock = self:voidsocket(sock);
905 -- Find all requests to the down server, and retry on the next server
906 self.time = socket.gettime();
907 for id,queries in pairs(self.active) do
908 for question,o in pairs(queries) do
909 if o.server == num then -- This request was to the broken server
910 o.server = o.server + 1 -- Use next server
911 if o.server > #self.server then
912 o.server = 1;
915 o.retries = (o.retries or 0) + 1;
916 if o.retries >= #self.server then
917 --print('timeout');
918 queries[question] = nil;
919 else
920 sock, err = self:getsocket(o.server);
921 if sock then sock:send(o.packet); end
925 if next(queries) == nil then
926 self.active[id] = nil;
930 if num == self.best_server then
931 self.best_server = self.best_server + 1;
932 if self.best_server > #self.server then
933 -- Exhausted all servers, try first again
934 self.best_server = 1;
937 return sock, err;
940 function resolver:settimeout(seconds)
941 self.timeout = seconds;
944 function resolver:receive(rset) -- - - - - - - - - - - - - - - - - receive
945 --print('receive'); print(self.socket);
946 self.time = socket.gettime();
947 rset = rset or self.socket;
949 local response;
950 for _, sock in pairs(rset) do
952 if self.socketset[sock] then
953 local packet = sock:receive();
954 if packet then
955 response = self:decode(packet);
956 if response and self.active[response.header.id]
957 and self.active[response.header.id][response.question.raw] then
958 --print('received response');
959 --self.print(response);
961 for _, rr in pairs(response.answer) do
962 if rr.name:sub(-#response.question[1].name, -1) == response.question[1].name then
963 self:remember(rr, response.question[1].type)
967 -- retire the query
968 local queries = self.active[response.header.id];
969 queries[response.question.raw] = nil;
971 if not next(queries) then self.active[response.header.id] = nil; end
972 if not next(self.active) then self:closeall(); end
974 -- was the query on the wanted list?
975 local q = response.question[1];
976 local cos = get(self.wanted, q.class, q.type, q.name);
977 if cos then
978 for co in pairs(cos) do
979 if coroutine.status(co) == "suspended" then coroutine.resume(co); end
981 set(self.wanted, q.class, q.type, q.name, nil);
989 return response;
993 function resolver:feed(sock, packet, force)
994 --print('receive'); print(self.socket);
995 self.time = socket.gettime();
997 local response = self:decode(packet, force);
998 if response and self.active[response.header.id]
999 and self.active[response.header.id][response.question.raw] then
1000 --print('received response');
1001 --self.print(response);
1003 for _, rr in pairs(response.answer) do
1004 self:remember(rr, rr.type);
1007 for _, rr in pairs(response.additional) do
1008 self:remember(rr, rr.type);
1011 -- retire the query
1012 local queries = self.active[response.header.id];
1013 queries[response.question.raw] = nil;
1014 if not next(queries) then self.active[response.header.id] = nil; end
1015 if not next(self.active) then self:closeall(); end
1017 -- was the query on the wanted list?
1018 local q = response.question[1];
1019 if q then
1020 local cos = get(self.wanted, q.class, q.type, q.name);
1021 if cos then
1022 for co in pairs(cos) do
1023 if coroutine.status(co) == "suspended" then coroutine.resume(co); end
1025 set(self.wanted, q.class, q.type, q.name, nil);
1030 return response;
1033 function resolver:cancel(qclass, qtype, qname)
1034 local cos = get(self.wanted, qclass, qtype, qname);
1035 if cos then
1036 for co in pairs(cos) do
1037 if coroutine.status(co) == "suspended" then coroutine.resume(co); end
1039 set(self.wanted, qclass, qtype, qname, nil);
1043 function resolver:pulse() -- - - - - - - - - - - - - - - - - - - - - pulse
1044 --print(':pulse');
1045 while self:receive() do end
1046 if not next(self.active) then return nil; end
1048 self.time = socket.gettime();
1049 for id,queries in pairs(self.active) do
1050 for question,o in pairs(queries) do
1051 if self.time >= o.retry then
1053 o.server = o.server + 1;
1054 if o.server > #self.server then
1055 o.server = 1;
1056 o.delay = o.delay + 1;
1059 if o.delay > #self.delays then
1060 --print('timeout');
1061 queries[question] = nil;
1062 if not next(queries) then self.active[id] = nil; end
1063 if not next(self.active) then return nil; end
1064 else
1065 --print('retry', o.server, o.delay);
1066 local _a = self.socket[o.server];
1067 if _a then _a:send(o.packet); end
1068 o.retry = self.time + self.delays[o.delay];
1074 if next(self.active) then return true; end
1075 return nil;
1079 function resolver:lookup(qname, qtype, qclass) -- - - - - - - - - - lookup
1080 self:query (qname, qtype, qclass)
1081 while self:pulse() do
1082 local recvt = {}
1083 for i, s in ipairs(self.socket) do
1084 recvt[i] = s
1086 socket.select(recvt, nil, 4)
1088 --print(self.cache);
1089 return self:peek(qname, qtype, qclass);
1092 function resolver:lookupex(handler, qname, qtype, qclass) -- - - - - - - - - - lookup
1093 return self:peek(qname, qtype, qclass) or self:query(qname, qtype, qclass);
1096 function resolver:tohostname(ip)
1097 return dns.lookup(ip:gsub("(%d+)%.(%d+)%.(%d+)%.(%d+)", "%4.%3.%2.%1.in-addr.arpa."), "PTR");
1100 --print ---------------------------------------------------------------- print
1103 local hints = { -- - - - - - - - - - - - - - - - - - - - - - - - - - - hints
1104 qr = { [0]='query', 'response' },
1105 opcode = { [0]='query', 'inverse query', 'server status request' },
1106 aa = { [0]='non-authoritative', 'authoritative' },
1107 tc = { [0]='complete', 'truncated' },
1108 rd = { [0]='recursion not desired', 'recursion desired' },
1109 ra = { [0]='recursion not available', 'recursion available' },
1110 z = { [0]='(reserved)' },
1111 rcode = { [0]='no error', 'format error', 'server failure', 'name error', 'not implemented' },
1113 type = dns.type,
1114 class = dns.class
1118 local function hint(p, s) -- - - - - - - - - - - - - - - - - - - - - - hint
1119 return (hints[s] and hints[s][p[s]]) or '';
1123 function resolver.print(response) -- - - - - - - - - - - - - resolver.print
1124 for _, s in pairs { 'id', 'qr', 'opcode', 'aa', 'tc', 'rd', 'ra', 'z',
1125 'rcode', 'qdcount', 'ancount', 'nscount', 'arcount' } do
1126 print( string.format('%-30s', 'header.'..s), response.header[s], hint(response.header, s) );
1129 for i,question in ipairs(response.question) do
1130 print(string.format ('question[%i].name ', i), question.name);
1131 print(string.format ('question[%i].type ', i), question.type);
1132 print(string.format ('question[%i].class ', i), question.class);
1135 local common = { name=1, type=1, class=1, ttl=1, rdlength=1, rdata=1 };
1136 local tmp;
1137 for _, s in pairs({'answer', 'authority', 'additional'}) do
1138 for i,rr in pairs(response[s]) do
1139 for _, t in pairs({ 'name', 'type', 'class', 'ttl', 'rdlength' }) do
1140 tmp = string.format('%s[%i].%s', s, i, t);
1141 print(string.format('%-30s', tmp), rr[t], hint(rr, t));
1143 for j,t in pairs(rr) do
1144 if not common[j] then
1145 tmp = string.format('%s[%i].%s', s, i, j);
1146 print(string.format('%-30s %s', tostring(tmp), tostring(t)));
1154 -- module api ------------------------------------------------------ module api
1157 function dns.resolver () -- - - - - - - - - - - - - - - - - - - - - resolver
1158 local r = { active = {}, cache = {}, unsorted = {}, wanted = {}, best_server = 1 };
1159 setmetatable (r, resolver);
1160 setmetatable (r.cache, cache_metatable);
1161 setmetatable (r.unsorted, { __mode = 'kv' });
1162 return r;
1165 local _resolver = dns.resolver();
1166 dns._resolver = _resolver;
1168 function dns.lookup(...) -- - - - - - - - - - - - - - - - - - - - - lookup
1169 return _resolver:lookup(...);
1172 function dns.tohostname(...)
1173 return _resolver:tohostname(...);
1176 function dns.purge(...) -- - - - - - - - - - - - - - - - - - - - - - purge
1177 return _resolver:purge(...);
1180 function dns.peek(...) -- - - - - - - - - - - - - - - - - - - - - - - peek
1181 return _resolver:peek(...);
1184 function dns.query(...) -- - - - - - - - - - - - - - - - - - - - - - query
1185 return _resolver:query(...);
1188 function dns.feed(...) -- - - - - - - - - - - - - - - - - - - - - - - feed
1189 return _resolver:feed(...);
1192 function dns.cancel(...) -- - - - - - - - - - - - - - - - - - - - - - cancel
1193 return _resolver:cancel(...);
1196 function dns.settimeout(...)
1197 return _resolver:settimeout(...);
1200 function dns.cache()
1201 return _resolver.cache;
1204 function dns.socket_wrapper_set(...) -- - - - - - - - - socket_wrapper_set
1205 return _resolver:socket_wrapper_set(...);
1208 return dns;