+function resolver:query(qname, qtype, qclass) -- - - - - - - - - - -- query
+ qname, qtype, qclass = standardize(qname, qtype, qclass)
+
+ if not self.server then self:adddefaultnameservers(); end
+
+ local question = encodeQuestion(qname, qtype, qclass);
+ local peek = self:peek (qname, qtype, qclass);
+ if peek then return peek; end
+
+ local header, id = encodeHeader();
+ --print ('query id', id, qclass, qtype, qname)
+ local o = {
+ packet = header..question,
+ server = self.best_server,
+ delay = 1,
+ retry = socket.gettime() + self.delays[1]
+ };
+
+ -- remember the query
+ self.active[id] = self.active[id] or {};
+ self.active[id][question] = o;
+
+ -- remember which coroutine wants the answer
+ local co = coroutine.running();
+ if co then
+ set(self.wanted, qclass, qtype, qname, co, true);
+ --set(self.yielded, co, qclass, qtype, qname, true);
+ end
+
+ local conn, err = self:getsocket(o.server)
+ if not conn then
+ return nil, err;
+ end
+ conn:send (o.packet)
+
+ if timer and self.timeout then
+ local num_servers = #self.server;
+ local i = 1;
+ timer.add_task(self.timeout, function ()
+ if get(self.wanted, qclass, qtype, qname, co) then
+ if i < num_servers then
+ i = i + 1;
+ self:servfail(conn);
+ o.server = self.best_server;
+ conn, err = self:getsocket(o.server);
+ if conn then
+ conn:send(o.packet);
+ return self.timeout;
+ end
+ end
+ -- Tried everything, failed
+ self:cancel(qclass, qtype, qname, co, true);
+ end
+ end)
+ end
+ return true;
+end
+
+function resolver:servfail(sock)
+ -- Resend all queries for this server
+
+ local num = self.socketset[sock]
+
+ -- Socket is dead now
+ self:voidsocket(sock);
+
+ -- Find all requests to the down server, and retry on the next server
+ self.time = socket.gettime();
+ for id,queries in pairs(self.active) do
+ for question,o in pairs(queries) do
+ if o.server == num then -- This request was to the broken server
+ o.server = o.server + 1 -- Use next server
+ if o.server > #self.server then
+ o.server = 1;
+ end
+
+ o.retries = (o.retries or 0) + 1;
+ if o.retries >= #self.server then
+ --print('timeout');
+ queries[question] = nil;
+ else
+ local _a = self:getsocket(o.server);
+ if _a then _a:send(o.packet); end
+ end
+ end
+ end
+ end
+
+ if num == self.best_server then
+ self.best_server = self.best_server + 1;
+ if self.best_server > #self.server then
+ -- Exhausted all servers, try first again
+ self.best_server = 1;
+ end
+ end
+end
+
+function resolver:settimeout(seconds)
+ self.timeout = seconds;
+end
+
+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)
+ end
+ --print(self.cache);
+ return self:peek(qname, qtype, qclass);
+end
+
+function resolver:lookupex(handler, qname, qtype, qclass) -- - - - - - - - - - lookup
+ return self:peek(qname, qtype, qclass) or self:query(qname, qtype, qclass);
+end
+
+function resolver:tohostname(ip)
+ return dns.lookup(ip:gsub("(%d+)%.(%d+)%.(%d+)%.(%d+)", "%4.%3.%2.%1.in-addr.arpa."), "PTR");
+end
+
+--print ---------------------------------------------------------------- print