Comparison

net/resolvers/basic.lua @ 12408:acfc51b9530c

net.resolvers.basic: Refactor to remove code duplication ...and prepare for Happy Eyeballs
author Matthew Wild <mwild1@gmail.com>
date Fri, 18 Mar 2022 16:09:22 +0000
parent 12129:7a68d5828f3b
child 12409:9f0baf15e792
comparison
equal deleted inserted replaced
12407:b6b01724e04f 12408:acfc51b9530c
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)
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 answer.status and #answer == 0 then
27 return reject(("%s in %s lookup"):format(answer.status, record_type));
28 end
29
30 local targets = { secure = answer.secure };
31 for _, record in ipairs(answer) do
32 if ipv then
33 table.insert(targets, { self.conn_type..ipv, record[record_type:lower()], self.port, self.extra });
34 else
35 table.insert(targets, record[record_type:lower()]);
36 end
37 end
38 return resolve(targets);
39 end, name, record_type, "IN");
40 end);
41 end
42
43 local function merge_targets(ipv4_targets, ipv6_targets)
44 local result = { secure = ipv4_targets.secure and ipv6_targets.secure };
45 t_move(ipv6_targets, 1, #ipv6_targets, 1, result);
46 t_move(ipv4_targets, 1, #ipv4_targets, #result+1, result);
47 return result;
48 end
11 49
12 -- Find the next target to connect to, and 50 -- Find the next target to connect to, and
13 -- pass it to cb() 51 -- pass it to cb()
14 function methods:next(cb) 52 function methods:next(cb)
15 if self.targets then 53 if self.targets then
16 if #self.targets == 0 then 54 if #self.targets == 0 then
17 cb(nil); 55 cb(nil);
18 return; 56 return;
19 end 57 end
20 local next_target = table.remove(self.targets, 1); 58 local next_target = table.remove(self.targets, 1);
21 cb(unpack(next_target, 1, 4)); 59 cb(next_target[1], next_target[2], next_target[3], next_target[4]);
22 return; 60 return;
23 end 61 end
24 62
25 if not self.hostname then 63 if not self.hostname then
26 self.last_error = "hostname failed IDNA"; 64 self.last_error = "hostname failed IDNA";
27 cb(nil); 65 cb(nil);
28 return; 66 return;
29 end 67 end
30 68
31 local secure = true; 69 -- Resolve DNS to target list
32 local tlsa = {}; 70 local dns_resolver = adns.resolver();
33 local targets = {}; 71
34 local n = 3; 72 local dns_lookups = {
35 local function ready() 73 ipv4 = do_dns_lookup(self, dns_resolver, "A", self.hostname);
36 n = n - 1; 74 ipv6 = do_dns_lookup(self, dns_resolver, "AAAA", self.hostname);
37 if n > 0 then return; end 75 tlsa = do_dns_lookup(self, dns_resolver, "TLSA", ("_%d._%s.%s"):format(self.port, self.conntype, self.hostname));
38 self.targets = targets; 76 };
77
78 promise.all_settled(dns_lookups):next(function (dns_results)
79 -- Combine targets, assign to self.targets, self:next(cb)
80 local have_ipv4 = dns_results.ipv4.status == "fulfilled";
81 local have_ipv6 = dns_results.ipv6.status == "fulfilled";
82
83 if have_ipv4 and have_ipv6 then
84 self.targets = merge_targets(dns_results.ipv4.value, dns_results.ipv6.value);
85 elseif have_ipv4 then
86 self.targets = dns_results.ipv4.value;
87 elseif have_ipv6 then
88 self.targets = dns_results.ipv6.value;
89 else
90 self.targets = {};
91 end
92
39 if self.extra and self.extra.use_dane then 93 if self.extra and self.extra.use_dane then
40 if secure and tlsa[1] then 94 if self.targets.secure and dns_results.tlsa.status == "fulfilled" then
41 self.extra.tlsa = tlsa; 95 self.extra.tlsa = dns_results.tlsa.value;
42 self.extra.dane_hostname = self.hostname; 96 self.extra.dane_hostname = self.hostname;
43 else 97 else
44 self.extra.tlsa = nil; 98 self.extra.tlsa = nil;
45 self.extra.dane_hostname = nil; 99 self.extra.dane_hostname = nil;
46 end 100 end
47 end 101 end
102
48 self:next(cb); 103 self:next(cb);
49 end 104 end):catch(function (err)
50 105 self.last_error = err;
51 -- Resolve DNS to target list 106 self.targets = {};
52 local dns_resolver = adns.resolver(); 107 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 108 end
117 109
118 local function new(hostname, port, conn_type, extra) 110 local function new(hostname, port, conn_type, extra)
119 local ascii_host = idna_to_ascii(hostname); 111 local ascii_host = idna_to_ascii(hostname);
120 local targets = nil; 112 local targets = nil;
135 127
136 return setmetatable({ 128 return setmetatable({
137 hostname = ascii_host; 129 hostname = ascii_host;
138 port = port; 130 port = port;
139 conn_type = conn_type; 131 conn_type = conn_type;
140 extra = extra; 132 extra = extra or {};
141 targets = targets; 133 targets = targets;
142 }, resolver_mt); 134 }, resolver_mt);
143 end 135 end
144 136
145 return { 137 return {