-local hints = { -- - - - - - - - - - - - - - - - - - - - - - - - - - - hints
- qr = { [0]='query', 'response' },
- opcode = { [0]='query', 'inverse query', 'server status request' },
- aa = { [0]='non-authoritative', 'authoritative' },
- tc = { [0]='complete', 'truncated' },
- rd = { [0]='recursion not desired', 'recursion desired' },
- ra = { [0]='recursion not available', 'recursion available' },
- z = { [0]='(reserved)' },
- rcode = { [0]='no error', 'format error', 'server failure', 'name error',
- 'not implemented' },
-
- type = dns.type,
- class = dns.class, }
-
-
-local function hint (p, s) -- - - - - - - - - - - - - - - - - - - - - - hint
- return (hints[s] and hints[s][p[s]]) or '' end
-
-
-function resolver.print (response) -- - - - - - - - - - - - - resolver.print
-
- for s,s in pairs { 'id', 'qr', 'opcode', 'aa', 'tc', 'rd', 'ra', 'z',
- 'rcode', 'qdcount', 'ancount', 'nscount', 'arcount' } do
- print ( string.format ('%-30s', 'header.'..s),
- response.header[s], hint (response.header, s) )
- end
-
- for i,question in ipairs (response.question) do
- print (string.format ('question[%i].name ', i), question.name)
- print (string.format ('question[%i].type ', i), question.type)
- print (string.format ('question[%i].class ', i), question.class)
- end
-
- local common = { name=1, type=1, class=1, ttl=1, rdlength=1, rdata=1 }
- local tmp
- for s,s in pairs {'answer', 'authority', 'additional'} do
- for i,rr in pairs (response[s]) do
- for j,t in pairs { 'name', 'type', 'class', 'ttl', 'rdlength' } do
- tmp = string.format ('%s[%i].%s', s, i, t)
- print (string.format ('%-30s', tmp), rr[t], hint (rr, t))
+function resolver:receive(rset) -- - - - - - - - - - - - - - - - - receive
+ --print('receive'); print(self.socket);
+ self.time = socket.gettime();
+ rset = rset or self.socket;
+
+ local response;
+ for i,sock in pairs(rset) do
+
+ if self.socketset[sock] then
+ local packet = sock:receive();
+ if packet then
+ response = self:decode(packet);
+ if response and self.active[response.header.id]
+ and self.active[response.header.id][response.question.raw] then
+ --print('received response');
+ --self.print(response);
+
+ for j,rr in pairs(response.answer) do
+ if rr.name:sub(-#response.question[1].name, -1) == response.question[1].name then
+ self:remember(rr, response.question[1].type)
+ end
+ end
+
+ -- retire the query
+ local queries = self.active[response.header.id];
+ queries[response.question.raw] = nil;
+
+ if not next(queries) then self.active[response.header.id] = nil; end
+ if not next(self.active) then self:closeall(); end
+
+ -- was the query on the wanted list?
+ local q = response.question[1];
+ local cos = get(self.wanted, q.class, q.type, q.name);
+ if cos then
+ for co in pairs(cos) do
+ set(self.yielded, co, q.class, q.type, q.name, nil);
+ if coroutine.status(co) == "suspended" then coroutine.resume(co); end
+ end
+ set(self.wanted, q.class, q.type, q.name, nil);
+ end
+ end
+ end
+ end
+ end
+
+ return response;
+end
+
+
+function resolver:feed(sock, packet, force)
+ --print('receive'); print(self.socket);
+ self.time = socket.gettime();
+
+ local response = self:decode(packet, force);
+ if response and self.active[response.header.id]
+ and self.active[response.header.id][response.question.raw] then
+ --print('received response');
+ --self.print(response);
+
+ for j,rr in pairs(response.answer) do
+ self:remember(rr, response.question[1].type);
+ end
+
+ -- retire the query
+ local queries = self.active[response.header.id];
+ queries[response.question.raw] = nil;
+ if not next(queries) then self.active[response.header.id] = nil; end
+ if not next(self.active) then self:closeall(); end
+
+ -- was the query on the wanted list?
+ local q = response.question[1];
+ if q then
+ local cos = get(self.wanted, q.class, q.type, q.name);
+ if cos then
+ for co in pairs(cos) do
+ set(self.yielded, co, q.class, q.type, q.name, nil);
+ if coroutine.status(co) == "suspended" then coroutine.resume(co); end
+ end
+ set(self.wanted, q.class, q.type, q.name, nil);
+ end
+ end
+ end
+
+ return response;
+end
+
+function resolver:cancel(qclass, qtype, qname, co, call_handler)
+ local cos = get(self.wanted, qclass, qtype, qname);
+ if cos then
+ if call_handler then
+ coroutine.resume(co);
+ end
+ cos[co] = nil;
+ end
+end
+
+function resolver:pulse() -- - - - - - - - - - - - - - - - - - - - - pulse
+ --print(':pulse');
+ while self:receive() do end
+ if not next(self.active) then return nil; end
+
+ self.time = socket.gettime();
+ for id,queries in pairs(self.active) do
+ for question,o in pairs(queries) do
+ if self.time >= o.retry then
+
+ o.server = o.server + 1;
+ if o.server > #self.server then
+ o.server = 1;
+ o.delay = o.delay + 1;
+ end
+
+ if o.delay > #self.delays then
+ --print('timeout');
+ queries[question] = nil;
+ if not next(queries) then self.active[id] = nil; end
+ if not next(self.active) then return nil; end
+ else
+ --print('retry', o.server, o.delay);
+ local _a = self.socket[o.server];
+ if _a then _a:send(o.packet); end
+ o.retry = self.time + self.delays[o.delay];
+ end
+ end
+ end
+ end
+
+ if next(self.active) then return true; end
+ return nil;
+end
+
+
+function resolver:lookup(qname, qtype, qclass) -- - - - - - - - - - lookup
+ self:query (qname, qtype, qclass)
+ while self:pulse() do
+ local recvt = {}
+ for i, s in ipairs(self.socket) do
+ recvt[i] = s
+ end
+ socket.select(recvt, nil, 4)