comparison net/unbound.lua @ 11200:bf8f2da84007

Merge 0.11->trunk
author Kim Alvefur <zash@zash.se>
date Thu, 05 Nov 2020 22:31:25 +0100
parents 23ae55cbbeaf
children a1aecd8cf7ee
comparison
equal deleted inserted replaced
11199:6c7c50a4de32 11200:bf8f2da84007
1 -- libunbound based net.adns replacement for Prosody IM
2 -- Copyright (C) 2013-2015 Kim Alvefur
3 --
4 -- This file is MIT licensed.
5 --
6 -- luacheck: ignore prosody
7
8 local setmetatable = setmetatable;
9 local tostring = tostring;
10 local t_concat = table.concat;
11 local s_format = string.format;
12 local s_lower = string.lower;
13 local s_upper = string.upper;
14 local noop = function() end;
15
16 local log = require "util.logger".init("unbound");
17 local net_server = require "net.server";
18 local libunbound = require"lunbound";
19 local promise = require"util.promise";
20
21 local gettime = require"socket".gettime;
22 local dns_utils = require"util.dns";
23 local classes, types, errors = dns_utils.classes, dns_utils.types, dns_utils.errors;
24 local parsers = dns_utils.parsers;
25
26 local function add_defaults(conf)
27 if conf then
28 for option, default in pairs(libunbound.config) do
29 if conf[option] == nil then
30 conf[option] = default;
31 end
32 end
33 end
34 return conf;
35 end
36
37 local unbound_config;
38 if prosody then
39 local config = require"core.configmanager";
40 unbound_config = add_defaults(config.get("*", "unbound"));
41 prosody.events.add_handler("config-reloaded", function()
42 unbound_config = add_defaults(config.get("*", "unbound"));
43 end);
44 end
45 -- Note: libunbound will default to using root hints if resolvconf is unset
46
47 local function connect_server(unbound, server)
48 return server.watchfd(unbound, function ()
49 unbound:process()
50 end);
51 end
52
53 local unbound = libunbound.new(unbound_config);
54
55 local server_conn = connect_server(unbound, net_server);
56
57 local answer_mt = {
58 __tostring = function(self)
59 if self._string then return self._string end
60 local h = s_format("Status: %s", errors[self.status]);
61 if self.secure then
62 h = h .. ", Secure";
63 elseif self.bogus then
64 h = h .. s_format(", Bogus: %s", self.bogus);
65 end
66 local t = { h };
67 for i = 1, #self do
68 t[i+1]=self.qname.."\t"..classes[self.qclass].."\t"..types[self.qtype].."\t"..tostring(self[i]);
69 end
70 local _string = t_concat(t, "\n");
71 self._string = _string;
72 return _string;
73 end;
74 };
75
76 local waiting_queries = {};
77
78 local function prep_answer(a)
79 if not a then return end
80 local status = errors[a.rcode];
81 local qclass = classes[a.qclass];
82 local qtype = types[a.qtype];
83 a.status, a.class, a.type = status, qclass, qtype;
84
85 local t = s_lower(qtype);
86 local rr_mt = { __index = a, __tostring = function(self) return tostring(self[t]) end };
87 local parser = parsers[qtype];
88 for i = 1, #a do
89 if a.bogus then
90 -- Discard bogus data
91 a[i] = nil;
92 else
93 a[i] = setmetatable({[t] = parser(a[i])}, rr_mt);
94 end
95 end
96 return setmetatable(a, answer_mt);
97 end
98
99 local function lookup(callback, qname, qtype, qclass)
100 qtype = qtype and s_upper(qtype) or "A";
101 qclass = qclass and s_upper(qclass) or "IN";
102 local ntype, nclass = types[qtype], classes[qclass];
103 local startedat = gettime();
104 local ret;
105 local function callback_wrapper(a, err)
106 local gotdataat = gettime();
107 waiting_queries[ret] = nil;
108 if a then
109 prep_answer(a);
110 log("debug", "Results for %s %s %s: %s (%s, %f sec)", qname, qclass, qtype, a.rcode == 0 and (#a .. " items") or a.status,
111 a.secure and "Secure" or a.bogus or "Insecure", gotdataat - startedat); -- Insecure as in unsigned
112 else
113 log("error", "Results for %s %s %s: %s", qname, qclass, qtype, tostring(err));
114 end
115 local ok, cerr = pcall(callback, a, err);
116 if not ok then log("error", "Error in callback: %s", cerr); end
117 end
118 log("debug", "Resolve %s %s %s", qname, qclass, qtype);
119 local err;
120 ret, err = unbound:resolve_async(callback_wrapper, qname, ntype, nclass);
121 if ret then
122 waiting_queries[ret] = callback;
123 else
124 log("warn", err);
125 end
126 return ret, err;
127 end
128
129 local function lookup_sync(qname, qtype, qclass)
130 qtype = qtype and s_upper(qtype) or "A";
131 qclass = qclass and s_upper(qclass) or "IN";
132 local ntype, nclass = types[qtype], classes[qclass];
133 local a, err = unbound:resolve(qname, ntype, nclass);
134 if not a then return a, err; end
135 return prep_answer(a);
136 end
137
138 local function cancel(id)
139 local cb = waiting_queries[id];
140 unbound:cancel(id);
141 if cb then
142 cb(nil, "canceled");
143 waiting_queries[id] = nil;
144 end
145 return true;
146 end
147
148 -- Reinitiate libunbound context, drops cache
149 local function purge()
150 for id in pairs(waiting_queries) do cancel(id); end
151 if server_conn then server_conn:close(); end
152 unbound = libunbound.new(unbound_config);
153 server_conn = connect_server(unbound, net_server);
154 return true;
155 end
156
157 local function not_implemented()
158 error "not implemented";
159 end
160 -- Public API
161 local _M = {
162 lookup = lookup;
163 cancel = cancel;
164 new_async_socket = not_implemented;
165 dns = {
166 lookup = lookup_sync;
167 cancel = cancel;
168 cache = noop;
169 socket_wrapper_set = noop;
170 settimeout = noop;
171 query = noop;
172 purge = purge;
173 random = noop;
174 peek = noop;
175
176 types = types;
177 classes = classes;
178 };
179 };
180
181 local function lookup_promise(_, qname, qtype, qclass)
182 return promise.new(function (resolve, reject)
183 local function callback(answer, err)
184 if err then
185 return reject(err);
186 else
187 return resolve(answer);
188 end
189 end
190 local ret, err = lookup(callback, qname, qtype, qclass)
191 if not ret then reject(err); end
192 end);
193 end
194
195 local wrapper = {
196 lookup = function (_, callback, qname, qtype, qclass)
197 return lookup(callback, qname, qtype, qclass)
198 end;
199 lookup_promise = lookup_promise;
200 _resolver = {
201 settimeout = function () end;
202 closeall = function () end;
203 };
204 }
205
206 function _M.resolver() return wrapper; end
207
208 return _M;