mirror of
https://github.com/transmission/transmission.git
synced 2026-02-15 07:26:49 +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?
|
// 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)
|
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();
|
dns.reset();
|
||||||
addr_expires_at_[ipp] = now + DnsRetryIntervalSecs;
|
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)
|
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
|
// update the addr if our lookup is past its shelf date
|
||||||
if (auto& dns = addr_pending_dns_[ipp]; !dns && addr_expires_at_[ipp] <= now)
|
if (auto& dns = addr_pending_dns_[ipp]; !dns && addr_expires_at_[ipp] <= now)
|
||||||
{
|
{
|
||||||
addr_[ipp].reset();
|
addr_[ipp].reset();
|
||||||
dns = std::async(
|
dns = std::async(
|
||||||
std::launch::async,
|
std::launch::async,
|
||||||
[this](tr_address_type ip_protocol) { return lookup(ip_protocol); },
|
[this, ipp_enum] { return mediator_.dns_lookup(ipp_enum, host_lookup, port.host(), log_name()); });
|
||||||
static_cast<tr_address_type>(ipp));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const ipp_enum = static_cast<tr_address_type>(ipp);
|
|
||||||
auto& conn_at = connecting_at[ipp];
|
auto& conn_at = connecting_at[ipp];
|
||||||
logtrace(
|
logtrace(
|
||||||
log_name(),
|
log_name(),
|
||||||
@@ -523,51 +527,6 @@ private:
|
|||||||
return std::ranges::any_of(addr_, [](auto const& o) { return !!o; });
|
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)
|
void fail_all(bool did_connect, bool did_timeout, std::string_view errmsg)
|
||||||
{
|
{
|
||||||
for (auto& req : scrapes)
|
for (auto& req : scrapes)
|
||||||
@@ -702,7 +661,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
Mediator& mediator_;
|
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<MaybeSockaddr, NUM_TR_AF_INET_TYPES> addr_ = {};
|
||||||
std::array<time_t, NUM_TR_AF_INET_TYPES> addr_expires_at_ = {};
|
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);
|
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 ~Mediator() noexcept = default;
|
||||||
virtual void sendto(void const* buf, size_t buflen, sockaddr const* addr, socklen_t addrlen) = 0;
|
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_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;
|
virtual ~tr_announcer_udp() noexcept = default;
|
||||||
|
|||||||
@@ -84,6 +84,56 @@ protected:
|
|||||||
return {};
|
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
|
struct Sent
|
||||||
{
|
{
|
||||||
Sent() = default;
|
Sent() = default;
|
||||||
|
|||||||
Reference in New Issue
Block a user