X-Git-Url: https://git.enpas.org/?a=blobdiff_plain;ds=sidebyside;f=net%2Fdns.lua;h=f3d802919b9ae7dbeee77c8f6e7867395c117092;hb=2b29f5f271f7012663f647d8f4a7e11fd308db2d;hp=2c0089407dae66487e0644aaeb7c5c239fde0ba3;hpb=c4fc4c8756c449c02b6e4ab1490630464c7f3395;p=prosody.git diff --git a/net/dns.lua b/net/dns.lua index 2c008940..f3d80291 100644 --- a/net/dns.lua +++ b/net/dns.lua @@ -2,8 +2,6 @@ -- This file is included with Prosody IM. It has modifications, -- which are hereby placed in the public domain. --- public domain 20080404 lua@ztact.com - -- todo: quick (default) header generation -- todo: nxdomain, error handling @@ -15,18 +13,61 @@ local socket = require "socket"; -local ztact = require "util.ztact"; +local timer = require "util.timer"; + local _, windows = pcall(require, "util.windows"); local is_windows = (_ and windows) or os.getenv("WINDIR"); local coroutine, io, math, string, table = coroutine, io, math, string, table; -local ipairs, next, pairs, print, setmetatable, tostring, assert, error, unpack = - ipairs, next, pairs, print, setmetatable, tostring, assert, error, unpack; +local ipairs, next, pairs, print, setmetatable, tostring, assert, error, unpack, select = + ipairs, next, pairs, print, setmetatable, tostring, assert, error, unpack, select; + +local ztact = { -- public domain 20080404 lua@ztact.com + get = function(parent, ...) + local len = select('#', ...); + for i=1,len do + parent = parent[select(i, ...)]; + if parent == nil then break; end + end + return parent; + end; + set = function(parent, ...) + local len = select('#', ...); + local key, value = select(len-1, ...); + local cutpoint, cutkey; + + for i=1,len-2 do + local key = select (i, ...) + local child = parent[key] + + if value == nil then + if child == nil then + return; + elseif next(child, next(child)) then + cutpoint = nil; cutkey = nil; + elseif cutpoint == nil then + cutpoint = parent; cutkey = key; + end + elseif child == nil then + child = {}; + parent[key] = child; + end + parent = child + end + if value == nil and cutpoint then + cutpoint[cutkey] = nil; + else + parent[key] = value; + return value; + end + end; +}; local get, set = ztact.get, ztact.set; +local default_timeout = 15; -------------------------------------------------- module dns module('dns') @@ -115,6 +156,7 @@ end local resolver = {}; resolver.__index = resolver; +resolver.timeout = default_timeout; local SRV_tostring; @@ -183,7 +225,7 @@ end function dns.random(...) -- - - - - - - - - - - - - - - - - - - dns.random - math.randomseed(10000*socket.gettime()); + math.randomseed(math.floor(10000*socket.gettime())); dns.random = math.random; return dns.random(...); end @@ -434,6 +476,9 @@ function resolver:SRV(rr) -- - - - - - - - - - - - - - - - - - - - - - SRV rr.srv.target = self:name(); end +function resolver:PTR(rr) + rr.ptr = self:name(); +end function SRV_tostring(rr) -- - - - - - - - - - - - - - - - - - SRV_tostring local s = rr.srv; @@ -524,7 +569,7 @@ end function resolver:adddefaultnameservers() -- - - - - adddefaultnameservers if is_windows then - if windows then + if windows and windows.get_nameservers then for _, server in ipairs(windows.get_nameservers()) do self:addnameserver(server); end @@ -532,14 +577,19 @@ function resolver:adddefaultnameservers() -- - - - - adddefaultnameservers if not self.server or #self.server == 0 then -- TODO log warning about no nameservers, adding opendns servers as fallback self:addnameserver("208.67.222.222"); - self:addnameserver("208.67.220.220") ; + self:addnameserver("208.67.220.220"); end else -- posix local resolv_conf = io.open("/etc/resolv.conf"); if resolv_conf then for line in resolv_conf:lines() do - local address = line:gsub("#.*$", ""):match('^%s*nameserver%s+(%d+%.%d+%.%d+%.%d+)%s*$'); - if address then self:addnameserver(address) end + line = line:gsub("#.*$", "") + :match('^%s*nameserver%s+(.*)%s*$'); + if line then + line:gsub("%f[%d.](%d+%.%d+%.%d+%.%d+)%f[^%d.]", function (address) + self:addnameserver(address) + end); + end end end if not self.server or #self.server == 0 then @@ -594,17 +644,18 @@ end function resolver:remember(rr, type) -- - - - - - - - - - - - - - remember --print ('remember', type, rr.class, rr.type, rr.name) + local qname, qtype, qclass = standardize(rr.name, rr.type, rr.class); if type ~= '*' then - type = rr.type; - local all = get(self.cache, rr.class, '*', rr.name); + type = qtype; + local all = get(self.cache, qclass, '*', qname); --print('remember all', all); if all then append(all, rr); end end self.cache = self.cache or setmetatable({}, cache_metatable); - local rrs = get(self.cache, rr.class, type, rr.name) or - set(self.cache, rr.class, type, rr.name, setmetatable({}, rrs_metatable)); + local rrs = get(self.cache, qclass, type, qname) or + set(self.cache, qclass, type, qname, setmetatable({}, rrs_metatable)); append(rrs, rr); if type == 'MX' then self.unsorted[rrs] = true; end @@ -661,18 +712,39 @@ function resolver:query(qname, qtype, qclass) -- - - - - - - - - - -- query retry = socket.gettime() + self.delays[1] }; - -- remember the query + -- remember the query self.active[id] = self.active[id] or {}; self.active[id][question] = o; - -- remember which coroutine wants the answer + -- 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 - self:getsocket (o.server):send (o.packet) + 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) @@ -704,7 +776,7 @@ function resolver:servfail(sock) end end end - + if num == self.best_server then self.best_server = self.best_server + 1; if self.best_server > #self.server then @@ -714,6 +786,10 @@ function resolver:servfail(sock) end end +function resolver:settimeout(seconds) + self.timeout = seconds; +end + function resolver:receive(rset) -- - - - - - - - - - - - - - - - - receive --print('receive'); print(self.socket); self.time = socket.gettime(); @@ -745,7 +821,7 @@ function resolver:receive(rset) -- - - - - - - - - - - - - - - - - receive if not next(self.active) then self:closeall(); end -- was the query on the wanted list? - local q = response.question; + 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 @@ -763,26 +839,23 @@ function resolver:receive(rset) -- - - - - - - - - - - - - - - - - receive end -function resolver:feed(sock, packet) +function resolver:feed(sock, packet, force) --print('receive'); print(self.socket); self.time = socket.gettime(); - local response = self:decode(packet); - if response then + 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 i,section in pairs({ 'answer', 'authority', 'additional' }) do - for j,rr in pairs(response[section]) do - self:remember(rr, response.question[1].type); - end + 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]; - if queries[response.question.raw] then - queries[response.question.raw] = nil; - end + 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 @@ -798,15 +871,18 @@ function resolver:feed(sock, packet) set(self.wanted, q.class, q.type, q.name, nil); end end - end + end return response; end -function resolver:cancel(data) - local cos = get(self.wanted, unpack(data, 1, 3)); +function resolver:cancel(qclass, qtype, qname, co, call_handler) + local cos = get(self.wanted, qclass, qtype, qname); if cos then - cos[data[4]] = nil; + if call_handler then + coroutine.resume(co); + end + cos[co] = nil; end end @@ -848,7 +924,13 @@ end function resolver:lookup(qname, qtype, qclass) -- - - - - - - - - - lookup self:query (qname, qtype, qclass) - while self:pulse() do socket.select(self.socket, nil, 4); end + 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 @@ -857,6 +939,9 @@ function resolver:lookupex(handler, qname, qtype, qclass) -- - - - - - - - - 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 @@ -915,11 +1000,6 @@ end -- module api ------------------------------------------------------ module api -local function resolve(func, ...) -- - - - - - - - - - - - - - resolver_get - return func(dns._resolver, ...); -end - - function dns.resolver () -- - - - - - - - - - - - - - - - - - - - - resolver -- this function seems to be redundant with resolver.new () @@ -930,37 +1010,43 @@ function dns.resolver () -- - - - - - - - - - - - - - - - - - - - - resolver return r; end +local _resolver = dns.resolver(); +dns._resolver = _resolver; function dns.lookup(...) -- - - - - - - - - - - - - - - - - - - - - lookup - return resolve(resolver.lookup, ...); + return _resolver:lookup(...); end +function dns.tohostname(...) + return _resolver:tohostname(...); +end function dns.purge(...) -- - - - - - - - - - - - - - - - - - - - - - purge - return resolve(resolver.purge, ...); + return _resolver:purge(...); end function dns.peek(...) -- - - - - - - - - - - - - - - - - - - - - - - peek - return resolve(resolver.peek, ...); + return _resolver:peek(...); end - function dns.query(...) -- - - - - - - - - - - - - - - - - - - - - - query - return resolve(resolver.query, ...); + return _resolver:query(...); end -function dns.feed(...) -- - - - - - - - - - - - - - - - - - - - - - feed - return resolve(resolver.feed, ...); +function dns.feed(...) -- - - - - - - - - - - - - - - - - - - - - - - feed + return _resolver:feed(...); end -function dns.cancel(...) -- - - - - - - - - - - - - - - - - - - - - - cancel - return resolve(resolver.cancel, ...); +function dns.cancel(...) -- - - - - - - - - - - - - - - - - - - - - - cancel + return _resolver:cancel(...); end -function dns:socket_wrapper_set(...) -- - - - - - - - - socket_wrapper_set - return resolve(resolver.socket_wrapper_set, ...); +function dns.settimeout(...) + return _resolver:settimeout(...); end -dns._resolver = dns.resolver(); +function dns.socket_wrapper_set(...) -- - - - - - - - - socket_wrapper_set + return _resolver:socket_wrapper_set(...); +end return dns;