Comparison

net/resolvers/basic.lua @ 12802:4a8740e01813

Merge 0.12->trunk
author Kim Alvefur <zash@zash.se>
date Mon, 12 Dec 2022 07:10:54 +0100
parent 12601:72f7bb3f30d3
child 12815:2d134201dc55
comparison
equal deleted inserted replaced
12801:ebd6b4d8bf04 12802:4a8740e01813
1 local adns = require "net.adns"; 1 local adns = require "net.adns";
2 local inet_pton = require "util.net".pton; 2 local inet_pton = require "util.net".pton;
3 local inet_ntop = require "util.net".ntop; 3 local inet_ntop = require "util.net".ntop;
4 local idna_to_ascii = require "util.encodings".idna.to_ascii; 4 local idna_to_ascii = require "util.encodings".idna.to_ascii;
5 local unpack = table.unpack or unpack; -- luacheck: ignore 113 5 local promise = require "util.promise";
6 local t_move = require "util.table".move;
6 7
7 local methods = {}; 8 local methods = {};
8 local resolver_mt = { __index = methods }; 9 local resolver_mt = { __index = methods };
9 10
10 -- FIXME RFC 6724 11 -- FIXME RFC 6724
12
13 local function do_dns_lookup(self, dns_resolver, record_type, name, allow_insecure)
14 return promise.new(function (resolve, reject)
15 local ipv = (record_type == "A" and "4") or (record_type == "AAAA" and "6") or nil;
16 if ipv and self.extra["use_ipv"..ipv] == false then
17 return reject(("IPv%s disabled - %s lookup skipped"):format(ipv, record_type));
18 elseif record_type == "TLSA" and self.extra.use_dane ~= true then
19 return reject("DANE disabled - TLSA lookup skipped");
20 end
21 dns_resolver:lookup(function (answer, err)
22 if not answer then
23 return reject(err);
24 elseif answer.bogus then
25 return reject(("Validation error in %s lookup"):format(record_type));
26 elseif not (answer.secure or allow_insecure) then
27 return reject(("Insecure response in %s lookup"):format(record_type));
28 elseif answer.status and #answer == 0 then
29 return reject(("%s in %s lookup"):format(answer.status, record_type));
30 end
31
32 local targets = { secure = answer.secure };
33 for _, record in ipairs(answer) do
34 if ipv then
35 table.insert(targets, { self.conn_type..ipv, record[record_type:lower()], self.port, self.extra });
36 else
37 table.insert(targets, record[record_type:lower()]);
38 end
39 end
40 return resolve(targets);
41 end, name, record_type, "IN");
42 end);
43 end
44
45 local function merge_targets(ipv4_targets, ipv6_targets)
46 local result = { secure = ipv4_targets.secure and ipv6_targets.secure };
47 local common_length = math.min(#ipv4_targets, #ipv6_targets);
48 for i = 1, common_length do
49 table.insert(result, ipv6_targets[i]);
50 table.insert(result, ipv4_targets[i]);
51 end
52 if common_length < #ipv4_targets then
53 t_move(ipv4_targets, common_length+1, #ipv4_targets, common_length+1, result);
54 elseif common_length < #ipv6_targets then
55 t_move(ipv6_targets, common_length+1, #ipv6_targets, common_length+1, result);
56 end
57 return result;
58 end
11 59
12 -- Find the next target to connect to, and 60 -- Find the next target to connect to, and
13 -- pass it to cb() 61 -- pass it to cb()
14 function methods:next(cb) 62 function methods:next(cb)
15 if self.targets then 63 if self.targets then
16 if #self.targets == 0 then 64 if #self.targets == 0 then
17 cb(nil); 65 cb(nil);
18 return; 66 return;
19 end 67 end
20 local next_target = table.remove(self.targets, 1); 68 local next_target = table.remove(self.targets, 1);
21 cb(unpack(next_target, 1, 4)); 69 cb(next_target[1], next_target[2], next_target[3], next_target[4], not not self.targets[1]);
22 return; 70 return;
23 end 71 end
24 72
25 if not self.hostname then 73 if not self.hostname then
26 self.last_error = "hostname failed IDNA"; 74 self.last_error = "hostname failed IDNA";
27 cb(nil); 75 cb(nil);
28 return; 76 return;
29 end 77 end
30 78
31 local secure = true; 79 -- Resolve DNS to target list
32 local tlsa = {}; 80 local dns_resolver = adns.resolver();
33 local targets = {}; 81
34 local n = 3; 82 local dns_lookups = {
35 local function ready() 83 ipv4 = do_dns_lookup(self, dns_resolver, "A", self.hostname, true);
36 n = n - 1; 84 ipv6 = do_dns_lookup(self, dns_resolver, "AAAA", self.hostname, true);
37 if n > 0 then return; end 85 tlsa = do_dns_lookup(self, dns_resolver, "TLSA", ("_%d._%s.%s"):format(self.port, self.conn_type, self.hostname));
38 self.targets = targets; 86 };
87
88 promise.all_settled(dns_lookups):next(function (dns_results)
89 -- Combine targets, assign to self.targets, self:next(cb)
90 local have_ipv4 = dns_results.ipv4.status == "fulfilled";
91 local have_ipv6 = dns_results.ipv6.status == "fulfilled";
92
93 if have_ipv4 and have_ipv6 then
94 self.targets = merge_targets(dns_results.ipv4.value, dns_results.ipv6.value);
95 elseif have_ipv4 then
96 self.targets = dns_results.ipv4.value;
97 elseif have_ipv6 then
98 self.targets = dns_results.ipv6.value;
99 else
100 self.targets = {};
101 end
102
39 if self.extra and self.extra.use_dane then 103 if self.extra and self.extra.use_dane then
40 if secure and tlsa[1] then 104 if self.targets.secure and dns_results.tlsa.status == "fulfilled" then
41 self.extra.tlsa = tlsa; 105 self.extra.tlsa = dns_results.tlsa.value;
42 self.extra.dane_hostname = self.hostname; 106 self.extra.dane_hostname = self.hostname;
43 else 107 else
44 self.extra.tlsa = nil; 108 self.extra.tlsa = nil;
45 self.extra.dane_hostname = nil; 109 self.extra.dane_hostname = nil;
46 end 110 end
47 end 111 end
112
48 self:next(cb); 113 self:next(cb);
49 end 114 end):catch(function (err)
50 115 self.last_error = err;
51 -- Resolve DNS to target list 116 self.targets = {};
52 local dns_resolver = adns.resolver(); 117 end);
53
54 if not self.extra or self.extra.use_ipv4 ~= false then
55 dns_resolver:lookup(function (answer, err)
56 if answer then
57 secure = secure and answer.secure;
58 for _, record in ipairs(answer) do
59 table.insert(targets, { self.conn_type.."4", record.a, self.port, self.extra });
60 end
61 if answer.bogus then
62 self.last_error = "Validation error in A lookup";
63 elseif answer.status then
64 self.last_error = answer.status .. " in A lookup";
65 end
66 else
67 self.last_error = err;
68 end
69 ready();
70 end, self.hostname, "A", "IN");
71 else
72 ready();
73 end
74
75 if not self.extra or self.extra.use_ipv6 ~= false then
76 dns_resolver:lookup(function (answer, err)
77 if answer then
78 secure = secure and answer.secure;
79 for _, record in ipairs(answer) do
80 table.insert(targets, { self.conn_type.."6", record.aaaa, self.port, self.extra });
81 end
82 if answer.bogus then
83 self.last_error = "Validation error in AAAA lookup";
84 elseif answer.status then
85 self.last_error = answer.status .. " in AAAA lookup";
86 end
87 else
88 self.last_error = err;
89 end
90 ready();
91 end, self.hostname, "AAAA", "IN");
92 else
93 ready();
94 end
95
96 if self.extra and self.extra.use_dane == true then
97 dns_resolver:lookup(function (answer, err)
98 if answer then
99 secure = secure and answer.secure;
100 for _, record in ipairs(answer) do
101 table.insert(tlsa, record.tlsa);
102 end
103 if answer.bogus then
104 self.last_error = "Validation error in TLSA lookup";
105 elseif answer.status then
106 self.last_error = answer.status .. " in TLSA lookup";
107 end
108 else
109 self.last_error = err;
110 end
111 ready();
112 end, ("_%d._tcp.%s"):format(self.port, self.hostname), "TLSA", "IN");
113 else
114 ready();
115 end
116 end 118 end
117 119
118 local function new(hostname, port, conn_type, extra) 120 local function new(hostname, port, conn_type, extra)
119 local ascii_host = idna_to_ascii(hostname); 121 local ascii_host = idna_to_ascii(hostname);
120 local targets = nil; 122 local targets = nil;
135 137
136 return setmetatable({ 138 return setmetatable({
137 hostname = ascii_host; 139 hostname = ascii_host;
138 port = port; 140 port = port;
139 conn_type = conn_type; 141 conn_type = conn_type;
140 extra = extra; 142 extra = extra or {};
141 targets = targets; 143 targets = targets;
142 }, resolver_mt); 144 }, resolver_mt);
143 end 145 end
144 146
145 return { 147 return {