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('#', ...);
33 parent
= parent
[select(i
, ...)];
34 if parent
== nil then break; end
38 set
= function(parent
, ...)
39 local len
= select('#', ...);
40 local key
, value
= select(len
-1, ...);
41 local cutpoint
, cutkey
;
44 local key
= select (i
, ...)
45 local child
= parent
[key
]
50 elseif next(child
, next(child
)) then
51 cutpoint
= nil; cutkey
= nil;
52 elseif cutpoint
== nil then
53 cutpoint
= parent
; cutkey
= key
;
55 elseif child
== nil then
62 if value
== nil and cutpoint
then
63 cutpoint
[cutkey
] = nil;
70 local get
, set
= ztact
.get
, ztact
.set
;
72 local default_timeout
= 15;
74 -------------------------------------------------- module 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;
91 local function augment (t
, prefix
) -- - - - - - - - - - - - - - - - - augment
93 for i
,s
in pairs(t
) do
96 a
[string.lower(s
)] = s
;
99 __index
= function (_
, i
)
100 if type(i
) == "number" then
101 return ("%s%d"):format(prefix
, i
);
102 elseif type(i
) == "string" then
111 local function encode (t
) -- - - - - - - - - - - - - - - - - - - - - encode
113 for i
,s
in pairs(t
) do
114 local word
= string.char(highbyte(i
), i
%0x100);
117 code
[string.lower(s
)] = word
;
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
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!
246 rrs
[rr
[rr
.type:lower()]]
= nil;
247 table.remove(rrs
, i
);
253 -- metatables & co. ------------------------------------------ metatables & co.
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>";
269 local special_tostrings
= {
270 LOC
= resolver
.LOC_tostring
;
272 return string.format('%2i %s', rr
.pref
, rr
.mx
);
276 return string.format('%5d %5d %5d %s', s
.priority
, s
.weight
, s
.port
, s
.target
);
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
)
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();
301 for class
,types
in pairs(cache
) do
302 for type,names
in pairs(types
) do
303 for name
,rrs
in pairs(names
) do
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
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
332 -- 2 server status request
334 o
.qr
= o
.qr
or 0; -- 1b 0 query, 1 response
336 o
.rcode
= o
.rcode
or 0; -- 4b 0 no error
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
366 local function encodeName(name
) -- - - - - - - - - - - - - - - - encodeName
368 for part
in string.gmatch(name
, '[^.]+') do
369 append(t
, string.char(string.len(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
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
412 local s
= string.sub(self
.packet
, self
.offset
, self
.offset
+ len
- 1);
413 self
.offset
= self
.offset
+ len
;
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);
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
448 function resolver
:name() -- - - - - - - - - - - - - - - - - - - - - - name
449 local remember
, pointers
= nil, 0;
450 local len
= self
:byte();
452 if len
== 0 then return "." end -- Root label
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
)..'.');
465 self
.offset
= remember
or self
.offset
;
466 return table.concat(n
);
470 function resolver
:question() -- - - - - - - - - - - - - - - - - - question
472 q
.name
= self
:name();
473 q
.type = dns
.type[self
:word()];
474 q
.class
= dns
.class
[self
:word()];
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
)
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");
498 for item
in addr
:gmatch(":[0:]+:[0:]+:") do
499 table.insert(zeros
, item
)
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();
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
) -- - - - - - - - - - - - -
550 if f
< 0 then pos
= neg
; f
= -f
; end
551 local deg, min, msec
;
556 return string.format('%3d %2d %2.3f %s', deg, min, msec
/1000, pos
);
560 function resolver
.LOC_tostring(rr
) -- - - - - - - - - - - - - LOC_tostring
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]));
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,
575 rr
.loc
.horiz_pre
/ 100,
576 rr
.loc
.vert_pre
/ 100
579 return table.concat(t
);
583 function resolver
:NS(rr
) -- - - - - - - - - - - - - - - - - - - - - - - NS
588 function resolver
:SOA(rr
) -- - - - - - - - - - - - - - - - - - - - - - SOA
592 function resolver
:SRV(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
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
);
629 function resolver
:rrs (count
) -- - - - - - - - - - - - - - - - - - - - - rrs
631 for _
= 1, count
do append(rrs
, self
:rr()); end
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);
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;
656 response
.answer
= self
:rrs(response
.header
.ancount
);
657 response
.authority
= self
:rrs(response
.header
.nscount
);
658 response
.additional
= self
:rrs(response
.header
.arcount
);
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
678 self
:addnameserver(address
);
682 function resolver
:adddefaultnameservers() -- - - - - adddefaultnameservers
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");
695 local resolv_conf
= io
.open("/etc/resolv.conf");
697 for line
in resolv_conf
:lines() do
698 line
= line
:gsub("#.*$", "")
699 :match('^%s*nameserver%s+([%x:%.]*%%?%S*)%s*$');
701 local ip
= new_ip(line
);
703 self
:addnameserver(ip
.addr
);
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
725 local peer
= self
.server
[servernum
];
726 if peer
:find(":") then
727 sock
, err
= socket
.udp6();
729 sock
, err
= (socket
.udp4
or socket
.udp
)();
731 if sock
and self
.socket_wrapper
then sock
, err
= self
.socket_wrapper(sock
, self
); end
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
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;
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;
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
);
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;
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
);
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);
814 if self
.unsorted
[rrs
] then table.sort (rrs
, comp_mx
); self
.unsorted
[rrs
] = nil; end
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
);
839 -- We are already waiting for a reply to an identical query.
840 set(self
.wanted
, qclass
, qtype
, qname
, co
, 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)
853 packet
= header
..question
,
854 server
= self
.best_server
,
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
)
869 -- remember which coroutine wants the answer
871 set(self
.wanted
, qclass
, qtype
, qname
, co
, true);
874 if timer
and self
.timeout
then
875 local num_servers
= #self
.server
;
877 timer
.add_task(self
.timeout
, function ()
878 if get(self
.wanted
, qclass
, qtype
, qname
, co
) then
879 if i
< num_servers
then
882 o
.server
= self
.best_server
;
883 conn
, err
= self
:getsocket(o
.server
);
889 -- Tried everything, failed
890 self
:cancel(qclass
, qtype
, qname
);
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
915 o
.retries
= (o
.retries
or 0) + 1;
916 if o
.retries
>= #self
.server
then
918 queries
[question
] = nil;
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;
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
;
950 for _
, sock
in pairs(rset
) do
952 if self
.socketset
[sock
] then
953 local packet
= sock
:receive();
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)
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
);
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);
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);
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];
1020 local cos = get(self
.wanted
, q
.class
, q
.type, q
.name
);
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);
1033 function resolver
:cancel(qclass
, qtype
, qname
)
1034 local cos = get(self
.wanted
, qclass
, qtype
, qname
);
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
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
1056 o
.delay
= o
.delay
+ 1;
1059 if o
.delay
> #self
.delays
then
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
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
1079 function resolver
:lookup(qname
, qtype
, qclass
) -- - - - - - - - - - lookup
1080 self
:query (qname
, qtype
, qclass
)
1081 while self
:pulse() do
1083 for i
, s
in ipairs(self
.socket
) do
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' },
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 };
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' });
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(...);