Mercurial > prosody-hg
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 |
| parents | 7a68d5828f3b |
| children | 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 { |
