| 1 | -- Prosody IM |
| 2 | -- Copyright (C) 2008-2010 Matthew Wild |
| 3 | -- Copyright (C) 2008-2010 Waqas Hussain |
| 4 | -- |
| 5 | -- This project is MIT/X11 licensed. Please see the |
| 6 | -- COPYING file in the source package for more information. |
| 7 | -- |
| 8 | |
| 9 | local server = require "net.server"; |
| 10 | local dns = require "net.dns"; |
| 11 | |
| 12 | local log = require "util.logger".init("adns"); |
| 13 | |
| 14 | local t_insert, t_remove = table.insert, table.remove; |
| 15 | local coroutine, tostring, pcall = coroutine, tostring, pcall; |
| 16 | |
| 17 | local function dummy_send(sock, data, i, j) return (j-i)+1; end |
| 18 | |
| 19 | module "adns" |
| 20 | |
| 21 | function lookup(handler, qname, qtype, qclass) |
| 22 | return coroutine.wrap(function (peek) |
| 23 | if peek then |
| 24 | log("debug", "Records for %s already cached, using those...", qname); |
| 25 | handler(peek); |
| 26 | return; |
| 27 | end |
| 28 | log("debug", "Records for %s not in cache, sending query (%s)...", qname, tostring(coroutine.running())); |
| 29 | local ok, err = dns.query(qname, qtype, qclass); |
| 30 | if ok then |
| 31 | coroutine.yield({ qclass or "IN", qtype or "A", qname, coroutine.running()}); -- Wait for reply |
| 32 | log("debug", "Reply for %s (%s)", qname, tostring(coroutine.running())); |
| 33 | end |
| 34 | if ok then |
| 35 | ok, err = pcall(handler, dns.peek(qname, qtype, qclass)); |
| 36 | else |
| 37 | log("error", "Error sending DNS query: %s", err); |
| 38 | ok, err = pcall(handler, nil, err); |
| 39 | end |
| 40 | if not ok then |
| 41 | log("error", "Error in DNS response handler: %s", tostring(err)); |
| 42 | end |
| 43 | end)(dns.peek(qname, qtype, qclass)); |
| 44 | end |
| 45 | |
| 46 | function cancel(handle, call_handler, reason) |
| 47 | log("warn", "Cancelling DNS lookup for %s", tostring(handle[3])); |
| 48 | dns.cancel(handle[1], handle[2], handle[3], handle[4], call_handler); |
| 49 | end |
| 50 | |
| 51 | function new_async_socket(sock, resolver) |
| 52 | local peername = "<unknown>"; |
| 53 | local listener = {}; |
| 54 | local handler = {}; |
| 55 | function listener.onincoming(conn, data) |
| 56 | if data then |
| 57 | dns.feed(handler, data); |
| 58 | end |
| 59 | end |
| 60 | function listener.ondisconnect(conn, err) |
| 61 | if err then |
| 62 | log("warn", "DNS socket for %s disconnected: %s", peername, err); |
| 63 | local servers = resolver.server; |
| 64 | if resolver.socketset[conn] == resolver.best_server and resolver.best_server == #servers then |
| 65 | log("error", "Exhausted all %d configured DNS servers, next lookup will try %s again", #servers, servers[1]); |
| 66 | end |
| 67 | |
| 68 | resolver:servfail(conn); -- Let the magic commence |
| 69 | end |
| 70 | end |
| 71 | handler = server.wrapclient(sock, "dns", 53, listener); |
| 72 | if not handler then |
| 73 | log("warn", "handler is nil"); |
| 74 | end |
| 75 | |
| 76 | handler.settimeout = function () end |
| 77 | handler.setsockname = function (_, ...) return sock:setsockname(...); end |
| 78 | handler.setpeername = function (_, ...) peername = (...); local ret = sock:setpeername(...); _:set_send(dummy_send); return ret; end |
| 79 | handler.connect = function (_, ...) return sock:connect(...) end |
| 80 | --handler.send = function (_, data) _:write(data); return _.sendbuffer and _.sendbuffer(); end |
| 81 | handler.send = function (_, data) return sock:send(data); end |
| 82 | return handler; |
| 83 | end |
| 84 | |
| 85 | dns.socket_wrapper_set(new_async_socket); |
| 86 | |
| 87 | return _M; |