refactor: replace tr_boundinfo with tr_session::BoundSocket (#4103)

This commit is contained in:
Charles Kerr 2022-11-06 10:35:48 -06:00 committed by GitHub
parent bf17a3baf1
commit 36edd516aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 222 additions and 205 deletions

View File

@ -372,14 +372,16 @@ void Blocklist::ensureLoaded() const
{
// bad binary file; try to rebuild it
in.close();
auto src_file = std::string_view{ bin_file_ };
src_file.remove_suffix(std::size(BinFileSuffix));
rules_ = parseFile(src_file);
if (!std::empty(rules_))
auto const sz_src_file = std::string{ std::data(bin_file_), std::size(bin_file_) - std::size(BinFileSuffix) };
if (tr_sys_path_exists(sz_src_file))
{
tr_logAddInfo(_("Rewriting old blocklist file format to new format"));
tr_sys_path_remove(bin_file_);
save(bin_file_, std::data(rules_), std::size(rules_));
rules_ = parseFile(sz_src_file);
if (!std::empty(rules_))
{
tr_logAddInfo(_("Rewriting old blocklist file format to new format"));
tr_sys_path_remove(bin_file_);
save(bin_file_, std::data(rules_), std::size(rules_));
}
}
return;
}

View File

@ -43,10 +43,6 @@
#define IN_MULTICAST(a) (((a)&0xf0000000) == 0xe0000000)
#endif
tr_address const tr_in6addr_any = { TR_AF_INET6, { IN6ADDR_ANY_INIT } };
tr_address const tr_inaddr_any = { TR_AF_INET, { { { { INADDR_ANY } } } } };
std::string tr_net_strerror(int err)
{
#ifdef _WIN32
@ -392,13 +388,13 @@ void tr_netClosePeerSocket(tr_session* session, tr_peer_socket socket)
}
}
static tr_socket_t tr_netBindTCPImpl(tr_address const* addr, tr_port port, bool suppress_msgs, int* err_out)
static tr_socket_t tr_netBindTCPImpl(tr_address const& addr, tr_port port, bool suppress_msgs, int* err_out)
{
TR_ASSERT(tr_address_is_valid(addr));
TR_ASSERT(tr_address_is_valid(&addr));
static auto constexpr Domains = std::array<int, NUM_TR_AF_INET_TYPES>{ AF_INET, AF_INET6 };
auto const fd = socket(Domains[addr->type], SOCK_STREAM, 0);
auto const fd = socket(Domains[addr.type], SOCK_STREAM, 0);
if (fd == TR_BAD_SOCKET)
{
*err_out = sockerrno;
@ -418,7 +414,7 @@ static tr_socket_t tr_netBindTCPImpl(tr_address const* addr, tr_port port, bool
#ifdef IPV6_V6ONLY
if (addr->isIPv6() &&
if (addr.isIPv6() &&
(setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char const*>(&optval), sizeof(optval)) == -1) &&
(sockerrno != ENOPROTOOPT)) // if the kernel doesn't support it, ignore it
{
@ -429,7 +425,7 @@ static tr_socket_t tr_netBindTCPImpl(tr_address const* addr, tr_port port, bool
#endif
auto const [sock, addrlen] = addr->toSockaddr(port);
auto const [sock, addrlen] = addr.toSockaddr(port);
if (bind(fd, (struct sockaddr*)&sock, addrlen) == -1)
{
@ -441,7 +437,7 @@ static tr_socket_t tr_netBindTCPImpl(tr_address const* addr, tr_port port, bool
err == EADDRINUSE ?
_("Couldn't bind port {port} on {address}: {error} ({error_code}) -- Is another copy of Transmission already running?") :
_("Couldn't bind port {port} on {address}: {error} ({error_code})"),
fmt::arg("address", addr->readable()),
fmt::arg("address", addr.readable()),
fmt::arg("port", port.host()),
fmt::arg("error", tr_net_strerror(err)),
fmt::arg("error_code", err)));
@ -454,7 +450,7 @@ static tr_socket_t tr_netBindTCPImpl(tr_address const* addr, tr_port port, bool
if (!suppress_msgs)
{
tr_logAddDebug(fmt::format(FMT_STRING("Bound socket {:d} to port {:d} on {:s}"), fd, port.host(), addr->readable()));
tr_logAddDebug(fmt::format(FMT_STRING("Bound socket {:d} to port {:d} on {:s}"), fd, port.host(), addr.readable()));
}
#ifdef TCP_FASTOPEN
@ -483,7 +479,7 @@ static tr_socket_t tr_netBindTCPImpl(tr_address const* addr, tr_port port, bool
return fd;
}
tr_socket_t tr_netBindTCP(tr_address const* addr, tr_port port, bool suppress_msgs)
tr_socket_t tr_netBindTCP(tr_address const& addr, tr_port port, bool suppress_msgs)
{
int unused = 0;
return tr_netBindTCPImpl(addr, port, suppress_msgs, &unused);
@ -497,7 +493,7 @@ bool tr_net_hasIPv6(tr_port port)
if (!already_done)
{
int err = 0;
auto const fd = tr_netBindTCPImpl(&tr_in6addr_any, port, true, &err);
auto const fd = tr_netBindTCPImpl(tr_address::AnyIPv4(), port, true, &err);
if (fd != TR_BAD_SOCKET || err != EAFNOSUPPORT) /* we support ipv6 */
{

View File

@ -263,10 +263,17 @@ struct tr_address
struct in6_addr addr6;
struct in_addr addr4;
} addr;
};
extern tr_address const tr_inaddr_any;
extern tr_address const tr_in6addr_any;
[[nodiscard]] static auto constexpr AnyIPv4() noexcept
{
return tr_address{ TR_AF_INET, { { { { INADDR_ANY } } } } };
}
[[nodiscard]] static auto constexpr AnyIPv6() noexcept
{
return tr_address{ TR_AF_INET6, { IN6ADDR_ANY_INIT } };
}
};
bool tr_address_is_valid_for_peers(tr_address const* addr, tr_port port);
@ -281,7 +288,7 @@ constexpr bool tr_address_is_valid(tr_address const* a)
struct tr_session;
tr_socket_t tr_netBindTCP(tr_address const* addr, tr_port port, bool suppress_msgs);
tr_socket_t tr_netBindTCP(tr_address const& addr, tr_port port, bool suppress_msgs);
[[nodiscard]] std::optional<std::tuple<tr_address, tr_port, tr_socket_t>> tr_netAccept(
tr_session* session,

View File

@ -44,7 +44,7 @@ public:
tr_port_forwarding_impl& operator=(tr_port_forwarding_impl&&) = delete;
tr_port_forwarding_impl& operator=(tr_port_forwarding_impl const&) = delete;
void portChanged() override
void localPortChanged() override
{
if (!is_enabled_)
{

View File

@ -40,6 +40,6 @@ public:
[[nodiscard]] virtual bool isEnabled() const = 0;
[[nodiscard]] virtual tr_port_forwarding_state state() const = 0;
virtual void portChanged() = 0;
virtual void localPortChanged() = 0;
virtual void setEnabled(bool enabled) = 0;
};

View File

@ -65,8 +65,6 @@ using namespace std::literals;
std::recursive_mutex tr_session::session_mutex_;
static auto constexpr DefaultBindAddressIpv4 = "0.0.0.0"sv;
static auto constexpr DefaultBindAddressIpv6 = "::"sv;
static auto constexpr SaveIntervalSecs = 360s;
static void bandwidthGroupRead(tr_session* session, std::string_view config_dir);
@ -257,7 +255,50 @@ void tr_sessionSetEncryption(tr_session* session, tr_encryption_mode mode)
****
***/
void tr_session::tr_bindinfo::close()
void tr_session::onIncomingPeerConnection(tr_socket_t fd, void* vsession)
{
auto* session = static_cast<tr_session*>(vsession);
if (auto const incoming_info = tr_netAccept(session, fd); incoming_info)
{
auto const& [addr, port, sock] = *incoming_info;
tr_logAddTrace(fmt::format("new incoming connection {} ({})", sock, addr.readable(port)));
session->addIncoming(addr, port, tr_peer_socket_tcp_create(sock));
}
}
tr_session::BoundSocket::BoundSocket(
event_base* evbase,
tr_address const& addr,
tr_port port,
IncomingCallback cb,
void* cb_data)
: cb_{ cb }
, cb_data_{ cb_data }
, socket_{ tr_netBindTCP(addr, port, false) }
{
if (socket_ == TR_BAD_SOCKET)
{
return;
}
tr_logAddInfo(
fmt::format(_("Listening to incoming peer connections on {hostport}"), fmt::arg("hostport", addr.readable(port))));
ev_ = event_new(
evbase,
socket_,
EV_READ | EV_PERSIST,
[](evutil_socket_t fd, short /*evtype*/, void* vself)
{
auto* const self = static_cast<BoundSocket*>(vself);
self->cb_(fd, self->cb_data_);
},
this);
event_add(ev_, nullptr);
}
tr_session::BoundSocket::~BoundSocket()
{
if (ev_ != nullptr)
{
@ -272,49 +313,24 @@ void tr_session::tr_bindinfo::close()
}
}
static void acceptIncomingPeer(evutil_socket_t fd, short /*what*/, void* vsession)
{
auto* session = static_cast<tr_session*>(vsession);
if (auto const incoming_info = tr_netAccept(session, fd); incoming_info)
{
auto const& [addr, port, sock] = *incoming_info;
tr_logAddTrace(fmt::format("new incoming connection {} ({})", sock, addr.readable(port)));
session->addIncoming(addr, port, tr_peer_socket_tcp_create(sock));
}
}
void tr_session::tr_bindinfo::bindAndListenForIncomingPeers(tr_session* session)
{
TR_ASSERT(session->allowsTCP());
auto const& port = session->localPeerPort();
socket_ = tr_netBindTCP(&addr_, port, false);
if (socket_ != TR_BAD_SOCKET)
{
tr_logAddInfo(
fmt::format(_("Listening to incoming peer connections on {hostport}"), fmt::arg("hostport", addr_.readable(port))));
ev_ = event_new(session->eventBase(), socket_, EV_READ | EV_PERSIST, acceptIncomingPeer, session);
event_add(ev_, nullptr);
}
}
tr_session::PublicAddressResult tr_session::publicAddress(tr_address_type type) const noexcept
{
switch (type)
if (type == TR_AF_INET)
{
case TR_AF_INET:
return { bind_ipv4_.addr_, bind_ipv4_.addr_.readable() == DefaultBindAddressIpv4 };
case TR_AF_INET6:
return { bind_ipv6_.addr_, bind_ipv6_.addr_.readable() == DefaultBindAddressIpv6 };
default:
TR_ASSERT_MSG(false, "invalid type");
return {};
static auto constexpr DefaultAddr = tr_address::AnyIPv4();
auto addr = tr_address::fromString(settings_.bind_address_ipv4).value_or(DefaultAddr);
return { addr, addr == DefaultAddr };
}
if (type == TR_AF_INET6)
{
static auto constexpr DefaultAddr = tr_address::AnyIPv6();
auto addr = tr_address::fromString(settings_.bind_address_ipv6).value_or(DefaultAddr);
return { addr, addr == DefaultAddr };
}
TR_ASSERT_MSG(false, "invalid type");
return {};
}
/***
@ -529,8 +545,6 @@ void tr_session::initImpl(init_data& data)
setSettings(client_settings, true);
this->udp_core_ = std::make_unique<tr_session::tr_udp_core>(*this, udpPort());
if (this->allowsLPD())
{
this->lpd_ = tr_lpd::create(lpd_mediator_, eventBase());
@ -548,14 +562,25 @@ static void updateBandwidth(tr_session* session, tr_direction dir);
void tr_session::setSettings(tr_variant* settings_dict, bool force)
{
TR_ASSERT(amInSessionThread());
TR_ASSERT(tr_variantIsDict(settings_dict));
auto* const settings = settings_dict;
TR_ASSERT(tr_variantIsDict(settings));
// load the session settings
auto new_settings = tr_session_settings{};
new_settings.load(settings_dict);
setSettings(std::move(new_settings), force);
// update the `settings_` field
auto const old_settings = settings_;
auto& new_settings = settings_;
new_settings.load(settings);
// delegate loading out the other settings
alt_speeds_.load(settings_dict);
rpc_server_->load(settings_dict);
}
void tr_session::setSettings(tr_session_settings settings_in, bool force)
{
auto const lock = unique_lock();
std::swap(settings_, settings_in);
auto const& new_settings = settings_;
auto const& old_settings = settings_in;
// the rest of the func is session_ responding to settings changes
@ -581,51 +606,65 @@ void tr_session::setSettings(tr_variant* settings_dict, bool force)
setDefaultTrackers(val);
}
if (auto val = bool{}; tr_variantDictFindBool(settings, TR_KEY_dht_enabled, &val))
{
tr_sessionSetDHTEnabled(this, val);
}
if (auto const& val = new_settings.utp_enabled; force || val != old_settings.utp_enabled)
{
tr_sessionSetUTPEnabled(this, val);
}
if (auto const& val = new_settings.lpd_enabled; force || val != old_settings.lpd_enabled)
{
tr_sessionSetLPDEnabled(this, val);
}
useBlocklist(new_settings.blocklist_enabled);
/// bound addresses, peer port, port forwarding
auto local_peer_port = force && settings_.peer_port_random_on_start ? randomPort() : new_settings.peer_port;
bool port_changed = false;
if (force || local_peer_port_ != local_peer_port)
{
auto port_needs_update = force;
local_peer_port_ = local_peer_port;
advertised_peer_port_ = local_peer_port;
port_changed = true;
}
if (auto const& val = new_settings.bind_address_ipv4; force || val != old_settings.bind_address_ipv4)
if (new_settings.tcp_enabled)
{
if (auto const& val = new_settings.bind_address_ipv4; force || port_changed || val != old_settings.bind_address_ipv4)
{
if (auto const addr = tr_address::fromString(val); addr && addr->isIPv4())
{
this->bind_ipv4_ = tr_bindinfo{ *addr };
port_needs_update |= true;
}
auto const [addr, is_default] = publicAddress(TR_AF_INET);
bound_ipv4_.emplace(eventBase(), addr, local_peer_port_, &tr_session::onIncomingPeerConnection, this);
}
if (auto const& val = new_settings.bind_address_ipv6; force || val != old_settings.bind_address_ipv6)
if (auto const& val = new_settings.bind_address_ipv6; force || port_changed || val != old_settings.bind_address_ipv6)
{
if (auto const addr = tr_address::fromString(val); addr && addr->isIPv6())
{
this->bind_ipv6_ = tr_bindinfo{ *addr };
port_needs_update |= true;
}
auto const [addr, is_default] = publicAddress(TR_AF_INET6);
bound_ipv6_.emplace(eventBase(), addr, local_peer_port_, &tr_session::onIncomingPeerConnection, this);
}
}
else
{
bound_ipv4_.reset();
bound_ipv6_.reset();
}
port_needs_update |= (new_settings.port_forwarding_enabled != old_settings.port_forwarding_enabled);
if (port_changed)
{
port_forwarding_->localPortChanged();
}
if (port_needs_update)
bool const dht_changed = new_settings.dht_enabled != old_settings.dht_enabled;
if (!udp_core_ || force || port_changed || dht_changed)
{
udp_core_ = std::make_unique<tr_session::tr_udp_core>(*this, udpPort());
}
// Sends out announce messages with advertisedPeerPort(), so this
// section neesd be happen here after the peer port settings changes
if (auto const& val = new_settings.lpd_enabled; force || val != old_settings.lpd_enabled)
{
if (val)
{
setPeerPort(isPortRandom() ? randomPort() : new_settings.peer_port);
tr_sessionSetPortForwardingEnabled(this, new_settings.port_forwarding_enabled);
lpd_ = tr_lpd::create(lpd_mediator_, eventBase());
}
else
{
lpd_.reset();
}
}
@ -633,9 +672,6 @@ void tr_session::setSettings(tr_variant* settings_dict, bool force)
// It's a harmless call, so just call it instead of checking for settings changes
updateBandwidth(this, TR_UP);
updateBandwidth(this, TR_DOWN);
alt_speeds_.load(settings);
rpc_server_->load(settings);
}
void tr_sessionSet(tr_session* session, tr_variant* settings)
@ -740,44 +776,20 @@ bool tr_sessionIsIncompleteDirEnabled(tr_session const* session)
**** Peer Port
***/
void tr_session::setPeerPort(tr_port port_in)
{
auto const in_session_thread = [this](tr_port port)
{
auto const lock = unique_lock();
auto& private_peer_port = settings_.peer_port;
private_peer_port = port;
advertised_peer_port_ = port;
closePeerPort();
if (allowsTCP())
{
bind_ipv4_.bindAndListenForIncomingPeers(this);
if (tr_net_hasIPv6(private_peer_port))
{
bind_ipv6_.bindAndListenForIncomingPeers(this);
}
}
port_forwarding_->portChanged();
for (auto* const tor : torrents())
{
tr_torrentChangeMyPort(tor);
}
};
runInSessionThread(in_session_thread, port_in);
}
void tr_sessionSetPeerPort(tr_session* session, uint16_t hport)
{
TR_ASSERT(session != nullptr);
session->setPeerPort(tr_port::fromHost(hport));
if (auto const port = tr_port::fromHost(hport); port != session->localPeerPort())
{
session->runInSessionThread(
[session, port]()
{
auto settings = session->settings_;
settings.peer_port = port;
session->setSettings(std::move(settings), false);
});
}
}
uint16_t tr_sessionGetPeerPort(tr_session const* session)
@ -1175,7 +1187,8 @@ void tr_session::closeImplPart1()
rpc_server_.reset();
lpd_.reset();
port_forwarding_.reset();
closePeerPort();
bound_ipv6_.reset();
bound_ipv4_.reset();
// tell other items to start shutting down
udp_core_->startShutdown();
@ -1389,18 +1402,16 @@ void tr_sessionSetDHTEnabled(tr_session* session, bool enabled)
{
TR_ASSERT(session != nullptr);
if (enabled == session->allowsDHT())
if (enabled != session->allowsDHT())
{
return;
session->runInSessionThread(
[session, enabled]()
{
auto settings = session->settings_;
settings.dht_enabled = enabled;
session->setSettings(std::move(settings), false);
});
}
session->runInSessionThread(
[session, enabled]()
{
session->udp_core_.reset();
session->settings_.dht_enabled = enabled;
session->udp_core_ = std::make_unique<tr_session::tr_udp_core>(*session, session->udpPort());
});
}
/***
@ -1439,21 +1450,16 @@ void tr_sessionSetLPDEnabled(tr_session* session, bool enabled)
{
TR_ASSERT(session != nullptr);
if (enabled == session->allowsLPD())
if (enabled != session->allowsLPD())
{
return;
}
session->runInSessionThread(
[session, enabled]()
{
session->lpd_.reset();
session->settings_.lpd_enabled = enabled;
if (enabled)
session->runInSessionThread(
[session, enabled]()
{
session->lpd_ = tr_lpd::create(session->lpd_mediator_, session->eventBase());
}
});
auto settings = session->settings_;
settings.lpd_enabled = enabled;
session->setSettings(std::move(settings), false);
});
}
}
bool tr_sessionIsLPDEnabled(tr_session const* session)

View File

@ -77,25 +77,22 @@ class SessionTest;
struct tr_session
{
private:
struct tr_bindinfo
class BoundSocket
{
explicit tr_bindinfo(tr_address addr)
: addr_{ std::move(addr) }
{
}
public:
using IncomingCallback = void (*)(tr_socket_t, void*);
BoundSocket(struct event_base*, tr_address const& addr, tr_port port, IncomingCallback cb, void* cb_data);
BoundSocket(BoundSocket&&) = delete;
BoundSocket(BoundSocket const&) = delete;
BoundSocket operator=(BoundSocket&&) = delete;
BoundSocket operator=(BoundSocket const&) = delete;
~BoundSocket();
void bindAndListenForIncomingPeers(tr_session* session);
void close();
[[nodiscard]] auto readable() const
{
return addr_.readable();
}
tr_address addr_;
struct event* ev_ = nullptr;
private:
IncomingCallback cb_;
void* cb_data_;
tr_socket_t socket_ = TR_BAD_SOCKET;
struct event* ev_ = nullptr;
};
class AltSpeedMediator final : public tr_session_alt_speeds::Mediator
@ -155,7 +152,7 @@ private:
[[nodiscard]] tr_address incomingPeerAddress() const override
{
return session_.bind_ipv4_.addr_;
return session_.publicAddress(TR_AF_INET).address;
}
[[nodiscard]] tr_port localPeerPort() const override
@ -625,7 +622,15 @@ public:
// that Transmission is running on.
[[nodiscard]] constexpr tr_port localPeerPort() const noexcept
{
return settings_.peer_port;
return local_peer_port_;
}
[[nodiscard]] constexpr tr_port udpPort() const noexcept
{
// Always use the same port number that's used for incoming TCP connections.
// This simplifies port forwarding and reduces the chance of confusion,
// since incoming UDP and TCP connections will use the same port number
return localPeerPort();
}
// The incoming peer port that's been opened on the public-facing
@ -637,14 +642,6 @@ public:
return advertised_peer_port_;
}
[[nodiscard]] constexpr tr_port udpPort() const noexcept
{
// Always use the same port number that's used for incoming TCP connections.
// This simplifies port forwarding and reduces the chance of confusion,
// since incoming UDP and TCP connections will use the same port number
return advertisedPeerPort();
}
[[nodiscard]] constexpr auto queueEnabled(tr_direction dir) const noexcept
{
return dir == TR_DOWN ? settings_.download_queue_enabled : settings_.seed_queue_enabled;
@ -888,21 +885,18 @@ private:
void setPeerPort(tr_port port);
void closePeerPort()
{
bind_ipv4_.close();
bind_ipv6_.close();
}
struct init_data;
void initImpl(init_data&);
void setSettings(tr_variant* settings_dict, bool force);
void setSettings(tr_session_settings settings, bool force);
void closeImplPart1();
void closeImplPart2();
void onNowTimer();
static void onIncomingPeerConnection(tr_socket_t fd, void* vsession);
friend class libtransmission::test::SessionTest;
friend struct tr_bindinfo;
@ -1028,6 +1022,12 @@ private:
tr_altSpeedFunc alt_speed_active_changed_func_ = nullptr;
void* alt_speed_active_changed_func_user_data_ = nullptr;
// The local peer port that we bind a socket to for listening
// to incoming peer connections. Usually the same as
// `settings_.peer_port` but can differ if
// `settings_.peer_port_random_on_start` is enabled.
tr_port local_peer_port_;
// The incoming peer port that's been opened on the public-facing
// device. This is usually the same as localPeerPort() but can differ,
// e.g. if the public device is a router that chose to use a different
@ -1048,15 +1048,17 @@ private:
tr_session_id session_id_;
tr_open_files open_files_;
std::vector<libtransmission::Blocklist> blocklists_;
/// other fields
// depends-on: session_thread_
tr_bindinfo bind_ipv4_ = tr_bindinfo{ tr_inaddr_any };
// depends-on: session_thread_, settings_.bind_address_ipv4, local_peer_port_
std::optional<BoundSocket> bound_ipv4_;
// depends-on: session_thread_
tr_bindinfo bind_ipv6_ = tr_bindinfo{ tr_in6addr_any };
// depends-on: session_thread_, settings_.bind_address_ipv6, local_peer_port_
std::optional<BoundSocket> bound_ipv6_;
public:
// depends-on: announcer_udp_
@ -1070,7 +1072,7 @@ private:
// depends-on: top_bandwidth_
std::vector<std::pair<tr_interned_string, std::unique_ptr<tr_bandwidth>>> bandwidth_groups_;
// depends-on: settings_, timer_maker_
// depends-on: timer_maker_, settings_, local_peer_port_
PortForwardingMediator port_forwarding_mediator_{ *this };
std::unique_ptr<tr_port_forwarding> port_forwarding_ = tr_port_forwarding::create(port_forwarding_mediator_);
@ -1078,8 +1080,6 @@ private:
AltSpeedMediator alt_speed_mediator_{ *this };
tr_session_alt_speeds alt_speeds_{ alt_speed_mediator_ };
tr_open_files open_files_;
// depends-on: open_files_
tr_torrents torrents_;
@ -1095,7 +1095,7 @@ private:
// depends-on: timer_maker_, top_bandwidth_, torrents_, web_
std::unique_ptr<struct tr_peerMgr, void (*)(struct tr_peerMgr*)> peer_mgr_;
// depends-on: peer_mgr_, torrents_
// depends-on: peer_mgr_, advertised_peer_port_, torrents_
LpdMediator lpd_mediator_{ *this };
// depends-on: lpd_mediator_

View File

@ -32,6 +32,12 @@ TEST_F(NetTest, conversionsIPv4)
EXPECT_EQ(Port, addrport->second);
}
TEST_F(NetTest, trAddress)
{
EXPECT_EQ("0.0.0.0", tr_address::AnyIPv4().readable());
EXPECT_EQ("::", tr_address::AnyIPv6().readable());
}
TEST_F(NetTest, compact4)
{
static auto constexpr ExpectedReadable = "10.10.10.5"sv;