Software /
code /
prosody
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 { |