From d445c7f061eb952509827e3b42e15ede55420a6f Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 14 Apr 2023 16:03:08 -0500 Subject: [PATCH] fix: crash in peer stats (#5279) --- libtransmission/peer-common.h | 2 - libtransmission/peer-mgr.cc | 101 ++++++++++-------------- libtransmission/peer-msgs.cc | 143 ++++++++++++---------------------- libtransmission/peer-msgs.h | 133 +++++++++++++++++++++++++------ 4 files changed, 201 insertions(+), 178 deletions(-) diff --git a/libtransmission/peer-common.h b/libtransmission/peer-common.h index f83454e93..372685c57 100644 --- a/libtransmission/peer-common.h +++ b/libtransmission/peer-common.h @@ -274,8 +274,6 @@ struct tr_swarm_stats tr_swarm_stats tr_swarmGetStats(tr_swarm const* swarm); -void tr_swarmIncrementActivePeers(tr_swarm* swarm, tr_direction direction, bool is_active); - // --- #ifdef _WIN32 diff --git a/libtransmission/peer-mgr.cc b/libtransmission/peer-mgr.cc index dbaa07d97..23d36cb31 100644 --- a/libtransmission/peer-mgr.cc +++ b/libtransmission/peer-mgr.cc @@ -369,7 +369,7 @@ public: [&now](auto const& webseed) { return webseed->isTransferringPieces(now, TR_DOWN, nullptr); }); } - [[nodiscard]] auto peerCount() const noexcept + [[nodiscard]] TR_CONSTEXPR20 auto peerCount() const noexcept { return std::size(peers); } @@ -977,7 +977,7 @@ namespace { namespace handshake_helpers { -void create_bit_torrent_peer(tr_torrent* tor, std::shared_ptr io, struct peer_atom* atom, tr_quark client) +void create_bit_torrent_peer(tr_torrent* tor, std::shared_ptr io, struct peer_atom* atom, tr_interned_string client) { TR_ASSERT(atom != nullptr); TR_ASSERT(tr_isTorrent(tor)); @@ -985,8 +985,7 @@ void create_bit_torrent_peer(tr_torrent* tor, std::shared_ptr io, str tr_swarm* swarm = tor->swarm; - auto* peer = tr_peerMsgsNew(tor, atom, std::move(io), &tr_swarm::peerCallbackFunc, swarm); - peer->client = client; + auto* peer = tr_peerMsgsNew(tor, atom, std::move(io), client, &tr_swarm::peerCallbackFunc, swarm); atom->is_connected = true; swarm->peers.push_back(peer); @@ -996,11 +995,6 @@ void create_bit_torrent_peer(tr_torrent* tor, std::shared_ptr io, str TR_ASSERT(swarm->stats.peer_count == swarm->peerCount()); TR_ASSERT(swarm->stats.peer_from_count[atom->fromFirst] <= swarm->stats.peer_count); - - // TODO is this needed? - // isn't it already initialized in tr_peerMsgsImpl's ctor? - peer->update_active(TR_UP); - peer->update_active(TR_DOWN); } /* FIXME: this is kind of a mess. */ @@ -1084,12 +1078,12 @@ void create_bit_torrent_peer(tr_torrent* tor, std::shared_ptr io, str } else { - auto client = tr_quark{ TR_KEY_NONE }; + auto client = tr_interned_string{}; if (result.peer_id) { auto buf = std::array{}; tr_clientForId(std::data(buf), sizeof(buf), *result.peer_id); - client = tr_quark_new(std::data(buf)); + client = tr_interned_string{ tr_quark_new(std::data(buf)) }; } result.io->set_bandwidth(&s->tor->bandwidth_); @@ -1324,10 +1318,11 @@ std::vector tr_peerMgrGetPeers(tr_torrent const* tor, uint8_t address_ty auto atoms = std::vector{}; if (list_mode == TR_PEERS_CONNECTED) /* connected peers only */ { - atoms.reserve(s->peerCount()); + auto const& peers = s->peers; + atoms.reserve(std::size(peers)); std::transform( - std::begin(s->peers), - std::end(s->peers), + std::begin(peers), + std::end(peers), std::back_inserter(atoms), [](auto const* peer) { return peer->atom; }); } @@ -1421,13 +1416,6 @@ void tr_peerMgrOnTorrentGotMetainfo(tr_torrent* tor) swarm->mark_atom_as_seed(*peer->atom); } } - - /* update the bittorrent peers' willingness... */ - for (auto* peer : swarm->peers) - { - peer->update_active(TR_UP); - peer->update_active(TR_DOWN); - } } int8_t tr_peerMgrPieceAvailability(tr_torrent const* tor, tr_piece_index_t piece) @@ -1462,33 +1450,25 @@ void tr_peerMgrTorrentAvailability(tr_torrent const* tor, int8_t* tab, unsigned } } -tr_swarm_stats tr_swarmGetStats(tr_swarm const* swarm) +tr_swarm_stats tr_swarmGetStats(tr_swarm const* const swarm) { TR_ASSERT(swarm != nullptr); + + auto count_active_peers = [&swarm](tr_direction dir) + { + return std::count_if( + std::begin(swarm->peers), + std::end(swarm->peers), + [dir](auto const& peer) { return peer->is_active(dir); }); + }; + auto& stats = swarm->stats; + stats.active_peer_count[TR_UP] = count_active_peers(TR_UP); + stats.active_peer_count[TR_DOWN] = count_active_peers(TR_DOWN); stats.active_webseed_count = swarm->countActiveWebseeds(tr_time_msec()); return stats; } -void tr_swarmIncrementActivePeers(tr_swarm* swarm, tr_direction direction, bool is_active) -{ - int n = swarm->stats.active_peer_count[direction]; - - if (is_active) - { - ++n; - } - else - { - --n; - } - - TR_ASSERT(n >= 0); - TR_ASSERT(n <= swarm->stats.peer_count); - - swarm->stats.active_peer_count[direction] = n; -} - /* count how many bytes we want that connected peers have */ uint64_t tr_peerMgrGetDesiredAvailable(tr_torrent const* tor) { @@ -1502,7 +1482,7 @@ uint64_t tr_peerMgrGetDesiredAvailable(tr_torrent const* tor) } tr_swarm const* const swarm = tor->swarm; - if (swarm == nullptr || swarm->peerCount() == 0U) + if (swarm == nullptr || std::empty(swarm->peers)) { return 0; } @@ -1547,7 +1527,7 @@ namespace namespace peer_stat_helpers { -[[nodiscard]] auto getPeerStats(tr_peerMsgs const* peer, time_t now, uint64_t now_msec) +[[nodiscard]] auto get_peer_stats(tr_peerMsgs const* peer, time_t now, uint64_t now_msec) { auto stats = tr_peer_stat{}; auto const* const atom = peer->atom; @@ -1555,7 +1535,7 @@ namespace peer_stat_helpers auto const [addr, port] = peer->socketAddress(); addr.display_name(stats.addr, sizeof(stats.addr)); - stats.client = peer->client.c_str(); + stats.client = peer->user_agent().c_str(); stats.port = port.host(); stats.from = atom->fromFirst; stats.progress = peer->percentDone(); @@ -1563,10 +1543,10 @@ namespace peer_stat_helpers stats.isEncrypted = peer->is_encrypted(); stats.rateToPeer_KBps = tr_toSpeedKBps(peer->get_piece_speed_bytes_per_second(now_msec, TR_CLIENT_TO_PEER)); stats.rateToClient_KBps = tr_toSpeedKBps(peer->get_piece_speed_bytes_per_second(now_msec, TR_PEER_TO_CLIENT)); - stats.peerIsChoked = peer->is_peer_choked(); - stats.peerIsInterested = peer->is_peer_interested(); - stats.clientIsChoked = peer->is_client_choked(); - stats.clientIsInterested = peer->is_client_interested(); + stats.peerIsChoked = peer->peer_is_choked(); + stats.peerIsInterested = peer->peer_is_interested(); + stats.clientIsChoked = peer->client_is_choked(); + stats.clientIsInterested = peer->client_is_interested(); stats.isIncoming = peer->is_incoming_connection(); stats.isDownloadingFrom = peer->is_active(TR_PEER_TO_CLIENT); stats.isUploadingTo = peer->is_active(TR_CLIENT_TO_PEER); @@ -1654,16 +1634,17 @@ tr_peer_stat* tr_peerMgrPeerStats(tr_torrent const* tor, size_t* setme_count) TR_ASSERT(tr_isTorrent(tor)); TR_ASSERT(tor->swarm->manager != nullptr); - auto const n = tor->swarm->peerCount(); + auto const peers = tor->swarm->peers; + auto const n = std::size(peers); auto* const ret = new tr_peer_stat[n]; auto const now = tr_time(); auto const now_msec = tr_time_msec(); std::transform( - std::begin(tor->swarm->peers), - std::end(tor->swarm->peers), + std::begin(peers), + std::end(peers), ret, - [&now, &now_msec](auto const* peer) { return getPeerStats(peer, now, now_msec); }); + [&now, &now_msec](auto const* peer) { return get_peer_stats(peer, now, now_msec); }); *setme_count = n; return ret; @@ -1718,7 +1699,7 @@ void updateInterest(tr_swarm* swarm) return; } - if (auto const peer_count = swarm->peerCount(); peer_count > 0) + if (auto const& peers = swarm->peers; !std::empty(peers)) { int const n = tor->pieceCount(); @@ -1730,7 +1711,7 @@ void updateInterest(tr_swarm* swarm) piece_is_interesting[i] = tor->pieceIsWanted(i) && !tor->hasPiece(i); } - for (auto* const peer : swarm->peers) + for (auto* const peer : peers) { peer->set_interested(isPeerInteresting(tor, piece_is_interesting, peer)); } @@ -1818,8 +1799,8 @@ void rechokeUploads(tr_swarm* s, uint64_t const now) { auto const lock = s->unique_lock(); - auto const peer_count = s->peerCount(); auto& peers = s->peers; + auto const peer_count = std::size(peers); auto choked = std::vector{}; choked.reserve(peer_count); auto const* const session = s->manager->session; @@ -1857,8 +1838,8 @@ void rechokeUploads(tr_swarm* s, uint64_t const now) peer, getRateBps(s->tor, peer, now), salter(), - peer->is_peer_interested(), - peer->is_peer_choked(), + peer->peer_is_interested(), + peer->peer_is_choked(), true); } } @@ -2071,9 +2052,11 @@ struct ComparePeerByActivity [[nodiscard]] auto getPeersToClose(tr_swarm const* const swarm, time_t const now_sec) { - auto peers_to_close = std::vector{}; + auto const& peers = swarm->peers; + auto const peer_count = std::size(peers); - auto const peer_count = swarm->peerCount(); + auto peers_to_close = std::vector{}; + peers_to_close.reserve(peer_count); for (auto* peer : swarm->peers) { if (shouldPeerBeClosed(swarm, peer, peer_count, now_sec)) diff --git a/libtransmission/peer-msgs.cc b/libtransmission/peer-msgs.cc index bb515cdf1..4eed0b0b1 100644 --- a/libtransmission/peer-msgs.cc +++ b/libtransmission/peer-msgs.cc @@ -261,7 +261,7 @@ void updateDesiredRequestCount(tr_peerMsgsImpl* msgs); __FILE__, \ __LINE__, \ (level), \ - fmt::format(FMT_STRING("{:s} [{:s}]: {:s}"), (msgs)->io->display_name(), (msgs)->client, text), \ + fmt::format(FMT_STRING("{:s} [{:s}]: {:s}"), (msgs)->io->display_name(), (msgs)->user_agent().sv(), text), \ (msgs)->torrent->name()); \ } \ } while (0) @@ -270,6 +270,8 @@ void updateDesiredRequestCount(tr_peerMsgsImpl* msgs); #define logtrace(msgs, text) myLogMacro(msgs, TR_LOG_TRACE, text) #define logwarn(msgs, text) myLogMacro(msgs, TR_LOG_WARN, text) +using ReadResult = std::pair; + /** * Low-level communication state information about a connected peer. * @@ -291,9 +293,10 @@ public: tr_torrent* torrent_in, peer_atom* atom_in, std::shared_ptr io_in, + tr_interned_string client, tr_peer_callback callback, void* callback_data) - : tr_peerMsgs{ torrent_in, atom_in } + : tr_peerMsgs{ torrent_in, atom_in, client, io_in->is_encrypted(), io_in->is_incoming(), io_in->is_utp() } , outMessagesBatchPeriod{ LowPriorityIntervalSecs } , torrent{ torrent_in } , io{ std::move(io_in) } @@ -331,6 +334,8 @@ public: io->set_callbacks(canRead, didWrite, gotError, this); updateDesiredRequestCount(this); + + update_active(); } tr_peerMsgsImpl(tr_peerMsgsImpl&&) = delete; @@ -391,61 +396,11 @@ public: } } - [[nodiscard]] bool is_peer_choked() const noexcept override - { - return peer_is_choked_; - } - - [[nodiscard]] bool is_peer_interested() const noexcept override - { - return peer_is_interested_; - } - - [[nodiscard]] bool is_client_choked() const noexcept override - { - return client_is_choked_; - } - - [[nodiscard]] bool is_client_interested() const noexcept override - { - return client_is_interested_; - } - - [[nodiscard]] bool is_utp_connection() const noexcept override - { - return io->is_utp(); - } - - [[nodiscard]] bool is_encrypted() const override - { - return io->is_encrypted(); - } - - [[nodiscard]] bool is_incoming_connection() const override - { - return io->is_incoming(); - } - [[nodiscard]] tr_bandwidth& bandwidth() noexcept override { return io->bandwidth(); } - [[nodiscard]] bool is_active(tr_direction direction) const override - { - TR_ASSERT(tr_isDirection(direction)); - auto const active = is_active_[direction]; - TR_ASSERT(active == calculate_active(direction)); - return active; - } - - void update_active(tr_direction direction) override - { - TR_ASSERT(tr_isDirection(direction)); - - set_active(direction, calculate_active(direction)); - } - [[nodiscard]] std::pair socketAddress() const override { return io->socket_address(); @@ -465,6 +420,8 @@ public: void onTorrentGotMetainfo() noexcept override { invalidatePercentDone(); + + update_active(); } void invalidatePercentDone() @@ -486,16 +443,16 @@ public: { // TODO logtrace(msgs, "Not changing choke to %d to avoid fibrillation", peer_is_choked); } - else if (peer_is_choked_ != peer_is_choked) + else if (this->peer_is_choked() != peer_is_choked) { - peer_is_choked_ = peer_is_choked; + set_peer_choked(peer_is_choked); - if (peer_is_choked_) + if (peer_is_choked) { cancelAllRequestsToClient(this); } - protocolSendChoke(this, peer_is_choked_); + protocolSendChoke(this, peer_is_choked); chokeChangedAt = now; update_active(TR_CLIENT_TO_PEER); } @@ -516,9 +473,9 @@ public: void set_interested(bool interested) override { - if (client_is_interested_ != interested) + if (client_is_interested() != interested) { - client_is_interested_ = interested; + set_client_interested(interested); sendInterest(this, interested); update_active(TR_PEER_TO_CLIENT); } @@ -539,8 +496,8 @@ public: void requestBlocks(tr_block_span_t const* block_spans, size_t n_spans) override { TR_ASSERT(torrent->clientCanDownload()); - TR_ASSERT(is_client_interested()); - TR_ASSERT(!is_client_choked()); + TR_ASSERT(client_is_interested()); + TR_ASSERT(!client_is_choked()); for (auto const *span = block_spans, *span_end = span + n_spans; span != span_end; ++span) { @@ -587,7 +544,7 @@ public: private: [[nodiscard]] size_t maxAvailableReqs() const { - if (torrent->isDone() || !torrent->hasMetainfo() || client_is_choked_ || !client_is_interested_) + if (torrent->isDone() || !torrent->hasMetainfo() || client_is_choked() || !client_is_interested()) { return 0; } @@ -644,7 +601,7 @@ private: { if (direction == TR_CLIENT_TO_PEER) { - return is_peer_interested() && !is_peer_choked(); + return peer_is_interested() && !peer_is_choked(); } // TR_PEER_TO_CLIENT @@ -654,36 +611,25 @@ private: return true; } - auto const active = is_client_interested() && !is_client_choked(); + auto const active = client_is_interested() && !client_is_choked(); TR_ASSERT(!active || !torrent->isDone()); return active; } - void set_active(tr_direction direction, bool active) + void update_active() { - // TODO logtrace(msgs, "direction [%d] is_active [%d]", int(direction), int(is_active)); - auto& val = is_active_[direction]; - if (val != active) - { - val = active; + update_active(TR_UP); + update_active(TR_DOWN); + } - tr_swarmIncrementActivePeers(torrent->swarm, direction, active); - } + void update_active(tr_direction direction) + { + TR_ASSERT(tr_isDirection(direction)); + + set_active(direction, calculate_active(direction)); } public: - /* Whether or not we've choked this peer. */ - bool peer_is_choked_ = true; - - /* whether or not the peer has indicated it will download from us. */ - bool peer_is_interested_ = false; - - /* whether or not the peer is choking us. */ - bool client_is_choked_ = true; - - /* whether or not we've indicated to the peer that we would download from them if unchoked. */ - bool client_is_interested_ = false; - bool peerSupportsPex = false; bool peerSupportsMetadataXfer = false; bool clientSentLtepHandshake = false; @@ -746,7 +692,8 @@ public: tr_bitfield have_; private: - std::array is_active_ = { false, false }; + friend ReadResult process_peer_message(tr_peerMsgsImpl* msgs, uint8_t id, libtransmission::Buffer& payload); + friend void parseLtepHandshake(tr_peerMsgsImpl* msgs, libtransmission::Buffer& payload); tr_peer_callback const callback_; void* const callback_data_; @@ -1086,6 +1033,15 @@ void parseLtepHandshake(tr_peerMsgsImpl* msgs, libtransmission::Buffer& payload) pex.flags |= ADDED_F_SEED_FLAG; } + // http://bittorrent.org/beps/bep_0010.html + // Client name and version (as a utf-8 string). This is a much more + // reliable way of identifying the client than relying on the + // peer id encoding. + if (auto sv = std::string_view{}; tr_variantDictFindStrView(&val, TR_KEY_v, &sv)) + { + msgs->set_user_agent(tr_interned_string{ sv }); + } + /* get peer's listening port */ if (tr_variantDictFindInt(&val, TR_KEY_p, &i)) { @@ -1269,8 +1225,6 @@ void parseLtep(tr_peerMsgsImpl* msgs, libtransmission::Buffer& payload) } } -using ReadResult = std::pair; - ReadResult process_peer_message(tr_peerMsgsImpl* msgs, uint8_t id, libtransmission::Buffer& payload); void prefetchPieces(tr_peerMsgsImpl* msgs) @@ -1294,7 +1248,7 @@ void prefetchPieces(tr_peerMsgsImpl* msgs) [[nodiscard]] bool canAddRequestFromPeer(tr_peerMsgsImpl const* const msgs, struct peer_request const& req) { - if (msgs->peer_is_choked_) + if (msgs->peer_is_choked()) { logtrace(msgs, "rejecting request from choked peer"); return false; @@ -1461,7 +1415,7 @@ ReadResult process_peer_message(tr_peerMsgsImpl* msgs, uint8_t id, libtransmissi { case BtPeerMsgs::Choke: logtrace(msgs, "got Choke"); - msgs->client_is_choked_ = true; + msgs->set_client_choked(true); if (!fext) { @@ -1473,20 +1427,20 @@ ReadResult process_peer_message(tr_peerMsgsImpl* msgs, uint8_t id, libtransmissi case BtPeerMsgs::Unchoke: logtrace(msgs, "got Unchoke"); - msgs->client_is_choked_ = false; + msgs->set_client_choked(false); msgs->update_active(TR_PEER_TO_CLIENT); updateDesiredRequestCount(msgs); break; case BtPeerMsgs::Interested: logtrace(msgs, "got Interested"); - msgs->peer_is_interested_ = true; + msgs->set_peer_interested(true); msgs->update_active(TR_CLIENT_TO_PEER); break; case BtPeerMsgs::NotInterested: logtrace(msgs, "got Not Interested"); - msgs->peer_is_interested_ = false; + msgs->set_peer_interested(false); msgs->update_active(TR_CLIENT_TO_PEER); break; @@ -1888,8 +1842,8 @@ void updateBlockRequests(tr_peerMsgsImpl* msgs) return; } - TR_ASSERT(msgs->is_client_interested()); - TR_ASSERT(!msgs->is_client_choked()); + TR_ASSERT(msgs->client_is_interested()); + TR_ASSERT(!msgs->client_is_choked()); if (auto const requests = tr_peerMgrGetNextRequests(tor, msgs, n_wanted); !std::empty(requests)) { @@ -2272,8 +2226,9 @@ tr_peerMsgs* tr_peerMsgsNew( tr_torrent* torrent, peer_atom* atom, std::shared_ptr io, + tr_interned_string user_agent, tr_peer_callback callback, void* callback_data) { - return new tr_peerMsgsImpl(torrent, atom, std::move(io), callback, callback_data); + return new tr_peerMsgsImpl(torrent, atom, std::move(io), user_agent, callback, callback_data); } diff --git a/libtransmission/peer-msgs.h b/libtransmission/peer-msgs.h index c1b50d6fd..4fb6a99fd 100644 --- a/libtransmission/peer-msgs.h +++ b/libtransmission/peer-msgs.h @@ -10,19 +10,16 @@ #endif #include -#include // int8_t -#include // size_t -#include // time_t +#include // for size_t #include -#include +#include // for std::pair<> -#include "bitfield.h" -#include "peer-common.h" -#include "torrent.h" +#include "peer-common.h" // for tr_peer class tr_peer; class tr_peerIo; struct tr_address; +struct tr_torrent; /** * @addtogroup peers Peers @@ -32,31 +29,73 @@ struct tr_address; class tr_peerMsgs : public tr_peer { public: - tr_peerMsgs(tr_torrent const* tor, peer_atom* atom_in) + tr_peerMsgs( + tr_torrent const* tor, + peer_atom* atom_in, + tr_interned_string user_agent, + bool connection_is_encrypted, + bool connection_is_incoming, + bool connection_is_utp) : tr_peer{ tor, atom_in } - , have_{ tor->pieceCount() } + , user_agent_{ user_agent } + , connection_is_encrypted_{ connection_is_encrypted } + , connection_is_incoming_{ connection_is_incoming } + , connection_is_utp_{ connection_is_utp } { ++n_peers; } virtual ~tr_peerMsgs() override; - [[nodiscard]] static size_t size() noexcept + [[nodiscard]] static auto size() noexcept { return n_peers.load(); } - [[nodiscard]] virtual bool is_peer_choked() const noexcept = 0; - [[nodiscard]] virtual bool is_peer_interested() const noexcept = 0; - [[nodiscard]] virtual bool is_client_choked() const noexcept = 0; - [[nodiscard]] virtual bool is_client_interested() const noexcept = 0; + [[nodiscard]] constexpr auto client_is_choked() const noexcept + { + return client_is_choked_; + } - [[nodiscard]] virtual bool is_utp_connection() const noexcept = 0; - [[nodiscard]] virtual bool is_encrypted() const = 0; - [[nodiscard]] virtual bool is_incoming_connection() const = 0; + [[nodiscard]] constexpr auto client_is_interested() const noexcept + { + return client_is_interested_; + } - [[nodiscard]] virtual bool is_active(tr_direction direction) const = 0; - virtual void update_active(tr_direction direction) = 0; + [[nodiscard]] constexpr auto peer_is_choked() const noexcept + { + return peer_is_choked_; + } + + [[nodiscard]] constexpr auto peer_is_interested() const noexcept + { + return peer_is_interested_; + } + + [[nodiscard]] constexpr auto is_encrypted() const noexcept + { + return connection_is_encrypted_; + } + + [[nodiscard]] constexpr auto is_incoming_connection() const noexcept + { + return connection_is_incoming_; + } + + [[nodiscard]] constexpr auto is_utp_connection() const noexcept + { + return connection_is_utp_; + } + + [[nodiscard]] constexpr auto const& user_agent() const noexcept + { + return user_agent_; + } + + [[nodiscard]] constexpr auto is_active(tr_direction direction) const noexcept + { + return is_active_[direction]; + } [[nodiscard]] virtual std::pair socketAddress() const = 0; @@ -71,20 +110,68 @@ public: virtual void on_piece_completed(tr_piece_index_t) = 0; - /// The client name. This is the app name derived from the `v` string in LTEP's handshake dictionary - tr_interned_string client; - protected: - tr_bitfield have_; + constexpr void set_client_choked(bool val) noexcept + { + client_is_choked_ = val; + } + + constexpr void set_client_interested(bool val) noexcept + { + client_is_interested_ = val; + } + + constexpr void set_peer_choked(bool val) noexcept + { + peer_is_choked_ = val; + } + + constexpr void set_peer_interested(bool val) noexcept + { + peer_is_interested_ = val; + } + + constexpr void set_active(tr_direction direction, bool active) noexcept + { + is_active_[direction] = active; + } + + constexpr void set_user_agent(tr_interned_string val) noexcept + { + user_agent_ = val; + } private: static inline auto n_peers = std::atomic{}; + + // What software the peer is running. + // Derived from the `v` string in LTEP's handshake dictionary, when available. + tr_interned_string user_agent_; + + bool const connection_is_encrypted_; + bool const connection_is_incoming_; + bool const connection_is_utp_; + + std::array is_active_ = {}; + + // whether or not the peer is choking us. + bool client_is_choked_ = true; + + // whether or not we've indicated to the peer that we would download from them if unchoked + bool client_is_interested_ = false; + + // whether or not we've choked this peer + bool peer_is_choked_ = true; + + // whether or not the peer has indicated it will download from us + bool peer_is_interested_ = false; }; tr_peerMsgs* tr_peerMsgsNew( tr_torrent* torrent, peer_atom* atom, std::shared_ptr io, + tr_interned_string user_agent, tr_peer_callback callback, void* callback_data);