mirror of
https://github.com/transmission/transmission.git
synced 2026-02-14 23:19:34 +00:00
refactor: mock udp announcer DNS resolver for tests (#8232)
* refactor: move udp tracker dns lookup to mediator * test: mock udp announcer DNS resolver
This commit is contained in:
@@ -439,7 +439,11 @@ struct tau_tracker
|
||||
// do we have a DNS request that's ready?
|
||||
if (auto& dns = addr_pending_dns_[ipp]; dns && dns->wait_for(0ms) == std::future_status::ready)
|
||||
{
|
||||
addr_[ipp] = dns->get();
|
||||
// TODO(C++23): use std::optional::transform() instead
|
||||
if (auto const& addr = dns->get(); addr.has_value())
|
||||
{
|
||||
addr_[ipp] = addr->to_sockaddr();
|
||||
}
|
||||
dns.reset();
|
||||
addr_expires_at_[ipp] = now + DnsRetryIntervalSecs;
|
||||
}
|
||||
@@ -453,17 +457,17 @@ struct tau_tracker
|
||||
|
||||
for (ipp_t ipp = 0; ipp < NUM_TR_AF_INET_TYPES; ++ipp)
|
||||
{
|
||||
auto const ipp_enum = static_cast<tr_address_type>(ipp);
|
||||
|
||||
// update the addr if our lookup is past its shelf date
|
||||
if (auto& dns = addr_pending_dns_[ipp]; !dns && addr_expires_at_[ipp] <= now)
|
||||
{
|
||||
addr_[ipp].reset();
|
||||
dns = std::async(
|
||||
std::launch::async,
|
||||
[this](tr_address_type ip_protocol) { return lookup(ip_protocol); },
|
||||
static_cast<tr_address_type>(ipp));
|
||||
[this, ipp_enum] { return mediator_.dns_lookup(ipp_enum, host_lookup, port.host(), log_name()); });
|
||||
}
|
||||
|
||||
auto const ipp_enum = static_cast<tr_address_type>(ipp);
|
||||
auto& conn_at = connecting_at[ipp];
|
||||
logtrace(
|
||||
log_name(),
|
||||
@@ -523,51 +527,6 @@ private:
|
||||
return std::ranges::any_of(addr_, [](auto const& o) { return !!o; });
|
||||
}
|
||||
|
||||
[[nodiscard]] MaybeSockaddr lookup(tr_address_type ip_protocol)
|
||||
{
|
||||
auto szport = std::array<char, 16>{};
|
||||
*fmt::format_to(std::data(szport), "{:d}", port.host()) = '\0';
|
||||
|
||||
auto hints = addrinfo{};
|
||||
hints.ai_family = tr_ip_protocol_to_af(ip_protocol);
|
||||
hints.ai_protocol = IPPROTO_UDP;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
|
||||
addrinfo* info = nullptr;
|
||||
auto const szhost = tr_urlbuf{ host_lookup };
|
||||
if (int const rc = getaddrinfo(szhost.c_str(), std::data(szport), &hints, &info); rc != 0)
|
||||
{
|
||||
logwarn(
|
||||
log_name(),
|
||||
fmt::format(
|
||||
fmt::runtime(_("Couldn't look up '{address}:{port}' in {ip_protocol}: {error} ({error_code})")),
|
||||
fmt::arg("address", host),
|
||||
fmt::arg("port", port.host()),
|
||||
fmt::arg("ip_protocol", tr_ip_protocol_to_sv(ip_protocol)),
|
||||
fmt::arg("error", gai_strerror(rc)),
|
||||
fmt::arg("error_code", static_cast<int>(rc))));
|
||||
return {};
|
||||
}
|
||||
auto const info_uniq = std::unique_ptr<addrinfo, decltype(&freeaddrinfo)>{ info, freeaddrinfo };
|
||||
|
||||
// N.B. getaddrinfo() will return IPv4-mapped addresses by default on macOS
|
||||
auto socket_address = tr_socket_address::from_sockaddr(info->ai_addr);
|
||||
if (!socket_address || socket_address->address().is_ipv6_ipv4_mapped())
|
||||
{
|
||||
logdbg(
|
||||
log_name(),
|
||||
fmt::format(
|
||||
"Couldn't look up '{address}:{port}' in {ip_protocol}: got invalid address",
|
||||
fmt::arg("address", host),
|
||||
fmt::arg("port", port.host()),
|
||||
fmt::arg("ip_protocol", tr_ip_protocol_to_sv(ip_protocol))));
|
||||
return {};
|
||||
}
|
||||
|
||||
logdbg(log_name(), fmt::format("{} DNS lookup succeeded", tr_ip_protocol_to_sv(ip_protocol)));
|
||||
return socket_address->to_sockaddr();
|
||||
}
|
||||
|
||||
void fail_all(bool did_connect, bool did_timeout, std::string_view errmsg)
|
||||
{
|
||||
for (auto& req : scrapes)
|
||||
@@ -702,7 +661,7 @@ public:
|
||||
private:
|
||||
Mediator& mediator_;
|
||||
|
||||
std::array<std::optional<std::future<MaybeSockaddr>>, NUM_TR_AF_INET_TYPES> addr_pending_dns_;
|
||||
std::array<std::optional<std::future<std::optional<tr_socket_address>>>, NUM_TR_AF_INET_TYPES> addr_pending_dns_;
|
||||
|
||||
std::array<MaybeSockaddr, NUM_TR_AF_INET_TYPES> addr_ = {};
|
||||
std::array<time_t, NUM_TR_AF_INET_TYPES> addr_expires_at_ = {};
|
||||
@@ -902,3 +861,51 @@ std::unique_ptr<tr_announcer_udp> tr_announcer_udp::create(Mediator& mediator)
|
||||
{
|
||||
return std::make_unique<tr_announcer_udp_impl>(mediator);
|
||||
}
|
||||
|
||||
std::optional<tr_socket_address> tr_announcer_udp::Mediator::dns_lookup(
|
||||
tr_address_type const ip_protocol,
|
||||
std::string_view const name,
|
||||
uint16_t const service,
|
||||
std::string_view const log_name) const
|
||||
{
|
||||
auto hints = addrinfo{};
|
||||
hints.ai_family = tr_ip_protocol_to_af(ip_protocol);
|
||||
hints.ai_protocol = IPPROTO_UDP;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
|
||||
addrinfo* info = nullptr;
|
||||
auto const szname = tr_urlbuf{ name };
|
||||
auto szservice = std::array<char, 16>{};
|
||||
*fmt::format_to(std::data(szservice), "{:d}", service) = '\0';
|
||||
if (int const rc = getaddrinfo(szname.c_str(), std::data(szservice), &hints, &info); rc != 0)
|
||||
{
|
||||
logwarn(
|
||||
log_name,
|
||||
fmt::format(
|
||||
fmt::runtime(_("Couldn't look up '{address}:{port}' in {ip_protocol}: {error} ({error_code})")),
|
||||
fmt::arg("address", name),
|
||||
fmt::arg("port", service),
|
||||
fmt::arg("ip_protocol", tr_ip_protocol_to_sv(ip_protocol)),
|
||||
fmt::arg("error", gai_strerror(rc)),
|
||||
fmt::arg("error_code", static_cast<int>(rc))));
|
||||
return {};
|
||||
}
|
||||
auto const info_uniq = std::unique_ptr<addrinfo, decltype(&freeaddrinfo)>{ info, freeaddrinfo };
|
||||
|
||||
// N.B. getaddrinfo() will return IPv4-mapped addresses by default on macOS
|
||||
auto socket_address = tr_socket_address::from_sockaddr(info->ai_addr);
|
||||
if (!socket_address || socket_address->address().is_ipv6_ipv4_mapped())
|
||||
{
|
||||
logdbg(
|
||||
log_name,
|
||||
fmt::format(
|
||||
"Couldn't look up '{address}:{port}' in {ip_protocol}: got invalid address",
|
||||
fmt::arg("address", name),
|
||||
fmt::arg("port", service),
|
||||
fmt::arg("ip_protocol", tr_ip_protocol_to_sv(ip_protocol))));
|
||||
return {};
|
||||
}
|
||||
|
||||
logdbg(log_name, fmt::format("{} DNS lookup succeeded", tr_ip_protocol_to_sv(ip_protocol)));
|
||||
return socket_address;
|
||||
}
|
||||
|
||||
@@ -141,6 +141,11 @@ public:
|
||||
virtual ~Mediator() noexcept = default;
|
||||
virtual void sendto(void const* buf, size_t buflen, sockaddr const* addr, socklen_t addrlen) = 0;
|
||||
[[nodiscard]] virtual std::optional<tr_address> announce_ip() const = 0;
|
||||
[[nodiscard]] virtual std::optional<tr_socket_address> dns_lookup(
|
||||
tr_address_type ip_protocol,
|
||||
std::string_view name,
|
||||
uint16_t service,
|
||||
std::string_view log_name) const;
|
||||
};
|
||||
|
||||
virtual ~tr_announcer_udp() noexcept = default;
|
||||
|
||||
@@ -84,6 +84,56 @@ protected:
|
||||
return {};
|
||||
}
|
||||
|
||||
// Mock DNS lookup that only resolves localhost
|
||||
[[nodiscard]] std::optional<tr_socket_address> dns_lookup(
|
||||
tr_address_type const ip_protocol,
|
||||
std::string_view const name,
|
||||
uint16_t const service,
|
||||
std::string_view /*log_name*/) const override
|
||||
{
|
||||
auto const is_localhost = name == "localhost"sv;
|
||||
auto const port = tr_port::from_host(service);
|
||||
switch (ip_protocol)
|
||||
{
|
||||
case TR_AF_INET:
|
||||
if (is_localhost)
|
||||
{
|
||||
auto const addr = tr_address::from_string("127.0.0.1"sv);
|
||||
EXPECT_TRUE(addr);
|
||||
EXPECT_TRUE(addr->is_ipv4_loopback());
|
||||
return tr_socket_address{ *addr, port };
|
||||
}
|
||||
|
||||
if (auto const addr = tr_address::from_string(name); addr && addr->is_ipv4_loopback())
|
||||
{
|
||||
return tr_socket_address{ *addr, port };
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case TR_AF_INET6:
|
||||
if (is_localhost)
|
||||
{
|
||||
auto const addr = tr_address::from_string("::1");
|
||||
EXPECT_TRUE(addr);
|
||||
EXPECT_TRUE(addr->is_ipv6_loopback());
|
||||
return tr_socket_address{ *addr, port };
|
||||
}
|
||||
|
||||
if (auto const addr = tr_address::from_string(name); addr && addr->is_ipv6_loopback())
|
||||
{
|
||||
return tr_socket_address{ *addr, port };
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
struct Sent
|
||||
{
|
||||
Sent() = default;
|
||||
|
||||
Reference in New Issue
Block a user