net/dns.lua

changeset 403
da92afa267cf
parent 399
93b6587d9afb
child 453
a1efb2cb4f9c
equal deleted inserted replaced
402:50f1c09541cd 403:da92afa267cf
1
2
3 -- public domain 20080404 lua@ztact.com
4
5
6 -- todo: quick (default) header generation
7 -- todo: nxdomain, error handling
8 -- todo: cache results of encodeName
9
10
11 -- reference: http://tools.ietf.org/html/rfc1035
12 -- reference: http://tools.ietf.org/html/rfc1876 (LOC)
13
14
15 require 'socket'
16 local ztact = require 'util.ztact'
17
18
19 local coroutine, io, math, socket, string, table =
20 coroutine, io, math, socket, string, table
21
22 local ipairs, next, pairs, print, setmetatable, tostring, assert, error =
23 ipairs, next, pairs, print, setmetatable, tostring, assert, error
24
25 local get, set = ztact.get, ztact.set
26
27
28 -------------------------------------------------- module dns
29 module ('dns')
30 local dns = _M;
31
32
33 -- dns type & class codes ------------------------------ dns type & class codes
34
35
36 local append = table.insert
37
38
39 local function highbyte (i) -- - - - - - - - - - - - - - - - - - - highbyte
40 return (i-(i%0x100))/0x100
41 end
42
43
44 local function augment (t) -- - - - - - - - - - - - - - - - - - - - augment
45 local a = {}
46 for i,s in pairs (t) do a[i] = s a[s] = s a[string.lower (s)] = s end
47 return a
48 end
49
50
51 local function encode (t) -- - - - - - - - - - - - - - - - - - - - - encode
52 local code = {}
53 for i,s in pairs (t) do
54 local word = string.char (highbyte (i), i %0x100)
55 code[i] = word
56 code[s] = word
57 code[string.lower (s)] = word
58 end
59 return code
60 end
61
62
63 dns.types = {
64 'A', 'NS', 'MD', 'MF', 'CNAME', 'SOA', 'MB', 'MG', 'MR', 'NULL', 'WKS',
65 'PTR', 'HINFO', 'MINFO', 'MX', 'TXT',
66 [ 28] = 'AAAA', [ 29] = 'LOC', [ 33] = 'SRV',
67 [252] = 'AXFR', [253] = 'MAILB', [254] = 'MAILA', [255] = '*' }
68
69
70 dns.classes = { 'IN', 'CS', 'CH', 'HS', [255] = '*' }
71
72
73 dns.type = augment (dns.types)
74 dns.class = augment (dns.classes)
75 dns.typecode = encode (dns.types)
76 dns.classcode = encode (dns.classes)
77
78
79
80 local function standardize (qname, qtype, qclass) -- - - - - - - standardize
81 if string.byte (qname, -1) ~= 0x2E then qname = qname..'.' end
82 qname = string.lower (qname)
83 return qname, dns.type[qtype or 'A'], dns.class[qclass or 'IN']
84 end
85
86
87 local function prune (rrs, time, soft) -- - - - - - - - - - - - - - - prune
88
89 time = time or socket.gettime ()
90 for i,rr in pairs (rrs) do
91
92 if rr.tod then
93 -- rr.tod = rr.tod - 50 -- accelerated decripitude
94 rr.ttl = math.floor (rr.tod - time)
95 if rr.ttl <= 0 then rrs[i] = nil end
96
97 elseif soft == 'soft' then -- What is this? I forget!
98 assert (rr.ttl == 0)
99 rrs[i] = nil
100 end end end
101
102
103 -- metatables & co. ------------------------------------------ metatables & co.
104
105
106 local resolver = {}
107 resolver.__index = resolver
108
109
110 local SRV_tostring
111
112
113 local rr_metatable = {} -- - - - - - - - - - - - - - - - - - - rr_metatable
114 function rr_metatable.__tostring (rr)
115 local s0 = string.format (
116 '%2s %-5s %6i %-28s', rr.class, rr.type, rr.ttl, rr.name )
117 local s1 = ''
118 if rr.type == 'A' then s1 = ' '..rr.a
119 elseif rr.type == 'MX' then
120 s1 = string.format (' %2i %s', rr.pref, rr.mx)
121 elseif rr.type == 'CNAME' then s1 = ' '..rr.cname
122 elseif rr.type == 'LOC' then s1 = ' '..resolver.LOC_tostring (rr)
123 elseif rr.type == 'NS' then s1 = ' '..rr.ns
124 elseif rr.type == 'SRV' then s1 = ' '..SRV_tostring (rr)
125 elseif rr.type == 'TXT' then s1 = ' '..rr.txt
126 else s1 = ' <UNKNOWN RDATA TYPE>' end
127 return s0..s1
128 end
129
130
131 local rrs_metatable = {} -- - - - - - - - - - - - - - - - - - rrs_metatable
132 function rrs_metatable.__tostring (rrs)
133 local t = {}
134 for i,rr in pairs (rrs) do append (t, tostring (rr)..'\n') end
135 return table.concat (t)
136 end
137
138
139 local cache_metatable = {} -- - - - - - - - - - - - - - - - cache_metatable
140 function cache_metatable.__tostring (cache)
141 local time = socket.gettime ()
142 local t = {}
143 for class,types in pairs (cache) do
144 for type,names in pairs (types) do
145 for name,rrs in pairs (names) do
146 prune (rrs, time)
147 append (t, tostring (rrs)) end end end
148 return table.concat (t)
149 end
150
151
152 function resolver:new () -- - - - - - - - - - - - - - - - - - - - - resolver
153 local r = { active = {}, cache = {}, unsorted = {} }
154 setmetatable (r, resolver)
155 setmetatable (r.cache, cache_metatable)
156 setmetatable (r.unsorted, { __mode = 'kv' })
157 return r
158 end
159
160
161 -- packet layer -------------------------------------------------- packet layer
162
163
164 function dns.random (...) -- - - - - - - - - - - - - - - - - - - dns.random
165 math.randomseed (10000*socket.gettime ())
166 dns.random = math.random
167 return dns.random (...)
168 end
169
170
171 local function encodeHeader (o) -- - - - - - - - - - - - - - - encodeHeader
172
173 o = o or {}
174
175 o.id = o.id or -- 16b (random) id
176 dns.random (0, 0xffff)
177
178 o.rd = o.rd or 1 -- 1b 1 recursion desired
179 o.tc = o.tc or 0 -- 1b 1 truncated response
180 o.aa = o.aa or 0 -- 1b 1 authoritative response
181 o.opcode = o.opcode or 0 -- 4b 0 query
182 -- 1 inverse query
183 -- 2 server status request
184 -- 3-15 reserved
185 o.qr = o.qr or 0 -- 1b 0 query, 1 response
186
187 o.rcode = o.rcode or 0 -- 4b 0 no error
188 -- 1 format error
189 -- 2 server failure
190 -- 3 name error
191 -- 4 not implemented
192 -- 5 refused
193 -- 6-15 reserved
194 o.z = o.z or 0 -- 3b 0 resvered
195 o.ra = o.ra or 0 -- 1b 1 recursion available
196
197 o.qdcount = o.qdcount or 1 -- 16b number of question RRs
198 o.ancount = o.ancount or 0 -- 16b number of answers RRs
199 o.nscount = o.nscount or 0 -- 16b number of nameservers RRs
200 o.arcount = o.arcount or 0 -- 16b number of additional RRs
201
202 -- string.char() rounds, so prevent roundup with -0.4999
203 local header = string.char (
204 highbyte (o.id), o.id %0x100,
205 o.rd + 2*o.tc + 4*o.aa + 8*o.opcode + 128*o.qr,
206 o.rcode + 16*o.z + 128*o.ra,
207 highbyte (o.qdcount), o.qdcount %0x100,
208 highbyte (o.ancount), o.ancount %0x100,
209 highbyte (o.nscount), o.nscount %0x100,
210 highbyte (o.arcount), o.arcount %0x100 )
211
212 return header, o.id
213 end
214
215
216 local function encodeName (name) -- - - - - - - - - - - - - - - - encodeName
217 local t = {}
218 for part in string.gmatch (name, '[^.]+') do
219 append (t, string.char (string.len (part)))
220 append (t, part)
221 end
222 append (t, string.char (0))
223 return table.concat (t)
224 end
225
226
227 local function encodeQuestion (qname, qtype, qclass) -- - - - encodeQuestion
228 qname = encodeName (qname)
229 qtype = dns.typecode[qtype or 'a']
230 qclass = dns.classcode[qclass or 'in']
231 return qname..qtype..qclass;
232 end
233
234
235 function resolver:byte (len) -- - - - - - - - - - - - - - - - - - - - - byte
236 len = len or 1
237 local offset = self.offset
238 local last = offset + len - 1
239 if last > #self.packet then
240 error (string.format ('out of bounds: %i>%i', last, #self.packet)) end
241 self.offset = offset + len
242 return string.byte (self.packet, offset, last)
243 end
244
245
246 function resolver:word () -- - - - - - - - - - - - - - - - - - - - - - word
247 local b1, b2 = self:byte (2)
248 return 0x100*b1 + b2
249 end
250
251
252 function resolver:dword () -- - - - - - - - - - - - - - - - - - - - - dword
253 local b1, b2, b3, b4 = self:byte (4)
254 -- print ('dword', b1, b2, b3, b4)
255 return 0x1000000*b1 + 0x10000*b2 + 0x100*b3 + b4
256 end
257
258
259 function resolver:sub (len) -- - - - - - - - - - - - - - - - - - - - - - sub
260 len = len or 1
261 local s = string.sub (self.packet, self.offset, self.offset + len - 1)
262 self.offset = self.offset + len
263 return s
264 end
265
266
267 function resolver:header (force) -- - - - - - - - - - - - - - - - - - header
268
269 local id = self:word ()
270 -- print (string.format (':header id %x', id))
271 if not self.active[id] and not force then return nil end
272
273 local h = { id = id }
274
275 local b1, b2 = self:byte (2)
276
277 h.rd = b1 %2
278 h.tc = b1 /2%2
279 h.aa = b1 /4%2
280 h.opcode = b1 /8%16
281 h.qr = b1 /128
282
283 h.rcode = b2 %16
284 h.z = b2 /16%8
285 h.ra = b2 /128
286
287 h.qdcount = self:word ()
288 h.ancount = self:word ()
289 h.nscount = self:word ()
290 h.arcount = self:word ()
291
292 for k,v in pairs (h) do h[k] = v-v%1 end
293
294 return h
295 end
296
297
298 function resolver:name () -- - - - - - - - - - - - - - - - - - - - - - name
299 local remember, pointers = nil, 0
300 local len = self:byte ()
301 local n = {}
302 while len > 0 do
303 if len >= 0xc0 then -- name is "compressed"
304 pointers = pointers + 1
305 if pointers >= 20 then error ('dns error: 20 pointers') end
306 local offset = ((len-0xc0)*0x100) + self:byte ()
307 remember = remember or self.offset
308 self.offset = offset + 1 -- +1 for lua
309 else -- name is not compressed
310 append (n, self:sub (len)..'.')
311 end
312 len = self:byte ()
313 end
314 self.offset = remember or self.offset
315 return table.concat (n)
316 end
317
318
319 function resolver:question () -- - - - - - - - - - - - - - - - - - question
320 local q = {}
321 q.name = self:name ()
322 q.type = dns.type[self:word ()]
323 q.class = dns.type[self:word ()]
324 return q
325 end
326
327
328 function resolver:A (rr) -- - - - - - - - - - - - - - - - - - - - - - - - A
329 local b1, b2, b3, b4 = self:byte (4)
330 rr.a = string.format ('%i.%i.%i.%i', b1, b2, b3, b4)
331 end
332
333
334 function resolver:CNAME (rr) -- - - - - - - - - - - - - - - - - - - - CNAME
335 rr.cname = self:name ()
336 end
337
338
339 function resolver:MX (rr) -- - - - - - - - - - - - - - - - - - - - - - - MX
340 rr.pref = self:word ()
341 rr.mx = self:name ()
342 end
343
344
345 function resolver:LOC_nibble_power () -- - - - - - - - - - LOC_nibble_power
346 local b = self:byte ()
347 -- print ('nibbles', ((b-(b%0x10))/0x10), (b%0x10))
348 return ((b-(b%0x10))/0x10) * (10^(b%0x10))
349 end
350
351
352 function resolver:LOC (rr) -- - - - - - - - - - - - - - - - - - - - - - LOC
353 rr.version = self:byte ()
354 if rr.version == 0 then
355 rr.loc = rr.loc or {}
356 rr.loc.size = self:LOC_nibble_power ()
357 rr.loc.horiz_pre = self:LOC_nibble_power ()
358 rr.loc.vert_pre = self:LOC_nibble_power ()
359 rr.loc.latitude = self:dword ()
360 rr.loc.longitude = self:dword ()
361 rr.loc.altitude = self:dword ()
362 end end
363
364
365 local function LOC_tostring_degrees (f, pos, neg) -- - - - - - - - - - - - -
366 f = f - 0x80000000
367 if f < 0 then pos = neg f = -f end
368 local deg, min, msec
369 msec = f%60000
370 f = (f-msec)/60000
371 min = f%60
372 deg = (f-min)/60
373 return string.format ('%3d %2d %2.3f %s', deg, min, msec/1000, pos)
374 end
375
376
377 function resolver.LOC_tostring (rr) -- - - - - - - - - - - - - LOC_tostring
378
379 local t = {}
380
381 --[[
382 for k,name in pairs { 'size', 'horiz_pre', 'vert_pre',
383 'latitude', 'longitude', 'altitude' } do
384 append (t, string.format ('%4s%-10s: %12.0f\n', '', name, rr.loc[name]))
385 end
386 --]]
387
388 append ( t, string.format (
389 '%s %s %.2fm %.2fm %.2fm %.2fm',
390 LOC_tostring_degrees (rr.loc.latitude, 'N', 'S'),
391 LOC_tostring_degrees (rr.loc.longitude, 'E', 'W'),
392 (rr.loc.altitude - 10000000) / 100,
393 rr.loc.size / 100,
394 rr.loc.horiz_pre / 100,
395 rr.loc.vert_pre / 100 ) )
396
397 return table.concat (t)
398 end
399
400
401 function resolver:NS (rr) -- - - - - - - - - - - - - - - - - - - - - - - NS
402 rr.ns = self:name ()
403 end
404
405
406 function resolver:SOA (rr) -- - - - - - - - - - - - - - - - - - - - - - SOA
407 end
408
409
410 function resolver:SRV (rr) -- - - - - - - - - - - - - - - - - - - - - - SRV
411 rr.srv = {}
412 rr.srv.priority = self:word ()
413 rr.srv.weight = self:word ()
414 rr.srv.port = self:word ()
415 rr.srv.target = self:name ()
416 end
417
418
419 function SRV_tostring (rr) -- - - - - - - - - - - - - - - - - - SRV_tostring
420 local s = rr.srv
421 return string.format ( '%5d %5d %5d %s',
422 s.priority, s.weight, s.port, s.target )
423 end
424
425
426 function resolver:TXT (rr) -- - - - - - - - - - - - - - - - - - - - - - TXT
427 rr.txt = self:sub (rr.rdlength)
428 end
429
430
431 function resolver:rr () -- - - - - - - - - - - - - - - - - - - - - - - - rr
432 local rr = {}
433 setmetatable (rr, rr_metatable)
434 rr.name = self:name (self)
435 rr.type = dns.type[self:word ()] or rr.type
436 rr.class = dns.class[self:word ()] or rr.class
437 rr.ttl = 0x10000*self:word () + self:word ()
438 rr.rdlength = self:word ()
439
440 if rr.ttl == 0 then -- pass
441 else rr.tod = self.time + rr.ttl end
442
443 local remember = self.offset
444 local rr_parser = self[dns.type[rr.type]]
445 if rr_parser then rr_parser (self, rr) end
446 self.offset = remember
447 rr.rdata = self:sub (rr.rdlength)
448 return rr
449 end
450
451
452 function resolver:rrs (count) -- - - - - - - - - - - - - - - - - - - - - rrs
453 local rrs = {}
454 for i = 1,count do append (rrs, self:rr ()) end
455 return rrs
456 end
457
458
459 function resolver:decode (packet, force) -- - - - - - - - - - - - - - decode
460
461 self.packet, self.offset = packet, 1
462 local header = self:header (force)
463 if not header then return nil end
464 local response = { header = header }
465
466 response.question = {}
467 local offset = self.offset
468 for i = 1,response.header.qdcount do
469 append (response.question, self:question ()) end
470 response.question.raw = string.sub (self.packet, offset, self.offset - 1)
471
472 if not force then
473 if not self.active[response.header.id] or
474 not self.active[response.header.id][response.question.raw] then
475 return nil end end
476
477 response.answer = self:rrs (response.header.ancount)
478 response.authority = self:rrs (response.header.nscount)
479 response.additional = self:rrs (response.header.arcount)
480
481 return response
482 end
483
484
485 -- socket layer -------------------------------------------------- socket layer
486
487
488 resolver.delays = { 1, 3, 11, 45 }
489
490
491 function resolver:addnameserver (address) -- - - - - - - - - - addnameserver
492 self.server = self.server or {}
493 append (self.server, address)
494 end
495
496
497 function resolver:setnameserver (address) -- - - - - - - - - - setnameserver
498 self.server = {}
499 self:addnameserver (address)
500 end
501
502
503 function resolver:adddefaultnameservers () -- - - - - adddefaultnameservers
504 local resolv_conf = io.open("/etc/resolv.conf");
505 if resolv_conf then
506 for line in resolv_conf:lines() do
507 local address = string.match (line, 'nameserver%s+(%d+%.%d+%.%d+%.%d+)')
508 if address then self:addnameserver (address) end
509 end
510 else -- FIXME correct for windows, using opendns nameservers for now
511 self:addnameserver ("208.67.222.222")
512 self:addnameserver ("208.67.220.220")
513 end
514 end
515
516
517 function resolver:getsocket (servernum) -- - - - - - - - - - - - - getsocket
518
519 self.socket = self.socket or {}
520 self.socketset = self.socketset or {}
521
522 local sock = self.socket[servernum]
523 if sock then return sock end
524
525 sock = socket.udp ()
526 if self.socket_wrapper then sock = self.socket_wrapper (sock) end
527 sock:settimeout (0)
528 -- todo: attempt to use a random port, fallback to 0
529 sock:setsockname ('*', 0)
530 sock:setpeername (self.server[servernum], 53)
531 self.socket[servernum] = sock
532 self.socketset[sock] = sock
533 return sock
534 end
535
536
537 function resolver:socket_wrapper_set (func) -- - - - - - - socket_wrapper_set
538 self.socket_wrapper = func
539 end
540
541
542 function resolver:closeall () -- - - - - - - - - - - - - - - - - - closeall
543 for i,sock in ipairs (self.socket) do self.socket[i]:close () end
544 self.socket = {}
545 end
546
547
548 function resolver:remember (rr, type) -- - - - - - - - - - - - - - remember
549
550 -- print ('remember', type, rr.class, rr.type, rr.name)
551
552 if type ~= '*' then
553 type = rr.type
554 local all = get (self.cache, rr.class, '*', rr.name)
555 -- print ('remember all', all)
556 if all then append (all, rr) end
557 end
558
559 self.cache = self.cache or setmetatable ({}, cache_metatable)
560 local rrs = get (self.cache, rr.class, type, rr.name) or
561 set (self.cache, rr.class, type, rr.name, setmetatable ({}, rrs_metatable))
562 append (rrs, rr)
563
564 if type == 'MX' then self.unsorted[rrs] = true end
565 end
566
567
568 local function comp_mx (a, b) -- - - - - - - - - - - - - - - - - - - comp_mx
569 return (a.pref == b.pref) and (a.mx < b.mx) or (a.pref < b.pref)
570 end
571
572
573 function resolver:peek (qname, qtype, qclass) -- - - - - - - - - - - - peek
574 qname, qtype, qclass = standardize (qname, qtype, qclass)
575 local rrs = get (self.cache, qclass, qtype, qname)
576 if not rrs then return nil end
577 if prune (rrs, socket.gettime ()) and qtype == '*' or not next (rrs) then
578 set (self.cache, qclass, qtype, qname, nil) return nil end
579 if self.unsorted[rrs] then table.sort (rrs, comp_mx) end
580 return rrs
581 end
582
583
584 function resolver:purge (soft) -- - - - - - - - - - - - - - - - - - - purge
585 if soft == 'soft' then
586 self.time = socket.gettime ()
587 for class,types in pairs (self.cache or {}) do
588 for type,names in pairs (types) do
589 for name,rrs in pairs (names) do
590 prune (rrs, self.time, 'soft')
591 end end end
592 else self.cache = {} end
593 end
594
595
596 function resolver:query (qname, qtype, qclass) -- - - - - - - - - - -- query
597
598 qname, qtype, qclass = standardize (qname, qtype, qclass)
599
600 if not self.server then self:adddefaultnameservers () end
601
602 local question = encodeQuestion (qname, qtype, qclass)
603 local peek = self:peek (qname, qtype, qclass)
604 if peek then return peek end
605
606 local header, id = encodeHeader ()
607 -- print ('query id', id, qclass, qtype, qname)
608 local o = { packet = header..question,
609 server = 1,
610 delay = 1,
611 retry = socket.gettime () + self.delays[1] }
612 self:getsocket (o.server):send (o.packet)
613
614 -- remember the query
615 self.active[id] = self.active[id] or {}
616 self.active[id][question] = o
617
618 -- remember which coroutine wants the answer
619 local co = coroutine.running ()
620 if co then
621 set (self.wanted, qclass, qtype, qname, co, true)
622 set (self.yielded, co, qclass, qtype, qname, true)
623 end end
624
625
626 function resolver:receive (rset) -- - - - - - - - - - - - - - - - - receive
627
628 -- print 'receive' print (self.socket)
629 self.time = socket.gettime ()
630 rset = rset or self.socket
631
632 local response
633 for i,sock in pairs (rset) do
634
635 if self.socketset[sock] then
636 local packet = sock:receive ()
637 if packet then
638
639 response = self:decode (packet)
640 if response then
641 -- print 'received response'
642 -- self.print (response)
643
644 for i,section in pairs { 'answer', 'authority', 'additional' } do
645 for j,rr in pairs (response[section]) do
646 self:remember (rr, response.question[1].type) end end
647
648 -- retire the query
649 local queries = self.active[response.header.id]
650 if queries[response.question.raw] then
651 queries[response.question.raw] = nil end
652 if not next (queries) then self.active[response.header.id] = nil end
653 if not next (self.active) then self:closeall () end
654
655 -- was the query on the wanted list?
656 local q = response.question
657 local cos = get (self.wanted, q.class, q.type, q.name)
658 if cos then
659 for co in pairs (cos) do
660 set (self.yielded, co, q.class, q.type, q.name, nil)
661 if not self.yielded[co] then coroutine.resume (co) end
662 end
663 set (self.wanted, q.class, q.type, q.name, nil)
664 end end end end end
665
666 return response
667 end
668
669
670 function resolver:pulse () -- - - - - - - - - - - - - - - - - - - - - pulse
671
672 -- print ':pulse'
673 while self:receive () do end
674 if not next (self.active) then return nil end
675
676 self.time = socket.gettime ()
677 for id,queries in pairs (self.active) do
678 for question,o in pairs (queries) do
679 if self.time >= o.retry then
680
681 o.server = o.server + 1
682 if o.server > #self.server then
683 o.server = 1
684 o.delay = o.delay + 1
685 end
686
687 if o.delay > #self.delays then
688 print ('timeout')
689 queries[question] = nil
690 if not next (queries) then self.active[id] = nil end
691 if not next (self.active) then return nil end
692 else
693 -- print ('retry', o.server, o.delay)
694 self.socket[o.server]:send (o.packet)
695 o.retry = self.time + self.delays[o.delay]
696 end end end end
697
698 if next (self.active) then return true end
699 return nil
700 end
701
702
703 function resolver:lookup (qname, qtype, qclass) -- - - - - - - - - - lookup
704 self:query (qname, qtype, qclass)
705 while self:pulse () do socket.select (self.socket, nil, 4) end
706 -- print (self.cache)
707 return self:peek (qname, qtype, qclass)
708 end
709
710
711 -- print ---------------------------------------------------------------- print
712
713
714 local hints = { -- - - - - - - - - - - - - - - - - - - - - - - - - - - hints
715 qr = { [0]='query', 'response' },
716 opcode = { [0]='query', 'inverse query', 'server status request' },
717 aa = { [0]='non-authoritative', 'authoritative' },
718 tc = { [0]='complete', 'truncated' },
719 rd = { [0]='recursion not desired', 'recursion desired' },
720 ra = { [0]='recursion not available', 'recursion available' },
721 z = { [0]='(reserved)' },
722 rcode = { [0]='no error', 'format error', 'server failure', 'name error',
723 'not implemented' },
724
725 type = dns.type,
726 class = dns.class, }
727
728
729 local function hint (p, s) -- - - - - - - - - - - - - - - - - - - - - - hint
730 return (hints[s] and hints[s][p[s]]) or '' end
731
732
733 function resolver.print (response) -- - - - - - - - - - - - - resolver.print
734
735 for s,s in pairs { 'id', 'qr', 'opcode', 'aa', 'tc', 'rd', 'ra', 'z',
736 'rcode', 'qdcount', 'ancount', 'nscount', 'arcount' } do
737 print ( string.format ('%-30s', 'header.'..s),
738 response.header[s], hint (response.header, s) )
739 end
740
741 for i,question in ipairs (response.question) do
742 print (string.format ('question[%i].name ', i), question.name)
743 print (string.format ('question[%i].type ', i), question.type)
744 print (string.format ('question[%i].class ', i), question.class)
745 end
746
747 local common = { name=1, type=1, class=1, ttl=1, rdlength=1, rdata=1 }
748 local tmp
749 for s,s in pairs {'answer', 'authority', 'additional'} do
750 for i,rr in pairs (response[s]) do
751 for j,t in pairs { 'name', 'type', 'class', 'ttl', 'rdlength' } do
752 tmp = string.format ('%s[%i].%s', s, i, t)
753 print (string.format ('%-30s', tmp), rr[t], hint (rr, t))
754 end
755 for j,t in pairs (rr) do
756 if not common[j] then
757 tmp = string.format ('%s[%i].%s', s, i, j)
758 print (string.format ('%-30s %s', tmp, t))
759 end end end end end
760
761
762 -- module api ------------------------------------------------------ module api
763
764
765 local function resolve (func, ...) -- - - - - - - - - - - - - - resolver_get
766 dns._resolver = dns._resolver or dns.resolver ()
767 return func (dns._resolver, ...)
768 end
769
770
771 function dns.resolver () -- - - - - - - - - - - - - - - - - - - - - resolver
772
773 -- this function seems to be redundant with resolver.new ()
774
775 local r = { active = {}, cache = {}, unsorted = {}, wanted = {}, yielded = {} }
776 setmetatable (r, resolver)
777 setmetatable (r.cache, cache_metatable)
778 setmetatable (r.unsorted, { __mode = 'kv' })
779 return r
780 end
781
782
783 function dns.lookup (...) -- - - - - - - - - - - - - - - - - - - - - lookup
784 return resolve (resolver.lookup, ...) end
785
786
787 function dns.purge (...) -- - - - - - - - - - - - - - - - - - - - - - purge
788 return resolve (resolver.purge, ...) end
789
790 function dns.peek (...) -- - - - - - - - - - - - - - - - - - - - - - - peek
791 return resolve (resolver.peek, ...) end
792
793
794 function dns.query (...) -- - - - - - - - - - - - - - - - - - - - - - query
795 return resolve (resolver.query, ...) end
796
797
798 function dns:socket_wrapper_set (...) -- - - - - - - - - socket_wrapper_set
799 return resolve (resolver.socket_wrapper_set, ...) end
800
801
802 return dns

mercurial