1
0
Fork 0
mirror of https://github.com/transmission/transmission synced 2025-02-20 21:26:53 +00:00

refactor: add tr_address::toCompact() (#4014)

* refactor: add tr_address::toCompact()

* test: use the theory.org compact ipv4/6 examples in NetTest.compact4, NetTest.compact6

* refactor: add tr_address::toCompact()

* test: add toCompact, fromCompact tests

* refactor: add compact <--> sockaddr_storage conversion
This commit is contained in:
Charles Kerr 2022-10-24 13:40:12 -05:00 committed by GitHub
parent be4a44292e
commit b32f3e0a24
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 286 additions and 103 deletions

View file

@ -249,11 +249,11 @@ void tr_announcerParseHttpAnnounceResponse(tr_announce_response& response, std::
}
else if (key == "peers"sv)
{
response_.pex = tr_peerMgrCompactToPex(std::data(value), std::size(value), nullptr, 0);
response_.pex = tr_pex::fromCompact4(std::data(value), std::size(value), nullptr, 0);
}
else if (key == "peers6"sv)
{
response_.pex6 = tr_peerMgrCompact6ToPex(std::data(value), std::size(value), nullptr, 0);
response_.pex6 = tr_pex::fromCompact6(std::data(value), std::size(value), nullptr, 0);
}
else if (key == "ip")
{
@ -268,7 +268,7 @@ void tr_announcerParseHttpAnnounceResponse(tr_announce_response& response, std::
}
else if (key == "external ip"sv && std::size(value) == 4)
{
auto const [addr, out] = tr_address::fromCompact4(reinterpret_cast<uint8_t const*>(std::data(value)));
auto const [addr, out] = tr_address::fromCompact4(reinterpret_cast<std::byte const*>(std::data(value)));
response_.external_ip = addr;
}
else

View file

@ -24,7 +24,7 @@
#include "crypto-utils.h" /* tr_rand_buffer() */
#include "log.h"
#include "peer-io.h"
#include "peer-mgr.h" /* tr_peerMgrCompactToPex() */
#include "peer-mgr.h" // for tr_pex::fromCompact4()
#include "session.h"
#include "tr-assert.h"
#include "tr-buffer.h"
@ -228,7 +228,7 @@ struct tau_announce_request
auto const compact_len = std::size(buf);
auto contiguous = std::array<uint8_t, 576>{};
buf.toBuf(std::data(contiguous), compact_len);
response.pex = tr_peerMgrCompactToPex(std::data(contiguous), compact_len, nullptr, 0);
response.pex = tr_pex::fromCompact4(std::data(contiguous), compact_len, nullptr, 0);
requestFinished();
}
else
@ -298,7 +298,7 @@ static tau_announce_request make_tau_announce_request(
buf.addUint32(announce_ip);
buf.addUint32(in.key);
buf.addUint32(in.numwant);
buf.addUint16(in.port.host());
buf.addPort(in.port);
/* build the tau_announce_request */
auto req = tau_announce_request();

View file

@ -30,7 +30,6 @@
#include "announcer.h"
#include "crypto-utils.h" /* tr_rand_int(), tr_rand_int_weak() */
#include "log.h"
#include "peer-mgr.h" /* tr_peerMgrCompactToPex() */
#include "session.h"
#include "timer.h"
#include "torrent.h"

View file

@ -812,13 +812,13 @@ struct tr_peer_socket tr_peer_socket_utp_create(struct UTPSocket* const handle)
/// tr_port
std::pair<tr_port, uint8_t const*> tr_port::fromCompact(uint8_t const* compact) noexcept
std::pair<tr_port, std::byte const*> tr_port::fromCompact(std::byte const* compact) noexcept
{
static auto constexpr PortLen = size_t{ 2 };
static_assert(PortLen == sizeof(uint16_t));
auto nport = uint16_t{};
std::copy_n(compact, PortLen, reinterpret_cast<uint8_t*>(&nport));
std::copy_n(compact, PortLen, reinterpret_cast<std::byte*>(&nport));
compact += PortLen;
return std::make_pair(tr_port::fromNetwork(nport), compact);
@ -878,26 +878,26 @@ template char* tr_address::readable<char*>(char*, tr_port) const;
return buf;
}
std::pair<tr_address, uint8_t const*> tr_address::fromCompact4(uint8_t const* compact) noexcept
std::pair<tr_address, std::byte const*> tr_address::fromCompact4(std::byte const* compact) noexcept
{
static auto constexpr Addr4Len = size_t{ 4 };
auto address = tr_address{};
static_assert(sizeof(address.addr.addr4) == Addr4Len);
address.type = TR_AF_INET;
std::copy_n(compact, Addr4Len, reinterpret_cast<uint8_t*>(&address.addr));
std::copy_n(compact, Addr4Len, reinterpret_cast<std::byte*>(&address.addr));
compact += Addr4Len;
return std::make_pair(address, compact);
}
std::pair<tr_address, uint8_t const*> tr_address::fromCompact6(uint8_t const* compact) noexcept
std::pair<tr_address, std::byte const*> tr_address::fromCompact6(std::byte const* compact) noexcept
{
static auto constexpr Addr6Len = size_t{ 16 };
auto address = tr_address{};
address.type = TR_AF_INET6;
std::copy_n(compact, Addr6Len, reinterpret_cast<uint8_t*>(&address.addr.addr6.s6_addr));
std::copy_n(compact, Addr6Len, reinterpret_cast<std::byte*>(&address.addr.addr6.s6_addr));
compact += Addr6Len;
return std::make_pair(address, compact);

View file

@ -8,8 +8,8 @@
#error only libtransmission should #include this header.
#endif
#include <algorithm> // for std::copy_n
#include <cstddef> // size_t
#include <cstdint> // uint8_t
#include <optional>
#include <string>
#include <string_view>
@ -114,7 +114,7 @@ public:
hport_ = ntohs(nport);
}
[[nodiscard]] static std::pair<tr_port, uint8_t const*> fromCompact(uint8_t const* compact) noexcept;
[[nodiscard]] static std::pair<tr_port, std::byte const*> fromCompact(std::byte const* compact) noexcept;
[[nodiscard]] constexpr auto operator<(tr_port const& that) const noexcept
{
@ -154,8 +154,8 @@ struct tr_address
{
[[nodiscard]] static std::optional<tr_address> fromString(std::string_view address_sv);
[[nodiscard]] static std::optional<std::pair<tr_address, tr_port>> fromSockaddr(struct sockaddr const*);
[[nodiscard]] static std::pair<tr_address, uint8_t const*> fromCompact4(uint8_t const* compact) noexcept;
[[nodiscard]] static std::pair<tr_address, uint8_t const*> fromCompact6(uint8_t const* compact) noexcept;
[[nodiscard]] static std::pair<tr_address, std::byte const*> fromCompact4(std::byte const* compact) noexcept;
[[nodiscard]] static std::pair<tr_address, std::byte const*> fromCompact6(std::byte const* compact) noexcept;
// human-readable formatting
template<typename OutputIt>
@ -163,6 +163,61 @@ struct tr_address
std::string_view readable(char* out, size_t outlen, tr_port port = {}) const;
[[nodiscard]] std::string readable(tr_port port = {}) const;
template<typename OutputIt>
static OutputIt toCompact4(OutputIt out, in_addr const* addr4, tr_port port)
{
auto const nport = port.network();
out = std::copy_n(reinterpret_cast<std::byte const*>(addr4), sizeof(*addr4), out);
out = std::copy_n(reinterpret_cast<std::byte const*>(&nport), sizeof(nport), out);
return out;
}
template<typename OutputIt>
static OutputIt toCompact4(OutputIt out, sockaddr_in const* sa4)
{
return toCompact4(out, &sa4->sin_addr, tr_port::fromNetwork(sa4->sin_port));
}
template<typename OutputIt>
static OutputIt toCompact6(OutputIt out, in6_addr const* addr6, tr_port port)
{
auto const nport = port.network();
out = std::copy_n(reinterpret_cast<std::byte const*>(addr6), sizeof(*addr6), out);
out = std::copy_n(reinterpret_cast<std::byte const*>(&nport), sizeof(nport), out);
return out;
}
template<typename OutputIt>
static OutputIt toCompact6(OutputIt out, sockaddr_in6 const* sa6)
{
return toCompact6(out, &sa6->sin6_addr, tr_port::fromNetwork(sa6->sin6_port));
}
template<typename OutputIt>
OutputIt toCompact4(OutputIt out, tr_port port) const
{
return toCompact4(out, &this->addr.addr4, port);
}
template<typename OutputIt>
OutputIt toCompact6(OutputIt out, tr_port port) const
{
return toCompact6(out, &this->addr.addr6, port);
}
template<typename OutputIt>
static OutputIt toCompact(OutputIt out, sockaddr const* saddr)
{
return saddr->sa_family == AF_INET ? toCompact4(out, reinterpret_cast<sockaddr_in const*>(saddr)) :
toCompact6(out, reinterpret_cast<sockaddr_in6 const*>(saddr));
}
template<typename OutputIt>
static OutputIt toCompact(OutputIt out, struct sockaddr_storage* ss)
{
return toCompact(out, reinterpret_cast<struct sockaddr*>(ss));
}
[[nodiscard]] constexpr auto isIPv4() const noexcept
{
return type == TR_AF_INET;

View file

@ -1297,10 +1297,10 @@ size_t tr_peerMgrAddPex(tr_torrent* tor, uint8_t from, tr_pex const* pex, size_t
return n_used;
}
std::vector<tr_pex> tr_peerMgrCompactToPex(void const* compact, size_t compact_len, uint8_t const* added_f, size_t added_f_len)
std::vector<tr_pex> tr_pex::fromCompact4(void const* compact, size_t compact_len, uint8_t const* added_f, size_t added_f_len)
{
size_t const n = compact_len / 6;
auto const* walk = static_cast<uint8_t const*>(compact);
auto const* walk = static_cast<std::byte const*>(compact);
auto pex = std::vector<tr_pex>(n);
for (size_t i = 0; i < n; ++i)
@ -1317,10 +1317,10 @@ std::vector<tr_pex> tr_peerMgrCompactToPex(void const* compact, size_t compact_l
return pex;
}
std::vector<tr_pex> tr_peerMgrCompact6ToPex(void const* compact, size_t compact_len, uint8_t const* added_f, size_t added_f_len)
std::vector<tr_pex> tr_pex::fromCompact6(void const* compact, size_t compact_len, uint8_t const* added_f, size_t added_f_len)
{
size_t const n = compact_len / 18;
auto const* walk = static_cast<uint8_t const*>(compact);
auto const* walk = static_cast<std::byte const*>(compact);
auto pex = std::vector<tr_pex>(n);
for (size_t i = 0; i < n; ++i)

View file

@ -64,6 +64,50 @@ struct tr_pex
{
}
template<typename OutputIt>
OutputIt toCompact4(OutputIt out) const
{
return this->addr.toCompact4(out, this->port);
}
template<typename OutputIt>
OutputIt toCompact6(OutputIt out) const
{
return this->addr.toCompact6(out, this->port);
}
template<typename OutputIt>
static OutputIt toCompact4(OutputIt out, tr_pex const* pex, size_t n_pex)
{
for (size_t i = 0; i < n_pex; ++i)
{
out = pex[i].toCompact4(out);
}
return out;
}
template<typename OutputIt>
static OutputIt toCompact6(OutputIt out, tr_pex const* pex, size_t n_pex)
{
for (size_t i = 0; i < n_pex; ++i)
{
out = pex[i].toCompact6(out);
}
return out;
}
[[nodiscard]] static std::vector<tr_pex> fromCompact4(
void const* compact,
size_t compact_len,
uint8_t const* added_f,
size_t added_f_len);
[[nodiscard]] static std::vector<tr_pex> fromCompact6(
void const* compact,
size_t compact_len,
uint8_t const* added_f,
size_t added_f_len);
template<typename OutputIt>
[[nodiscard]] OutputIt readable(OutputIt out) const
{
@ -128,18 +172,6 @@ void tr_peerMgrClientSentRequests(tr_torrent* torrent, tr_peer* peer, tr_block_s
void tr_peerMgrAddIncoming(tr_peerMgr* manager, tr_address const& addr, tr_port port, struct tr_peer_socket const socket);
[[nodiscard]] std::vector<tr_pex> tr_peerMgrCompactToPex(
void const* compact,
size_t compact_len,
uint8_t const* added_f,
size_t added_f_len);
[[nodiscard]] std::vector<tr_pex> tr_peerMgrCompact6ToPex(
void const* compact,
size_t compact_len,
uint8_t const* added_f,
size_t added_f_len);
size_t tr_peerMgrAddPex(tr_torrent* tor, uint8_t from, tr_pex const* pex, size_t n_pex);
void tr_peerMgrSetSwarmIsAllSeeds(tr_torrent* tor);

View file

@ -806,7 +806,7 @@ static void protocolSendPort(tr_peerMsgsImpl* msgs, tr_port port)
logtrace(msgs, fmt::format(FMT_STRING("sending Port {:d}"), port.host()));
out.addUint32(3);
out.addUint8(BtPeerMsgs::Port);
out.addUint16(port.network());
out.addPort(port);
}
static void protocolSendHave(tr_peerMsgsImpl* msgs, tr_piece_index_t index)
@ -1221,7 +1221,7 @@ static void parseUtPex(tr_peerMsgsImpl* msgs, uint32_t msglen)
added_f = nullptr;
}
auto pex = tr_peerMgrCompactToPex(added, added_len, added_f, added_f_len);
auto pex = tr_pex::fromCompact4(added, added_len, added_f, added_f_len);
pex.resize(std::min(MaxPexPeerCount, std::size(pex)));
tr_peerMgrAddPex(tor, TR_PEER_FROM_PEX, std::data(pex), std::size(pex));
}
@ -1236,7 +1236,7 @@ static void parseUtPex(tr_peerMsgsImpl* msgs, uint32_t msglen)
added_f = nullptr;
}
auto pex = tr_peerMgrCompact6ToPex(added, added_len, added_f, added_f_len);
auto pex = tr_pex::fromCompact6(added, added_len, added_f, added_f_len);
pex.resize(std::min(MaxPexPeerCount, std::size(pex)));
tr_peerMgrAddPex(tor, TR_PEER_FROM_PEX, std::data(pex), std::size(pex));
}
@ -2288,33 +2288,25 @@ void tr_peerMsgsImpl::sendPex()
auto val = tr_variant{};
tr_variantInitDict(&val, 3); /* ipv6 support: left as 3: speed vs. likelihood? */
auto tmpbuf = std::vector<uint8_t>{};
auto tmpbuf = std::vector<std::byte>{};
tmpbuf.reserve(MaxPexAdded * 18);
if (!std::empty(added))
{
// "added"
tmpbuf.resize(std::size(added) * 6U);
auto* begin = std::data(tmpbuf);
auto* walk = begin;
for (auto const& p : added)
{
memcpy(walk, &p.addr.addr, 4U);
walk += 4U;
memcpy(walk, &p.port, 2U);
walk += 2U;
}
TR_ASSERT(static_cast<size_t>(walk - begin) == std::size(added) * 6U);
tr_variantDictAddRaw(&val, TR_KEY_added, begin, walk - begin);
tmpbuf.clear();
tr_pex::toCompact4(std::back_inserter(tmpbuf), std::data(added), std::size(added));
TR_ASSERT(std::size(tmpbuf) == std::size(added) * 6);
tr_variantDictAddRaw(&val, TR_KEY_added, std::data(tmpbuf), std::size(tmpbuf));
// "added.f"
// unset each holepunch flag because we don't support it.
tmpbuf.resize(std::size(added));
begin = std::data(tmpbuf);
walk = begin;
auto* begin = std::data(tmpbuf);
auto* walk = begin;
for (auto const& p : added)
{
*walk++ = p.flags & ~ADDED_F_HOLEPUNCH;
*walk++ = std::byte{ p.flags } & ~std::byte{ ADDED_F_HOLEPUNCH };
}
TR_ASSERT(static_cast<size_t>(walk - begin) == std::size(added));
@ -2324,46 +2316,27 @@ void tr_peerMsgsImpl::sendPex()
if (!std::empty(dropped))
{
// "dropped"
tmpbuf.resize(std::size(dropped) * 6U);
auto* begin = std::data(tmpbuf);
auto* walk = begin;
for (auto const& p : dropped)
{
memcpy(walk, &p.addr.addr, 4U);
walk += 4U;
memcpy(walk, &p.port, 2U);
walk += 2U;
}
TR_ASSERT(static_cast<size_t>(walk - begin) == std::size(dropped) * 6U);
tr_variantDictAddRaw(&val, TR_KEY_dropped, begin, walk - begin);
tmpbuf.clear();
tr_pex::toCompact4(std::back_inserter(tmpbuf), std::data(dropped), std::size(dropped));
TR_ASSERT(std::size(tmpbuf) == std::size(dropped) * 6);
tr_variantDictAddRaw(&val, TR_KEY_dropped, std::data(tmpbuf), std::size(tmpbuf));
}
if (!std::empty(added6))
{
// "added6"
tmpbuf.resize(std::size(added6) * 18U);
auto* begin = std::data(tmpbuf);
auto* walk = begin;
for (auto const& p : added6)
{
memcpy(walk, &p.addr.addr.addr6.s6_addr, 16U);
walk += 16U;
memcpy(walk, &p.port, 2U);
walk += 2U;
}
TR_ASSERT(static_cast<size_t>(walk - begin) == std::size(added6) * 18U);
tr_variantDictAddRaw(&val, TR_KEY_added6, begin, walk - begin);
tmpbuf.clear();
tr_pex::toCompact6(std::back_inserter(tmpbuf), std::data(added6), std::size(added6));
TR_ASSERT(std::size(tmpbuf) == std::size(added6) * 18);
tr_variantDictAddRaw(&val, TR_KEY_added6, std::data(tmpbuf), std::size(tmpbuf));
// "added6.f"
// unset each holepunch flag because we don't support it.
tmpbuf.resize(std::size(added6));
begin = std::data(tmpbuf);
walk = begin;
auto* begin = std::data(tmpbuf);
auto* walk = begin;
for (auto const& p : added6)
{
*walk++ = p.flags & ~ADDED_F_HOLEPUNCH;
*walk++ = std::byte{ p.flags } & ~std::byte{ ADDED_F_HOLEPUNCH };
}
TR_ASSERT(static_cast<size_t>(walk - begin) == std::size(added6));
@ -2373,19 +2346,10 @@ void tr_peerMsgsImpl::sendPex()
if (!std::empty(dropped6))
{
// "dropped6"
tmpbuf.resize(std::size(dropped6) * 18U);
auto* const begin = std::data(tmpbuf);
auto* walk = begin;
for (auto const& p : dropped6)
{
memcpy(walk, &p.addr.addr.addr6.s6_addr, 16U);
walk += 16U;
memcpy(walk, &p.port, 2U);
walk += 2U;
}
TR_ASSERT(static_cast<size_t>(walk - begin) == std::size(dropped6) * 18U);
tr_variantDictAddRaw(&val, TR_KEY_dropped6, begin, walk - begin);
tmpbuf.clear();
tr_pex::toCompact6(std::back_inserter(tmpbuf), std::data(dropped6), std::size(dropped6));
TR_ASSERT(std::size(tmpbuf) == std::size(dropped6) * 18);
tr_variantDictAddRaw(&val, TR_KEY_dropped6, std::data(tmpbuf), std::size(tmpbuf));
}
/* write the pex message */

View file

@ -302,6 +302,12 @@ public:
add(&ch, 1);
}
void addPort(tr_port const& port)
{
auto nport = port.network();
add(&nport, sizeof(nport));
}
void addUint8(uint8_t uch)
{
add(&uch, 1);

View file

@ -342,7 +342,7 @@ static void bootstrapFromFile(std::string_view config_dir)
}
}
static void bootstrapStart(std::string_view config_dir, std::vector<uint8_t> nodes4, std::vector<uint8_t> nodes6)
static void bootstrapStart(std::string_view config_dir, std::vector<std::byte> nodes4, std::vector<std::byte> nodes6)
{
if (!tr_dhtEnabled())
{
@ -452,8 +452,8 @@ int tr_dhtInit(tr_session* session, tr_socket_t udp4_socket, tr_socket_t udp6_so
auto const ok = tr_variantFromFile(&benc, TR_VARIANT_PARSE_BENC, dat_file.sv());
bool have_id = false;
auto nodes = std::vector<uint8_t>{};
auto nodes6 = std::vector<uint8_t>{};
auto nodes = std::vector<std::byte>{};
auto nodes6 = std::vector<std::byte>{};
if (ok)
{
@ -465,7 +465,7 @@ int tr_dhtInit(tr_session* session, tr_socket_t udp4_socket, tr_socket_t udp6_so
}
size_t raw_len = 0U;
uint8_t const* raw = nullptr;
std::byte const* raw = nullptr;
if (tr_variantDictFindRaw(&benc, TR_KEY_nodes, &raw, &raw_len) && raw_len % 6 == 0)
{
@ -643,8 +643,8 @@ static void callback(void* vsession, int event, unsigned char const* info_hash,
{
if (tor != nullptr && tor->allowsDht())
{
auto const pex = event == DHT_EVENT_VALUES ? tr_peerMgrCompactToPex(data, data_len, nullptr, 0) :
tr_peerMgrCompact6ToPex(data, data_len, nullptr, 0);
auto const pex = event == DHT_EVENT_VALUES ? tr_pex::fromCompact4(data, data_len, nullptr, 0) :
tr_pex::fromCompact6(data, data_len, nullptr, 0);
tr_peerMgrAddPex(tor, TR_PEER_FROM_DHT, std::data(pex), std::size(pex));
tr_logAddDebugTor(
tor,

View file

@ -246,6 +246,19 @@ bool tr_variantGetStrView(tr_variant const* v, std::string_view* setme)
return true;
}
bool tr_variantGetRaw(tr_variant const* v, std::byte const** setme_raw, size_t* setme_len)
{
bool const success = tr_variantIsString(v);
if (success)
{
*setme_raw = (std::byte const*)getStr(v);
*setme_len = v->val.s.len;
}
return success;
}
bool tr_variantGetRaw(tr_variant const* v, uint8_t const** setme_raw, size_t* setme_len)
{
bool const success = tr_variantIsString(v);
@ -358,7 +371,13 @@ bool tr_variantDictFindDict(tr_variant* dict, tr_quark const key, tr_variant** s
bool tr_variantDictFindRaw(tr_variant* dict, tr_quark const key, uint8_t const** setme_raw, size_t* setme_len)
{
tr_variant const* child = tr_variantDictFind(dict, key);
auto const* child = tr_variantDictFind(dict, key);
return tr_variantGetRaw(child, setme_raw, setme_len);
}
bool tr_variantDictFindRaw(tr_variant* dict, tr_quark const key, std::byte const** setme_raw, size_t* setme_len)
{
auto const* child = tr_variantDictFind(dict, key);
return tr_variantGetRaw(child, setme_raw, setme_len);
}

View file

@ -176,6 +176,7 @@ void tr_variantInitStrView(tr_variant* initme, std::string_view);
void tr_variantInitQuark(tr_variant* initme, tr_quark const quark);
void tr_variantInitRaw(tr_variant* initme, void const* raw, size_t raw_len);
bool tr_variantGetRaw(tr_variant const* variant, std::byte const** setme_raw, size_t* setme_len);
bool tr_variantGetRaw(tr_variant const* variant, uint8_t const** setme_raw, size_t* setme_len);
/***
@ -275,6 +276,7 @@ bool tr_variantDictFindReal(tr_variant* dict, tr_quark const key, double* setme)
bool tr_variantDictFindBool(tr_variant* dict, tr_quark const key, bool* setme);
bool tr_variantDictFindStrView(tr_variant* dict, tr_quark const key, std::string_view* setme);
bool tr_variantDictFindRaw(tr_variant* dict, tr_quark const key, uint8_t const** setme_raw, size_t* setme_len);
bool tr_variantDictFindRaw(tr_variant* dict, tr_quark const key, std::byte const** setme_raw, size_t* setme_len);
/* this is only quasi-supported. don't rely on it too heavily outside of libT */
void tr_variantMergeDicts(tr_variant* dict_target, tr_variant const* dict_source);

View file

@ -6,6 +6,7 @@
#include "transmission.h"
#include "net.h"
#include "peer-mgr.h"
#include "test-fixtures.h"
@ -30,3 +31,108 @@ TEST_F(NetTest, conversionsIPv4)
EXPECT_EQ(addr, addrport->first);
EXPECT_EQ(Port, addrport->second);
}
TEST_F(NetTest, compact4)
{
static auto constexpr ExpectedReadable = "10.10.10.5"sv;
static auto constexpr ExpectedPort = tr_port::fromHost(128);
static auto constexpr Compact4 = std::array<std::byte, 6>{ std::byte{ 0x0A }, std::byte{ 0x0A }, std::byte{ 0x0A },
std::byte{ 0x05 }, std::byte{ 0x00 }, std::byte{ 0x80 } };
/// compact <--> tr_address, port
// extract the address and port from a compact stream...
auto in = std::data(Compact4);
auto addr = tr_address{};
auto port = tr_port{};
std::tie(addr, in) = tr_address::fromCompact4(in);
std::tie(port, in) = tr_port::fromCompact(in);
EXPECT_EQ(std::data(Compact4) + std::size(Compact4), in);
EXPECT_EQ(ExpectedReadable, addr.readable());
EXPECT_EQ(ExpectedPort, port);
// ...serialize it back again
auto compact4 = std::array<std::byte, 6>{};
auto out = std::data(compact4);
out = addr.toCompact4(out, port);
EXPECT_EQ(std::size(Compact4), out - std::data(compact4));
EXPECT_EQ(Compact4, compact4);
/// sockaddr --> compact
auto [ss, sslen] = addr.toSockaddr(port);
std::fill(std::begin(compact4), std::end(compact4), std::byte{});
out = std::data(compact4);
out = tr_address::toCompact(out, &ss);
EXPECT_EQ(out, std::data(compact4) + std::size(compact4));
EXPECT_EQ(Compact4, compact4);
/// compact <--> tr_pex
// extract them into a tr_pex struct...
auto const pex = tr_pex::fromCompact4(std::data(compact4), std::size(compact4), nullptr, 0U);
ASSERT_EQ(1U, std::size(pex));
EXPECT_EQ(addr, pex.front().addr);
EXPECT_EQ(port, pex.front().port);
// ...serialize that back again too
std::fill(std::begin(compact4), std::end(compact4), std::byte{});
out = std::data(compact4);
out = tr_pex::toCompact4(out, std::data(pex), std::size(pex));
EXPECT_EQ(std::data(compact4) + std::size(compact4), out);
EXPECT_EQ(Compact4, compact4);
}
TEST_F(NetTest, compact6)
{
static auto constexpr ExpectedReadable = "1002:1035:4527:3546:7854:1237:3247:3217"sv;
static auto constexpr ExpectedPort = tr_port::fromHost(6881);
static auto constexpr Compact6 = std::array<std::byte, 18>{
std::byte{ 0x10 }, std::byte{ 0x02 }, std::byte{ 0x10 }, std::byte{ 0x35 }, std::byte{ 0x45 }, std::byte{ 0x27 },
std::byte{ 0x35 }, std::byte{ 0x46 }, std::byte{ 0x78 }, std::byte{ 0x54 }, std::byte{ 0x12 }, std::byte{ 0x37 },
std::byte{ 0x32 }, std::byte{ 0x47 }, std::byte{ 0x32 }, std::byte{ 0x17 }, std::byte{ 0x1A }, std::byte{ 0xE1 }
};
/// compact <--> tr_address, tr_port
// extract the address and port from a compact stream...
auto in = std::data(Compact6);
auto addr = tr_address{};
auto port = tr_port{};
std::tie(addr, in) = tr_address::fromCompact6(in);
std::tie(port, in) = tr_port::fromCompact(in);
EXPECT_EQ(std::data(Compact6) + std::size(Compact6), in);
EXPECT_EQ(ExpectedReadable, addr.readable());
EXPECT_EQ(ExpectedPort, port);
// ...serialize it back again
auto compact6 = std::array<std::byte, 18>{};
auto out = std::data(compact6);
out = addr.toCompact6(out, port);
EXPECT_EQ(std::size(Compact6), out - std::data(compact6));
EXPECT_EQ(Compact6, compact6);
/// sockaddr --> compact
auto [ss, sslen] = addr.toSockaddr(port);
std::fill(std::begin(compact6), std::end(compact6), std::byte{});
out = std::data(compact6);
out = tr_address::toCompact(out, &ss);
EXPECT_EQ(out, std::data(compact6) + std::size(compact6));
EXPECT_EQ(Compact6, compact6);
/// compact <--> tr_pex
// extract them into a tr_pex struct...
auto const pex = tr_pex::fromCompact6(std::data(compact6), std::size(compact6), nullptr, 0U);
ASSERT_EQ(1U, std::size(pex));
EXPECT_EQ(addr, pex.front().addr);
EXPECT_EQ(port, pex.front().port);
// ...serialize that back again too
std::fill(std::begin(compact6), std::end(compact6), std::byte{});
out = std::data(compact6);
out = tr_pex::toCompact6(out, std::data(pex), std::size(pex));
EXPECT_EQ(std::data(compact6) + std::size(compact6), out);
EXPECT_EQ(Compact6, compact6);
}