diff --git a/libtransmission/global-ip-cache.cc b/libtransmission/global-ip-cache.cc index 02e45c428..d944eaab7 100644 --- a/libtransmission/global-ip-cache.cc +++ b/libtransmission/global-ip-cache.cc @@ -31,12 +31,6 @@ using namespace std::literals; static_assert(TR_AF_INET == 0); static_assert(TR_AF_INET6 == 1); -auto TR_CONSTEXPR23 protocol_str(tr_address_type type) noexcept -{ - static auto TR_CONSTEXPR23 map = std::array{ "IPv4"sv, "IPv6"sv }; - return map[type]; -} - auto constexpr IPQueryServices = std::array{ std::array{ "https://ip4.transmissionbt.com/"sv }, std::array{ "https://ip6.transmissionbt.com/"sv } }; @@ -268,7 +262,7 @@ void tr_global_ip_cache::update_source_addr(tr_address_type type) noexcept } TR_ASSERT(is_updating_[type] == is_updating_t::YES); - auto const protocol = protocol_str(type); + auto const protocol = tr_ip_protocol_sv(type); auto err = int{ 0 }; auto const& source_addr = get_global_source_address(bind_addr(type), err); @@ -302,7 +296,7 @@ void tr_global_ip_cache::on_response_ip_query(tr_address_type type, tr_web::Fetc TR_ASSERT(is_updating_[type] == is_updating_t::YES); TR_ASSERT(ix_service_[type] < std::size(IPQueryServices[type])); - auto const protocol = protocol_str(type); + auto const protocol = tr_ip_protocol_sv(type); auto success = false; if (response.status == 200 /* HTTP_OK */) @@ -343,7 +337,7 @@ void tr_global_ip_cache::unset_global_addr(tr_address_type type) noexcept { auto const lock = std::lock_guard{ global_addr_mutex_[type] }; global_addr_[type].reset(); - tr_logAddTrace(fmt::format("Unset {} global address cache", protocol_str(type))); + tr_logAddTrace(fmt::format("Unset {} global address cache", tr_ip_protocol_sv(type))); } void tr_global_ip_cache::set_source_addr(tr_address const& addr) noexcept @@ -357,7 +351,7 @@ void tr_global_ip_cache::unset_addr(tr_address_type type) noexcept { auto const lock = std::lock_guard{ source_addr_mutex_[type] }; source_addr_[type].reset(); - tr_logAddTrace(fmt::format("Unset {} source address cache", protocol_str(type))); + tr_logAddTrace(fmt::format("Unset {} source address cache", tr_ip_protocol_sv(type))); // No public internet connectivity means no global IP address unset_global_addr(type); diff --git a/libtransmission/net.cc b/libtransmission/net.cc index c243dbf74..5c4d5331f 100644 --- a/libtransmission/net.cc +++ b/libtransmission/net.cc @@ -56,6 +56,12 @@ std::string tr_net_strerror(int err) #endif } +std::string_view tr_ip_protocol_sv(tr_address_type type) noexcept +{ + static auto TR_CONSTEXPR23 map = std::array{ std::string_view{ "IPv4" }, std::string_view{ "IPv6" } }; + return map[type]; +} + // - TCP Sockets [[nodiscard]] std::optional tr_tos_t::from_string(std::string_view name) diff --git a/libtransmission/net.h b/libtransmission/net.h index 8f9e3a03a..47b94afcf 100644 --- a/libtransmission/net.h +++ b/libtransmission/net.h @@ -129,6 +129,8 @@ public: hport_ = 0; } + static auto constexpr CompactPortBytes = 2U; + private: explicit constexpr tr_port(uint16_t hport) noexcept : hport_{ hport } @@ -138,13 +140,15 @@ private: uint16_t hport_ = 0; }; -enum tr_address_type +enum tr_address_type : uint8_t { TR_AF_INET, TR_AF_INET6, NUM_TR_AF_INET_TYPES }; +std::string_view tr_ip_protocol_sv(tr_address_type type) noexcept; + struct tr_socket_address; struct tr_address @@ -296,6 +300,8 @@ struct tr_address struct in_addr addr4; } addr; + static auto constexpr CompactAddrBytes = std::array{ 4U, 16U }; + [[nodiscard]] static auto constexpr any_ipv4() noexcept { return tr_address{ TR_AF_INET, { { { { INADDR_ANY } } } } }; @@ -371,6 +377,9 @@ struct tr_socket_address tr_address address_; tr_port port_; + + static auto constexpr CompactSockAddrBytes = std::array{ tr_address::CompactAddrBytes[0] + tr_port::CompactPortBytes, + tr_address::CompactAddrBytes[1] + tr_port::CompactPortBytes }; }; template<> diff --git a/libtransmission/peer-mgr.cc b/libtransmission/peer-mgr.cc index fa92d84c3..d9f38cef5 100644 --- a/libtransmission/peer-mgr.cc +++ b/libtransmission/peer-mgr.cc @@ -1038,7 +1038,7 @@ std::vector tr_pex::from_compact_ipv4( uint8_t const* added_f, size_t added_f_len) { - size_t const n = compact_len / 6; + size_t const n = compact_len / tr_socket_address::CompactSockAddrBytes[TR_AF_INET]; auto const* walk = static_cast(compact); auto pex = std::vector(n); @@ -1062,7 +1062,7 @@ std::vector tr_pex::from_compact_ipv6( uint8_t const* added_f, size_t added_f_len) { - size_t const n = compact_len / 18; + size_t const n = compact_len / tr_socket_address::CompactSockAddrBytes[TR_AF_INET6]; auto const* walk = static_cast(compact); auto pex = std::vector(n); diff --git a/libtransmission/peer-mgr.h b/libtransmission/peer-mgr.h index 2c6ccdcbf..066fc25d4 100644 --- a/libtransmission/peer-mgr.h +++ b/libtransmission/peer-mgr.h @@ -435,6 +435,21 @@ struct tr_pex return out; } + template + static OutputIt to_compact(OutputIt out, tr_pex const* pex, size_t n_pex, tr_address_type type) + { + switch (type) + { + case TR_AF_INET: + return to_compact_ipv4(out, pex, n_pex); + case TR_AF_INET6: + return to_compact_ipv6(out, pex, n_pex); + default: + TR_ASSERT_MSG(false, "invalid type"); + return out; + } + } + [[nodiscard]] static std::vector from_compact_ipv4( void const* compact, size_t compact_len, diff --git a/libtransmission/peer-msgs.cc b/libtransmission/peer-msgs.cc index 560dab8df..780e2434c 100644 --- a/libtransmission/peer-msgs.cc +++ b/libtransmission/peer-msgs.cc @@ -653,8 +653,7 @@ public: std::vector peer_requested_; - std::vector pex; - std::vector pex6; + std::array, NUM_TR_AF_INET_TYPES> pex; std::queue peerAskedForMetadata; @@ -1169,7 +1168,7 @@ void parseUtMetadata(tr_peerMsgsImpl* msgs, MessageReader& payload_in) } if (msg_type == MetadataMsgType::Data && total_size == msgs->metadata_size_hint && !msgs->torrent->has_metainfo() && - msg_end - benc_end <= METADATA_PIECE_SIZE && piece * METADATA_PIECE_SIZE + (msg_end - benc_end) <= total_size) + msg_end - benc_end <= MetadataPieceSize && piece * MetadataPieceSize + (msg_end - benc_end) <= total_size) { size_t const piece_len = msg_end - benc_end; tr_torrentSetMetadataPiece(msgs->torrent, piece, benc_end, piece_len); @@ -2023,123 +2022,97 @@ void tr_peerMsgsImpl::sendPex() return; } - auto& old4 = this->pex; - auto new4 = tr_peerMgrGetPeers(this->torrent, TR_AF_INET, TR_PEERS_CONNECTED, MaxPexPeerCount); - auto added = std::vector{}; - added.reserve(std::size(new4)); - std::set_difference(std::begin(new4), std::end(new4), std::begin(old4), std::end(old4), std::back_inserter(added)); - auto dropped = std::vector{}; - dropped.reserve(std::size(old4)); - std::set_difference(std::begin(old4), std::end(old4), std::begin(new4), std::end(new4), std::back_inserter(dropped)); - - auto& old6 = this->pex6; - auto new6 = tr_peerMgrGetPeers(this->torrent, TR_AF_INET6, TR_PEERS_CONNECTED, MaxPexPeerCount); - auto added6 = std::vector{}; - added6.reserve(std::size(new6)); - std::set_difference(std::begin(new6), std::end(new6), std::begin(old6), std::end(old6), std::back_inserter(added6)); - auto dropped6 = std::vector{}; - dropped6.reserve(std::size(old6)); - std::set_difference(std::begin(old6), std::end(old6), std::begin(new6), std::end(new6), std::back_inserter(dropped6)); - - // Some peers give us error messages if we send - // more than this many peers in a single pex message. - // https://wiki.theory.org/BitTorrentPeerExchangeConventions static auto constexpr MaxPexAdded = size_t{ 50 }; - added.resize(std::min(std::size(added), MaxPexAdded)); - added6.resize(std::min(std::size(added6), MaxPexAdded)); static auto constexpr MaxPexDropped = size_t{ 50 }; - dropped.resize(std::min(std::size(dropped), MaxPexDropped)); - dropped6.resize(std::min(std::size(dropped6), MaxPexDropped)); - logtrace( - this, - fmt::format( - FMT_STRING("pex: old peer count {:d}+{:d}, new peer count {:d}+{:d}, added {:d}+{:d}, dropped {:d}+{:d}"), - std::size(old4), - std::size(old6), - std::size(new4), - std::size(new6), - std::size(added), - std::size(added6), - std::size(dropped), - std::size(dropped6))); - - // if there's nothing to send, then we're done - if (std::empty(added) && std::empty(dropped) && std::empty(added6) && std::empty(dropped6)) - { - return; - } - - // update msgs - std::swap(old4, new4); - std::swap(old6, new6); - - // build the pex payload auto val = tr_variant{}; tr_variantInitDict(&val, 3); /* ipv6 support: left as 3: speed vs. likelihood? */ auto tmpbuf = std::vector{}; - tmpbuf.reserve(MaxPexAdded * 18); - if (!std::empty(added)) + for (uint8_t i = 0; i < NUM_TR_AF_INET_TYPES; ++i) { - // "added" - tmpbuf.clear(); - tr_pex::to_compact_ipv4(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)); + static auto constexpr AddedMap = std::array{ TR_KEY_added, TR_KEY_added6 }; + static auto constexpr AddedFMap = std::array{ TR_KEY_added_f, TR_KEY_added6_f }; + static auto constexpr DroppedMap = std::array{ TR_KEY_dropped, TR_KEY_dropped6 }; + auto const ip_type = static_cast(i); - // "added.f" - // unset each holepunch flag because we don't support it. - tmpbuf.resize(std::size(added)); - auto* begin = std::data(tmpbuf); - auto* walk = begin; - for (auto const& p : added) + auto& old_pex = pex[i]; + auto new_pex = tr_peerMgrGetPeers(this->torrent, ip_type, TR_PEERS_CONNECTED, MaxPexPeerCount); + auto added = std::vector{}; + added.reserve(std::size(new_pex)); + std::set_difference( + std::begin(new_pex), + std::end(new_pex), + std::begin(old_pex), + std::end(old_pex), + std::back_inserter(added)); + auto dropped = std::vector{}; + dropped.reserve(std::size(old_pex)); + std::set_difference( + std::begin(old_pex), + std::end(old_pex), + std::begin(new_pex), + std::end(new_pex), + std::back_inserter(dropped)); + + // Some peers give us error messages if we send + // more than this many peers in a single pex message. + // https://wiki.theory.org/BitTorrentPeerExchangeConventions + added.resize(std::min(std::size(added), MaxPexAdded)); + dropped.resize(std::min(std::size(dropped), MaxPexDropped)); + + logtrace( + this, + fmt::format( + FMT_STRING("pex: old {:s} peer count {:d}, new peer count {:d}, added {:d}, dropped {:d}"), + tr_ip_protocol_sv(ip_type), + std::size(old_pex), + std::size(new_pex), + std::size(added), + std::size(dropped))); + + // if there's nothing to send, then we're done + if (std::empty(added) && std::empty(dropped)) { - *walk++ = std::byte{ p.flags } & ~std::byte{ ADDED_F_HOLEPUNCH }; + continue; } - TR_ASSERT(static_cast(walk - begin) == std::size(added)); - tr_variantDictAddRaw(&val, TR_KEY_added_f, begin, walk - begin); - } + // update msgs + std::swap(old_pex, new_pex); - if (!std::empty(dropped)) - { - // "dropped" - tmpbuf.clear(); - tr_pex::to_compact_ipv4(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)) - { - tmpbuf.clear(); - tr_pex::to_compact_ipv6(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)); - auto* begin = std::data(tmpbuf); - auto* walk = begin; - for (auto const& p : added6) + // build the pex payload + if (!std::empty(added)) { - *walk++ = std::byte{ p.flags } & ~std::byte{ ADDED_F_HOLEPUNCH }; + // "added" + tmpbuf.clear(); + tmpbuf.reserve(std::size(added) * tr_socket_address::CompactSockAddrBytes[i]); + tr_pex::to_compact(std::back_inserter(tmpbuf), std::data(added), std::size(added), ip_type); + TR_ASSERT(std::size(tmpbuf) == std::size(added) * tr_socket_address::CompactSockAddrBytes[i]); + tr_variantDictAddRaw(&val, AddedMap[i], std::data(tmpbuf), std::size(tmpbuf)); + + // "added.f" + tmpbuf.resize(std::size(added)); + auto* begin = std::data(tmpbuf); + auto* walk = begin; + for (auto const& p : added) + { + *walk++ = std::byte{ p.flags }; + } + + TR_ASSERT(static_cast(walk - begin) == std::size(added)); + tr_variantDictAddRaw(&val, AddedFMap[i], begin, walk - begin); } - TR_ASSERT(static_cast(walk - begin) == std::size(added6)); - tr_variantDictAddRaw(&val, TR_KEY_added6_f, begin, walk - begin); - } - - if (!std::empty(dropped6)) - { - // "dropped6" - tmpbuf.clear(); - tr_pex::to_compact_ipv6(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)); + if (!std::empty(dropped)) + { + // "dropped" + tmpbuf.clear(); + tmpbuf.reserve(std::size(dropped) * tr_socket_address::CompactSockAddrBytes[i]); + tr_pex::to_compact(std::back_inserter(tmpbuf), std::data(dropped), std::size(dropped), ip_type); + TR_ASSERT(std::size(tmpbuf) == std::size(dropped) * tr_socket_address::CompactSockAddrBytes[i]); + tr_variantDictAddRaw(&val, DroppedMap[i], std::data(tmpbuf), std::size(tmpbuf)); + } } protocol_send_message(this, BtPeerMsgs::Ltep, this->ut_pex_id, tr_variantToStr(&val, TR_VARIANT_FMT_BENC)); diff --git a/libtransmission/torrent-magnet.cc b/libtransmission/torrent-magnet.cc index f8fb4999c..45030335e 100644 --- a/libtransmission/torrent-magnet.cc +++ b/libtransmission/torrent-magnet.cc @@ -65,7 +65,7 @@ bool tr_torrentSetMetadataSizeHint(tr_torrent* tor, int64_t size) return false; } - int const n = (size <= 0 || size > INT_MAX) ? -1 : div_ceil(size, METADATA_PIECE_SIZE); + int const n = (size <= 0 || size > INT_MAX) ? -1 : div_ceil(size, MetadataPieceSize); tr_logAddDebugTor(tor, fmt::format("metadata is {} bytes in {} pieces", size, n)); if (n <= 0) { @@ -97,7 +97,7 @@ bool tr_torrentGetMetadataPiece(tr_torrent const* tor, int piece, tr_metadata_pi return {}; } - auto const n_pieces = std::max(1, div_ceil(tor->info_dict_size(), METADATA_PIECE_SIZE)); + auto const n_pieces = std::max(1, div_ceil(tor->info_dict_size(), MetadataPieceSize)); if (piece < 0 || piece >= n_pieces) { return {}; @@ -111,14 +111,14 @@ bool tr_torrentGetMetadataPiece(tr_torrent const* tor, int piece, tr_metadata_pi auto const info_dict_size = tor->info_dict_size(); TR_ASSERT(info_dict_size > 0); - auto const offset_in_info_dict = static_cast(piece) * METADATA_PIECE_SIZE; + auto const offset_in_info_dict = static_cast(piece) * MetadataPieceSize; if (auto const offset_in_file = tor->info_dict_offset() + offset_in_info_dict; !in.seekg(offset_in_file)) { return {}; } - auto const piece_len = offset_in_info_dict + METADATA_PIECE_SIZE <= info_dict_size ? METADATA_PIECE_SIZE : - info_dict_size - offset_in_info_dict; + auto const piece_len = offset_in_info_dict + MetadataPieceSize <= info_dict_size ? MetadataPieceSize : + info_dict_size - offset_in_info_dict; setme.resize(piece_len); return !!in.read(reinterpret_cast(std::data(setme)), std::size(setme)); } @@ -155,8 +155,8 @@ namespace set_metadata_piece_helpers [[nodiscard]] constexpr size_t get_piece_length(tr_incomplete_metadata const& m, int piece) { return piece + 1 == m.piece_count ? // last piece - std::size(m.metadata) - (piece * METADATA_PIECE_SIZE) : - METADATA_PIECE_SIZE; + std::size(m.metadata) - (piece * MetadataPieceSize) : + MetadataPieceSize; } void build_metainfo_except_info_dict(tr_torrent_metainfo const& tm, tr_variant* top) @@ -346,7 +346,7 @@ void tr_torrentSetMetadataPiece(tr_torrent* tor, int piece, void const* data, si return; } - size_t const offset = piece * METADATA_PIECE_SIZE; + size_t const offset = piece * MetadataPieceSize; std::copy_n(reinterpret_cast(data), len, std::begin(m->metadata) + offset); needed.erase(iter); diff --git a/libtransmission/torrent-magnet.h b/libtransmission/torrent-magnet.h index 3841d38c0..796f1f3f9 100644 --- a/libtransmission/torrent-magnet.h +++ b/libtransmission/torrent-magnet.h @@ -23,9 +23,9 @@ struct tr_torrent; struct tr_torrent_metainfo; // defined by BEP #9 -inline constexpr int METADATA_PIECE_SIZE = 1024 * 16; +inline constexpr int MetadataPieceSize = 1024 * 16; -using tr_metadata_piece = small::max_size_vector; +using tr_metadata_piece = small::max_size_vector; struct tr_incomplete_metadata { diff --git a/tests/libtransmission/net-test.cc b/tests/libtransmission/net-test.cc index 20ca4d79e..ab489bc31 100644 --- a/tests/libtransmission/net-test.cc +++ b/tests/libtransmission/net-test.cc @@ -57,8 +57,10 @@ 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{ 0x0A }, std::byte{ 0x0A }, std::byte{ 0x0A }, - std::byte{ 0x05 }, std::byte{ 0x00 }, std::byte{ 0x80 } }; + static auto constexpr Compact4Bytes = tr_socket_address::CompactSockAddrBytes[TR_AF_INET]; + static auto constexpr Compact4 = std::array{ std::byte{ 0x0A }, std::byte{ 0x0A }, + std::byte{ 0x0A }, std::byte{ 0x05 }, + std::byte{ 0x00 }, std::byte{ 0x80 } }; /// compact <--> tr_address, port @@ -73,7 +75,7 @@ TEST_F(NetTest, compact4) EXPECT_EQ(ExpectedPort, port); // ...serialize it back again - auto compact4 = std::array{}; + auto compact4 = std::array{}; auto out = std::data(compact4); out = addr.to_compact_ipv4(out, port); EXPECT_EQ(std::size(Compact4), static_cast(out - std::data(compact4))); @@ -90,10 +92,13 @@ TEST_F(NetTest, compact4) compact4.fill(std::byte{}); out = std::data(compact4); out = addr.to_compact(out); - EXPECT_EQ(std::size(Compact4) - 2U, static_cast(out - std::data(compact4))); - EXPECT_TRUE(std::equal(std::data(Compact4), std::data(Compact4) + std::size(Compact4) - 2U, std::data(compact4))); + EXPECT_EQ(std::size(Compact4) - tr_port::CompactPortBytes, static_cast(out - std::data(compact4))); + EXPECT_TRUE(std::equal( + std::data(Compact4), + std::data(Compact4) + std::size(Compact4) - tr_port::CompactPortBytes, + std::data(compact4))); EXPECT_TRUE(std::all_of( - std::begin(compact4) + std::size(Compact4) - 2U, + std::begin(compact4) + std::size(Compact4) - tr_port::CompactPortBytes, std::end(compact4), [](std::byte const& byte) { return static_cast(byte) == 0U; })); @@ -126,7 +131,8 @@ 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{ + static auto constexpr Compact6Bytes = tr_socket_address::CompactSockAddrBytes[TR_AF_INET6]; + static auto constexpr Compact6 = std::array{ 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 } @@ -145,7 +151,7 @@ TEST_F(NetTest, compact6) EXPECT_EQ(ExpectedPort, port); // ...serialize it back again - auto compact6 = std::array{}; + auto compact6 = std::array{}; auto out = std::data(compact6); out = addr.to_compact_ipv6(out, port); EXPECT_EQ(std::size(Compact6), static_cast(out - std::data(compact6))); @@ -162,10 +168,13 @@ TEST_F(NetTest, compact6) compact6.fill(std::byte{}); out = std::data(compact6); out = addr.to_compact(out); - EXPECT_EQ(std::size(Compact6) - 2U, static_cast(out - std::data(compact6))); - EXPECT_TRUE(std::equal(std::data(Compact6), std::data(Compact6) + std::size(Compact6) - 2U, std::data(compact6))); + EXPECT_EQ(std::size(Compact6) - tr_port::CompactPortBytes, static_cast(out - std::data(compact6))); + EXPECT_TRUE(std::equal( + std::data(Compact6), + std::data(Compact6) + std::size(Compact6) - tr_port::CompactPortBytes, + std::data(compact6))); EXPECT_TRUE(std::all_of( - std::begin(compact6) + std::size(Compact6) - 2U, + std::begin(compact6) + std::size(Compact6) - tr_port::CompactPortBytes, std::end(compact6), [](std::byte const& byte) { return static_cast(byte) == 0U; }));