- qname, qtype, qclass = standardize (qname, qtype, qclass)
- local rrs = get (self.cache, qclass, qtype, qname)
- if not rrs then return nil end
- if prune (rrs, socket.gettime ()) and qtype == '*' or not next (rrs) then
- set (self.cache, qclass, qtype, qname, nil) return nil end
- if self.unsorted[rrs] then table.sort (rrs, comp_mx) end
- return rrs
- end
-
-
-function resolver:purge (soft) -- - - - - - - - - - - - - - - - - - - purge
- if soft == 'soft' then
- self.time = socket.gettime ()
- for class,types in pairs (self.cache or {}) do
- for type,names in pairs (types) do
- for name,rrs in pairs (names) do
- prune (rrs, time, 'soft')
- end end end
- else self.cache = {} end
- end
-
-
-function resolver:query (qname, qtype, qclass) -- - - - - - - - - - -- query
-
- qname, qtype, qclass = standardize (qname, qtype, qclass)
-
- if not self.server then self:adddefaultnameservers () end
-
- local question = question or 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 = 1,
- delay = 1,
- retry = socket.gettime () + self.delays[1] }
- self:getsocket (o.server):send (o.packet)
-
- -- 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 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 then
- -- print 'received response'
- -- self.print (response)
-
- for i,section in pairs { 'answer', 'authority', 'additional' } do
- for j,rr in pairs (response[section]) do
- self:remember (rr, response.question[1].type) end end
-
- -- retire the query
- local queries = self.active[response.header.id]
- if queries[response.question.raw] then
- queries[response.question.raw] = nil end
- 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
- 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 not self.yielded[co] 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: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)
- self.socket[o.server]:send (o.packet)
- 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 socket.select (self.socket, nil, 4) end
- -- print (self.cache)
- return self:peek (qname, qtype, qclass)
- end
-
-
--- print ---------------------------------------------------------------- print
+ qname, qtype, qclass = standardize(qname, qtype, qclass);
+ local rrs = get(self.cache, qclass, qtype, qname);
+ if not rrs then return nil; end
+ if prune(rrs, socket.gettime()) and qtype == '*' or not next(rrs) then
+ set(self.cache, qclass, qtype, qname, nil);
+ return nil;
+ end
+ if self.unsorted[rrs] then table.sort (rrs, comp_mx); end
+ return rrs;
+end
+
+
+function resolver:purge(soft) -- - - - - - - - - - - - - - - - - - - purge
+ if soft == 'soft' then
+ self.time = socket.gettime();
+ for class,types in pairs(self.cache or {}) do
+ for type,names in pairs(types) do
+ for name,rrs in pairs(names) do
+ prune(rrs, self.time, 'soft')
+ end
+ end
+ end
+ else self.cache = {}; end
+end
+
+
+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 = self:getsocket(o.server)
+ 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 = self:getsocket(o.server);
+ conn:send(o.packet);
+ return self.timeout;
+ else
+ -- Tried everything, failed
+ self:cancel(qclass, qtype, qname, co, true);
+ end
+ end
+ end)
+ end
+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
+
+
+--print ---------------------------------------------------------------- print