mirror of
https://github.com/transmission/transmission
synced 2024-12-21 23:32:35 +00:00
fix: various pex flag bugs and cleanup (#6917)
* fix: allow connection between seeds when pex is enabled * chore: add comment to explain `tr_peerMsgs::on_torrent_got_metainfo()` * refactor: remove `tr_swarm::mark_peer_as_seed()` * fix: update seed flag in response to BT msgs Regression from81a42c6bb6
* chore: remove redundant code to update peer seed flag * refactor: inc failure count if there were no piece data exchanged * fix: save information from ltep handshake * refactor: rename `tr_peerIo::is_seed_` to disambiguate * fix: add instead of set pex flags when adding non-pex and non-resume peers * fix: don't mark peer as connectable on getting ltep port msg By BEP-11's definition, this flag is only set for peers whom we successfully initiated an outgoing connection with. * refactor: set holepunch flag when we get it from ltep handshake * fix: only accept positive `reqq` in ltep handshake * refactor: handle encryption preference in `tr_peer_info` * refactor: prefer own value for utp support * refactor: make `tr_peer_info::from_first_` const * refactor: handle holepunch support in `tr_peer_info` * fix: parse metadata size only if we have a valid extention id for metadata xfer * refactor: remove `tr_peer_info::add_pex_flags()` as it's no longer needed * fix: correctly handle holepunch support when there is no `m` key in ltep handshake * fix: distinguish between upload only and seed Say we just connected to a partial seed, the peer sends an ltep handshake that has the `upload_only` key, then a BT `Bitfield` message: Without this change, the pex seed flag would be set when parsing the ltep handshake, then immediately unset when parsing the `Bitfield` message. We don't want that. * fix: don't update `tr_peer_info::is_seed_` when merging peer info objects * perf: priority in peer candidate score need 2 bits only * fix: prefer to connect to downloading peers Regression fromc867f00153
* chore: add TODO for C++20 opportunity * refactor: don't filter out peers without `ADDED_F_CONNECTABLE` revert change froma2849219f7
* refactor: move peer state updates out of peermgr code --------- Co-authored-by: Charles Kerr <charles@charleskerr.com>
This commit is contained in:
parent
7c7046be6e
commit
9ff95d162e
9 changed files with 219 additions and 130 deletions
|
@ -690,7 +690,11 @@ void publishError(tr_tier* tier, std::string_view msg)
|
|||
publishMessage(tier, msg, tr_tracker_event::Type::Error);
|
||||
}
|
||||
|
||||
void publishPeerCounts(tr_tier* tier, std::optional<int64_t> seeders, std::optional<int64_t> leechers)
|
||||
void publishPeerCounts(
|
||||
tr_tier* tier,
|
||||
std::optional<int64_t> seeders,
|
||||
std::optional<int64_t> leechers,
|
||||
std::optional<int64_t> downloaders)
|
||||
{
|
||||
if (tier->tor->torrent_announcer->callback != nullptr)
|
||||
{
|
||||
|
@ -698,9 +702,14 @@ void publishPeerCounts(tr_tier* tier, std::optional<int64_t> seeders, std::optio
|
|||
e.type = tr_tracker_event::Type::Counts;
|
||||
e.seeders = seeders;
|
||||
e.leechers = leechers;
|
||||
e.downloaders = downloaders;
|
||||
tr_logAddDebugTier(
|
||||
tier,
|
||||
fmt::format("peer counts: {} seeders, {} leechers.", seeders.value_or(-1), leechers.value_or(-1)));
|
||||
fmt::format(
|
||||
"peer counts: {} seeders, {} leechers, {} downloaders.",
|
||||
seeders.value_or(-1),
|
||||
leechers.value_or(-1),
|
||||
downloaders.value_or(-1)));
|
||||
|
||||
tier->tor->torrent_announcer->callback(*tier->tor, &e);
|
||||
}
|
||||
|
@ -1099,7 +1108,7 @@ void tr_announcer_impl::onAnnounceDone(
|
|||
publishPeersPex(tier, response.pex6);
|
||||
}
|
||||
|
||||
publishPeerCounts(tier, response.seeders, response.leechers);
|
||||
publishPeerCounts(tier, response.seeders, response.leechers, {});
|
||||
|
||||
tier->isRunning = is_running_on_success;
|
||||
|
||||
|
@ -1371,7 +1380,7 @@ void tr_announcer_impl::onScrapeDone(tr_scrape_response const& response)
|
|||
tracker->consecutive_failures = 0;
|
||||
}
|
||||
|
||||
publishPeerCounts(tier, row.seeders, row.leechers);
|
||||
publishPeerCounts(tier, row.seeders, row.leechers, row.downloaders);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -62,6 +62,7 @@ struct tr_tracker_event
|
|||
// for Peers and Counts events
|
||||
std::optional<int64_t> leechers;
|
||||
std::optional<int64_t> seeders;
|
||||
std::optional<int64_t> downloaders;
|
||||
};
|
||||
|
||||
using tr_tracker_callback = std::function<void(tr_torrent&, tr_tracker_event const*)>;
|
||||
|
|
|
@ -85,12 +85,12 @@ tr_peerIo::tr_peerIo(
|
|||
tr_session* session,
|
||||
tr_sha1_digest_t const* info_hash,
|
||||
bool is_incoming,
|
||||
bool is_seed,
|
||||
bool client_is_seed,
|
||||
tr_bandwidth* parent_bandwidth)
|
||||
: bandwidth_{ parent_bandwidth }
|
||||
, info_hash_{ info_hash != nullptr ? *info_hash : tr_sha1_digest_t{} }
|
||||
, session_{ session }
|
||||
, is_seed_{ is_seed }
|
||||
, client_is_seed_{ client_is_seed }
|
||||
, is_incoming_{ is_incoming }
|
||||
{
|
||||
}
|
||||
|
@ -125,7 +125,7 @@ std::shared_ptr<tr_peerIo> tr_peerIo::new_outgoing(
|
|||
tr_bandwidth* parent,
|
||||
tr_socket_address const& socket_address,
|
||||
tr_sha1_digest_t const& info_hash,
|
||||
bool is_seed,
|
||||
bool client_is_seed,
|
||||
bool utp)
|
||||
{
|
||||
using preferred_key_t = std::underlying_type_t<tr_preferred_transport>;
|
||||
|
@ -136,7 +136,7 @@ std::shared_ptr<tr_peerIo> tr_peerIo::new_outgoing(
|
|||
TR_ASSERT(socket_address.is_valid());
|
||||
TR_ASSERT(utp || session->allowsTCP());
|
||||
|
||||
auto peer_io = tr_peerIo::create(session, parent, &info_hash, false, is_seed);
|
||||
auto peer_io = tr_peerIo::create(session, parent, &info_hash, false, client_is_seed);
|
||||
auto const func = small::max_size_map<preferred_key_t, std::function<bool()>, TR_NUM_PREFERRED_TRANSPORT>{
|
||||
{ TR_PREFER_UTP,
|
||||
[&]()
|
||||
|
@ -162,7 +162,7 @@ std::shared_ptr<tr_peerIo> tr_peerIo::new_outgoing(
|
|||
{
|
||||
if (!peer_io->socket_.is_valid())
|
||||
{
|
||||
if (auto sock = tr_netOpenPeerSocket(session, socket_address, is_seed); sock.is_valid())
|
||||
if (auto sock = tr_netOpenPeerSocket(session, socket_address, client_is_seed); sock.is_valid())
|
||||
{
|
||||
peer_io->set_socket(std::move(sock));
|
||||
return true;
|
||||
|
@ -254,7 +254,7 @@ bool tr_peerIo::reconnect()
|
|||
return false;
|
||||
}
|
||||
|
||||
auto sock = tr_netOpenPeerSocket(session_, socket_address(), is_seed());
|
||||
auto sock = tr_netOpenPeerSocket(session_, socket_address(), client_is_seed());
|
||||
if (!sock.is_tcp())
|
||||
{
|
||||
return false;
|
||||
|
|
|
@ -68,7 +68,7 @@ public:
|
|||
tr_session* session_in,
|
||||
tr_sha1_digest_t const* info_hash,
|
||||
bool is_incoming,
|
||||
bool is_seed,
|
||||
bool client_is_seed,
|
||||
tr_bandwidth* parent_bandwidth);
|
||||
|
||||
~tr_peerIo();
|
||||
|
@ -78,7 +78,7 @@ public:
|
|||
tr_bandwidth* parent,
|
||||
tr_socket_address const& socket_address,
|
||||
tr_sha1_digest_t const& info_hash,
|
||||
bool is_seed,
|
||||
bool client_is_seed,
|
||||
bool utp);
|
||||
|
||||
static std::shared_ptr<tr_peerIo> new_incoming(tr_session* session, tr_bandwidth* parent, tr_peer_socket socket);
|
||||
|
@ -329,9 +329,9 @@ private:
|
|||
|
||||
friend class libtransmission::test::HandshakeTest;
|
||||
|
||||
[[nodiscard]] constexpr auto is_seed() const noexcept
|
||||
[[nodiscard]] constexpr auto client_is_seed() const noexcept
|
||||
{
|
||||
return is_seed_;
|
||||
return client_is_seed_;
|
||||
}
|
||||
|
||||
void call_error_callback(tr_error const& error)
|
||||
|
@ -398,7 +398,7 @@ private:
|
|||
|
||||
tr_priority_t priority_ = TR_PRI_NORMAL;
|
||||
|
||||
bool const is_seed_;
|
||||
bool const client_is_seed_;
|
||||
bool const is_incoming_;
|
||||
|
||||
bool utp_supported_ = false;
|
||||
|
|
|
@ -214,12 +214,25 @@ void tr_peer_info::merge(tr_peer_info& that) noexcept
|
|||
}
|
||||
}
|
||||
|
||||
set_utp_supported(supports_utp() || that.supports_utp());
|
||||
if (auto const& other = that.supports_utp(); !supports_utp().has_value() && other)
|
||||
{
|
||||
set_utp_supported(*other);
|
||||
}
|
||||
|
||||
if (auto const& other = that.prefers_encryption(); !prefers_encryption().has_value() && other)
|
||||
{
|
||||
set_encryption_preferred(*other);
|
||||
}
|
||||
|
||||
if (auto const& other = that.supports_holepunch(); !supports_holepunch().has_value() && other)
|
||||
{
|
||||
set_holepunch_supported(*other);
|
||||
}
|
||||
|
||||
/* from_first_ should never be modified */
|
||||
found_at(that.from_best());
|
||||
|
||||
/* num_consecutive_fails_ is already the latest */
|
||||
/* num_consecutive_fruitless_ is already the latest */
|
||||
pex_flags_ |= that.pex_flags_;
|
||||
|
||||
if (that.is_banned())
|
||||
|
@ -227,7 +240,8 @@ void tr_peer_info::merge(tr_peer_info& that) noexcept
|
|||
ban();
|
||||
}
|
||||
/* is_connected_ should already be set */
|
||||
set_seed(is_seed() || that.is_seed());
|
||||
/* keep is_seed_ as-is */
|
||||
/* keep upload_only_ as-is */
|
||||
|
||||
if (that.outgoing_handshake_)
|
||||
{
|
||||
|
@ -263,7 +277,7 @@ constexpr struct
|
|||
return val;
|
||||
}
|
||||
|
||||
return a.compare_by_failure_count(b);
|
||||
return a.compare_by_fruitless_count(b);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool operator()(tr_peer_info const& a, tr_peer_info const& b) const noexcept
|
||||
|
@ -347,7 +361,7 @@ public:
|
|||
tor_in->piece_completed_.observe([this](tr_torrent*, tr_piece_index_t p) { on_piece_completed(p); }),
|
||||
tor_in->started_.observe([this](tr_torrent*) { on_torrent_started(); }),
|
||||
tor_in->stopped_.observe([this](tr_torrent*) { on_torrent_stopped(); }),
|
||||
tor_in->swarm_is_all_seeds_.observe([this](tr_torrent* /*tor*/) { on_swarm_is_all_seeds(); }),
|
||||
tor_in->swarm_is_all_upload_only_.observe([this](tr_torrent* /*tor*/) { on_swarm_is_all_upload_only(); }),
|
||||
} }
|
||||
{
|
||||
rebuild_webseeds();
|
||||
|
@ -440,17 +454,17 @@ public:
|
|||
return is_endgame_;
|
||||
}
|
||||
|
||||
[[nodiscard]] TR_CONSTEXPR20 auto is_all_seeds() const noexcept
|
||||
[[nodiscard]] TR_CONSTEXPR20 auto is_all_upload_only() const noexcept
|
||||
{
|
||||
if (!pool_is_all_seeds_)
|
||||
if (!pool_is_all_upload_only_)
|
||||
{
|
||||
pool_is_all_seeds_ = std::all_of(
|
||||
pool_is_all_upload_only_ = std::all_of(
|
||||
std::begin(connectable_pool),
|
||||
std::end(connectable_pool),
|
||||
[](auto const& key_val) { return key_val.second->is_seed(); });
|
||||
[](auto const& key_val) { return key_val.second->is_upload_only(); });
|
||||
}
|
||||
|
||||
return *pool_is_all_seeds_;
|
||||
return *pool_is_all_upload_only_;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::shared_ptr<tr_peer_info> get_existing_peer_info(tr_socket_address const& socket_address) const noexcept
|
||||
|
@ -485,7 +499,7 @@ public:
|
|||
++stats.known_peer_from_count[from];
|
||||
}
|
||||
|
||||
mark_all_seeds_flag_dirty();
|
||||
mark_all_upload_only_flag_dirty();
|
||||
|
||||
return peer_info;
|
||||
}
|
||||
|
@ -501,42 +515,37 @@ public:
|
|||
{
|
||||
case tr_peer_event::Type::ClientSentPieceData:
|
||||
{
|
||||
auto const now = tr_time();
|
||||
auto* const tor = s->tor;
|
||||
|
||||
tor->bytes_uploaded_ += event.length;
|
||||
tr_announcerAddBytes(tor, TR_ANN_UP, event.length);
|
||||
tor->set_date_active(now);
|
||||
tor->set_date_active(tr_time());
|
||||
tor->session->add_uploaded(event.length);
|
||||
|
||||
msgs->peer_info->set_latest_piece_data_time(now);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case tr_peer_event::Type::ClientGotPieceData:
|
||||
{
|
||||
auto const now = tr_time();
|
||||
on_client_got_piece_data(s->tor, event.length, now);
|
||||
msgs->peer_info->set_latest_piece_data_time(now);
|
||||
}
|
||||
|
||||
on_client_got_piece_data(s->tor, event.length, tr_time());
|
||||
break;
|
||||
|
||||
case tr_peer_event::Type::ClientGotHave:
|
||||
s->got_have.emit(s->tor, event.pieceIndex);
|
||||
s->mark_all_upload_only_flag_dirty();
|
||||
break;
|
||||
|
||||
case tr_peer_event::Type::ClientGotHaveAll:
|
||||
s->got_have_all.emit(s->tor);
|
||||
s->mark_all_upload_only_flag_dirty();
|
||||
break;
|
||||
|
||||
case tr_peer_event::Type::ClientGotHaveNone:
|
||||
// no-op
|
||||
s->mark_all_upload_only_flag_dirty();
|
||||
break;
|
||||
|
||||
case tr_peer_event::Type::ClientGotBitfield:
|
||||
s->got_bitfield.emit(s->tor, msgs->has());
|
||||
s->mark_all_upload_only_flag_dirty();
|
||||
break;
|
||||
|
||||
case tr_peer_event::Type::ClientGotChoke:
|
||||
|
@ -617,13 +626,6 @@ public:
|
|||
tr_peerMsgs* optimistic = nullptr; /* the optimistic peer, or nullptr if none */
|
||||
|
||||
private:
|
||||
void mark_peer_as_seed(tr_peer_info& peer_info)
|
||||
{
|
||||
tr_logAddTraceSwarm(this, fmt::format("marking peer {} as a seed", peer_info.display_name()));
|
||||
peer_info.set_seed();
|
||||
mark_all_seeds_flag_dirty();
|
||||
}
|
||||
|
||||
void rebuild_webseeds()
|
||||
{
|
||||
auto const n = tor->webseed_count();
|
||||
|
@ -681,9 +683,9 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
void mark_all_seeds_flag_dirty() noexcept
|
||||
void mark_all_upload_only_flag_dirty() noexcept
|
||||
{
|
||||
pool_is_all_seeds_.reset();
|
||||
pool_is_all_upload_only_.reset();
|
||||
}
|
||||
|
||||
void on_torrent_doomed()
|
||||
|
@ -700,16 +702,16 @@ private:
|
|||
wishlist.reset();
|
||||
}
|
||||
|
||||
void on_swarm_is_all_seeds()
|
||||
void on_swarm_is_all_upload_only()
|
||||
{
|
||||
auto const lock = unique_lock();
|
||||
|
||||
for (auto const& [socket_address, peer_info] : connectable_pool)
|
||||
{
|
||||
mark_peer_as_seed(*peer_info);
|
||||
peer_info->set_upload_only();
|
||||
}
|
||||
|
||||
mark_all_seeds_flag_dirty();
|
||||
mark_all_upload_only_flag_dirty();
|
||||
}
|
||||
|
||||
void on_piece_completed(tr_piece_index_t piece)
|
||||
|
@ -770,16 +772,9 @@ private:
|
|||
// the webseed list may have changed...
|
||||
rebuild_webseeds();
|
||||
|
||||
// some peer_msgs' progress fields may not be accurate if we
|
||||
// didn't have the metadata before now... so refresh them all...
|
||||
for (auto* peer : peers)
|
||||
{
|
||||
peer->on_torrent_got_metainfo();
|
||||
|
||||
if (peer->is_seed())
|
||||
{
|
||||
mark_peer_as_seed(*peer->peer_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -867,11 +862,6 @@ private:
|
|||
// info_that will be replaced by info_this later, so decrement stat
|
||||
--stats.known_peer_from_count[info_that->from_first()];
|
||||
}
|
||||
// we are going to insert a brand-new peer info object to the pool
|
||||
else if (std::empty(info_this->listen_port()))
|
||||
{
|
||||
info_this->set_connectable();
|
||||
}
|
||||
|
||||
// erase the old peer info entry
|
||||
stats.known_peer_from_count[info_this->from_first()] -= connectable_pool.erase(info_this->listen_socket_address());
|
||||
|
@ -884,7 +874,7 @@ private:
|
|||
connectable_pool.insert_or_assign(info_this->listen_socket_address(), std::move(info_this));
|
||||
|
||||
EXIT:
|
||||
mark_all_seeds_flag_dirty();
|
||||
mark_all_upload_only_flag_dirty();
|
||||
}
|
||||
|
||||
bool on_got_port_duplicate_connection(tr_peerMsgs* const msgs, std::shared_ptr<tr_peer_info> info_that)
|
||||
|
@ -922,7 +912,7 @@ EXIT:
|
|||
|
||||
std::array<libtransmission::ObserverTag, 8> const tags_;
|
||||
|
||||
mutable std::optional<bool> pool_is_all_seeds_;
|
||||
mutable std::optional<bool> pool_is_all_upload_only_;
|
||||
|
||||
bool is_endgame_ = false;
|
||||
};
|
||||
|
@ -1302,16 +1292,16 @@ void create_bit_torrent_peer(
|
|||
{
|
||||
if (info && !info->is_connected())
|
||||
{
|
||||
info->on_connection_failed();
|
||||
info->on_fruitless_connection();
|
||||
|
||||
if (!result.read_anything_from_peer)
|
||||
{
|
||||
tr_logAddTraceSwarm(
|
||||
swarm,
|
||||
fmt::format(
|
||||
"marking peer {} as unreachable... num_fails is {}",
|
||||
"marking peer {} as unreachable... num_fruitless is {}",
|
||||
info->display_name(),
|
||||
info->connection_failure_count()));
|
||||
info->fruitless_connection_count()));
|
||||
info->set_connectable(false);
|
||||
}
|
||||
}
|
||||
|
@ -1410,10 +1400,8 @@ size_t tr_peerMgrAddPex(tr_torrent* tor, tr_peer_from from, tr_pex const* pex, s
|
|||
{
|
||||
if (tr_isPex(pex) && /* safeguard against corrupt data */
|
||||
!s->manager->blocklists_.contains(pex->socket_address.address()) && pex->is_valid_for_peers(from) &&
|
||||
from != TR_PEER_FROM_INCOMING && (from != TR_PEER_FROM_PEX || (pex->flags & ADDED_F_CONNECTABLE) != 0))
|
||||
from != TR_PEER_FROM_INCOMING)
|
||||
{
|
||||
// we store this peer since it is supposedly connectable (socket address should be the peer's listening address)
|
||||
// don't care about non-connectable peers that we are not connected to
|
||||
s->ensure_info_exists(pex->socket_address, pex->flags, from);
|
||||
++n_used;
|
||||
}
|
||||
|
@ -1477,7 +1465,7 @@ namespace get_peers_helpers
|
|||
|
||||
[[nodiscard]] bool is_peer_interesting(tr_torrent const* tor, tr_peer_info const& info)
|
||||
{
|
||||
if (tor->is_done() && info.is_seed())
|
||||
if (tor->is_done() && info.is_upload_only())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -2443,8 +2431,8 @@ namespace connect_helpers
|
|||
return false;
|
||||
}
|
||||
|
||||
// not if we're both seeds
|
||||
if (tor->is_done() && peer_info.is_seed())
|
||||
// not if we're both upload only and pex is disabled
|
||||
if (tor->is_done() && peer_info.is_upload_only() && !tor->allows_pex())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -2489,8 +2477,8 @@ namespace connect_helpers
|
|||
auto i = uint64_t{};
|
||||
auto score = uint64_t{};
|
||||
|
||||
/* prefer peers we've connected to, or never tried, over peers we failed to connect to. */
|
||||
i = peer_info.connection_failure_count() != 0U ? 1U : 0U;
|
||||
/* prefer peers we've exchanged piece data with, or never tried, over other peers. */
|
||||
i = peer_info.fruitless_connection_count() != 0U ? 1U : 0U;
|
||||
score = addValToKey(score, 1U, i);
|
||||
|
||||
/* prefer the one we attempted least recently (to cycle through all peers) */
|
||||
|
@ -2517,7 +2505,7 @@ namespace connect_helpers
|
|||
break;
|
||||
}
|
||||
|
||||
score = addValToKey(score, 4U, i);
|
||||
score = addValToKey(score, 2U, i);
|
||||
|
||||
// prefer recently-started torrents
|
||||
i = tor->started_recently(tr_time()) ? 0 : 1;
|
||||
|
@ -2532,12 +2520,12 @@ namespace connect_helpers
|
|||
score = addValToKey(score, 1U, i);
|
||||
|
||||
/* prefer peers that we might be able to upload to */
|
||||
i = peer_info.is_seed() ? 0 : 1;
|
||||
i = peer_info.is_upload_only() ? 1 : 0;
|
||||
score = addValToKey(score, 1U, i);
|
||||
|
||||
/* Prefer peers that we got from more trusted sources.
|
||||
* lower `fromBest` values indicate more trusted sources */
|
||||
score = addValToKey(score, 4U, peer_info.from_best());
|
||||
score = addValToKey(score, 4U, peer_info.from_best()); // TODO(tearfur): use std::bit_width(TR_PEER_FROM__MAX - 1)
|
||||
|
||||
/* salt */
|
||||
score = addValToKey(score, 8U, salt);
|
||||
|
@ -2588,10 +2576,10 @@ void get_peer_candidates(size_t global_peer_limit, tr_torrents& torrents, tr_pee
|
|||
continue;
|
||||
}
|
||||
|
||||
/* if everyone in the swarm is seeds and pex is disabled,
|
||||
/* if everyone in the swarm is upload only and pex is disabled,
|
||||
* then don't initiate connections */
|
||||
bool const seeding = tor->is_done();
|
||||
if (seeding && swarm->is_all_seeds() && !tor->allows_pex())
|
||||
if (seeding && swarm->is_all_upload_only() && !tor->allows_pex())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
@ -2664,7 +2652,7 @@ void initiate_connection(tr_peerMgr* mgr, tr_swarm* s, tr_peer_info& peer_info)
|
|||
{
|
||||
tr_logAddTraceSwarm(s, fmt::format("peerIo not created; marking peer {} as unreachable", peer_info.display_name()));
|
||||
peer_info.set_connectable(false);
|
||||
peer_info.on_connection_failed();
|
||||
peer_info.on_fruitless_connection();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -43,7 +43,7 @@ enum
|
|||
/* true if the peer supports encryption */
|
||||
ADDED_F_ENCRYPTION_FLAG = 1,
|
||||
/* true if the peer is a seed or partial seed */
|
||||
ADDED_F_SEED_FLAG = 2,
|
||||
ADDED_F_UPLOAD_ONLY_FLAG = 2,
|
||||
/* true if the peer supports µTP */
|
||||
ADDED_F_UTP_FLAGS = 4,
|
||||
/* true if the peer has holepunch support */
|
||||
|
@ -161,6 +161,16 @@ public:
|
|||
return is_seed_;
|
||||
}
|
||||
|
||||
constexpr void set_upload_only(bool value = true) noexcept
|
||||
{
|
||||
is_upload_only_ = value;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto is_upload_only() const noexcept
|
||||
{
|
||||
return is_upload_only_ || is_seed();
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
void set_connectable(bool value = true) noexcept
|
||||
|
@ -187,9 +197,33 @@ public:
|
|||
|
||||
// ---
|
||||
|
||||
[[nodiscard]] constexpr auto compare_by_failure_count(tr_peer_info const& that) const noexcept
|
||||
void set_encryption_preferred(bool value = true) noexcept
|
||||
{
|
||||
return tr_compare_3way(num_consecutive_fails_, that.num_consecutive_fails_);
|
||||
is_encryption_preferred_ = value;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto const& prefers_encryption() const noexcept
|
||||
{
|
||||
return is_encryption_preferred_;
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
void set_holepunch_supported(bool value = true) noexcept
|
||||
{
|
||||
is_holepunch_supported_ = value;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto const& supports_holepunch() const noexcept
|
||||
{
|
||||
return is_holepunch_supported_;
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
[[nodiscard]] constexpr auto compare_by_fruitless_count(tr_peer_info const& that) const noexcept
|
||||
{
|
||||
return tr_compare_3way(num_consecutive_fruitless_, that.num_consecutive_fruitless_);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto compare_by_piece_data_time(tr_peer_info const& that) const noexcept
|
||||
|
@ -201,15 +235,27 @@ public:
|
|||
|
||||
constexpr auto set_connected(time_t now, bool is_connected = true) noexcept
|
||||
{
|
||||
if (is_connected_ == is_connected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
connection_changed_at_ = now;
|
||||
|
||||
is_connected_ = is_connected;
|
||||
|
||||
if (is_connected_)
|
||||
{
|
||||
num_consecutive_fails_ = {};
|
||||
piece_data_at_ = {};
|
||||
}
|
||||
else if (has_transferred_piece_data())
|
||||
{
|
||||
num_consecutive_fruitless_ = {};
|
||||
}
|
||||
else
|
||||
{
|
||||
on_fruitless_connection();
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto is_connected() const noexcept
|
||||
|
@ -316,17 +362,17 @@ public:
|
|||
|
||||
// ---
|
||||
|
||||
constexpr void on_connection_failed() noexcept
|
||||
constexpr void on_fruitless_connection() noexcept
|
||||
{
|
||||
if (num_consecutive_fails_ != std::numeric_limits<decltype(num_consecutive_fails_)>::max())
|
||||
if (num_consecutive_fruitless_ != std::numeric_limits<decltype(num_consecutive_fruitless_)>::max())
|
||||
{
|
||||
++num_consecutive_fails_;
|
||||
++num_consecutive_fruitless_;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto connection_failure_count() const noexcept
|
||||
[[nodiscard]] constexpr auto fruitless_connection_count() const noexcept
|
||||
{
|
||||
return num_consecutive_fails_;
|
||||
return num_consecutive_fruitless_;
|
||||
}
|
||||
|
||||
// ---
|
||||
|
@ -345,7 +391,20 @@ public:
|
|||
set_utp_supported();
|
||||
}
|
||||
|
||||
is_seed_ = (pex_flags & ADDED_F_SEED_FLAG) != 0U;
|
||||
if ((pex_flags & ADDED_F_ENCRYPTION_FLAG) != 0U)
|
||||
{
|
||||
set_encryption_preferred();
|
||||
}
|
||||
|
||||
if ((pex_flags & ADDED_F_HOLEPUNCH) != 0U)
|
||||
{
|
||||
set_holepunch_supported();
|
||||
}
|
||||
|
||||
if ((pex_flags & ADDED_F_UPLOAD_ONLY_FLAG) != 0U)
|
||||
{
|
||||
set_upload_only();
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr uint8_t pex_flags() const noexcept
|
||||
|
@ -376,9 +435,37 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
if (is_seed_)
|
||||
if (is_encryption_preferred_)
|
||||
{
|
||||
ret |= ADDED_F_SEED_FLAG;
|
||||
if (*is_encryption_preferred_)
|
||||
{
|
||||
ret |= ADDED_F_ENCRYPTION_FLAG;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret &= ~ADDED_F_ENCRYPTION_FLAG;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_holepunch_supported_)
|
||||
{
|
||||
if (*is_holepunch_supported_)
|
||||
{
|
||||
ret |= ADDED_F_HOLEPUNCH;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret &= ~ADDED_F_HOLEPUNCH;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_upload_only())
|
||||
{
|
||||
ret |= ADDED_F_UPLOAD_ONLY_FLAG;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret &= ~ADDED_F_UPLOAD_ONLY_FLAG;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -404,7 +491,7 @@ private:
|
|||
// otherwise, the interval depends on how many times we've tried
|
||||
// and failed to connect to the peer. Penalize peers that were
|
||||
// unreachable the last time we tried
|
||||
auto step = this->num_consecutive_fails_;
|
||||
auto step = num_consecutive_fruitless_;
|
||||
if (unreachable)
|
||||
{
|
||||
step += 2;
|
||||
|
@ -445,16 +532,19 @@ private:
|
|||
mutable std::optional<bool> blocklisted_;
|
||||
std::optional<bool> is_connectable_;
|
||||
std::optional<bool> is_utp_supported_;
|
||||
std::optional<bool> is_encryption_preferred_;
|
||||
std::optional<bool> is_holepunch_supported_;
|
||||
|
||||
tr_peer_from from_first_; // where the peer was first found
|
||||
tr_peer_from const from_first_; // where the peer was first found
|
||||
tr_peer_from from_best_; // the "best" place where this peer was found
|
||||
|
||||
uint8_t num_consecutive_fails_ = {};
|
||||
uint8_t num_consecutive_fruitless_ = {};
|
||||
uint8_t pex_flags_ = {};
|
||||
|
||||
bool is_banned_ = false;
|
||||
bool is_connected_ = false;
|
||||
bool is_seed_ = false;
|
||||
bool is_upload_only_ = false;
|
||||
|
||||
std::unique_ptr<tr_handshake> outgoing_handshake_;
|
||||
};
|
||||
|
|
|
@ -191,15 +191,6 @@ auto constexpr MaxPexPeerCount = size_t{ 50U };
|
|||
|
||||
// ---
|
||||
|
||||
enum class EncryptionPreference : uint8_t
|
||||
{
|
||||
Unknown,
|
||||
Yes,
|
||||
No
|
||||
};
|
||||
|
||||
// ---
|
||||
|
||||
struct peer_request
|
||||
{
|
||||
uint32_t index = 0;
|
||||
|
@ -405,6 +396,8 @@ public:
|
|||
|
||||
void on_torrent_got_metainfo() noexcept override
|
||||
{
|
||||
// A peer may not be interesting to us anymore after
|
||||
// sending us metadata, so do a status update
|
||||
update_active();
|
||||
}
|
||||
|
||||
|
@ -496,8 +489,8 @@ public:
|
|||
|
||||
void update_active()
|
||||
{
|
||||
update_active(TR_UP);
|
||||
update_active(TR_DOWN);
|
||||
update_active(TR_CLIENT_TO_PEER);
|
||||
update_active(TR_PEER_TO_CLIENT);
|
||||
}
|
||||
|
||||
void update_active(tr_direction direction)
|
||||
|
@ -693,8 +686,6 @@ private:
|
|||
|
||||
tr_port dht_port_;
|
||||
|
||||
EncryptionPreference encryption_preference_ = EncryptionPreference::Unknown;
|
||||
|
||||
tr_torrent& tor_;
|
||||
|
||||
std::shared_ptr<tr_peerIo> const io_;
|
||||
|
@ -1203,21 +1194,15 @@ void tr_peerMsgsImpl::parse_ltep_handshake(MessageReader& payload)
|
|||
logtrace(this, fmt::format("here is the base64-encoded handshake: [{:s}]", tr_base64_encode(handshake_sv)));
|
||||
|
||||
// does the peer prefer encrypted connections?
|
||||
auto pex = tr_pex{};
|
||||
auto& [addr, port] = pex.socket_address;
|
||||
if (auto e = int64_t{}; tr_variantDictFindInt(&*var, TR_KEY_e, &e))
|
||||
{
|
||||
encryption_preference_ = e != 0 ? EncryptionPreference::Yes : EncryptionPreference::No;
|
||||
|
||||
if (encryption_preference_ == EncryptionPreference::Yes)
|
||||
{
|
||||
pex.flags |= ADDED_F_ENCRYPTION_FLAG;
|
||||
}
|
||||
peer_info->set_encryption_preferred(e != 0);
|
||||
}
|
||||
|
||||
// check supported messages for utorrent pex
|
||||
peer_supports_pex_ = false;
|
||||
peer_supports_metadata_xfer_ = false;
|
||||
auto holepunch_supported = false;
|
||||
|
||||
if (tr_variant* sub = nullptr; tr_variantDictFindDict(&*var, TR_KEY_m, &sub))
|
||||
{
|
||||
|
@ -1237,15 +1222,24 @@ void tr_peerMsgsImpl::parse_ltep_handshake(MessageReader& payload)
|
|||
|
||||
if (auto ut_holepunch = int64_t{}; tr_variantDictFindInt(sub, TR_KEY_ut_holepunch, &ut_holepunch))
|
||||
{
|
||||
// Transmission doesn't support this extension yet.
|
||||
// But its presence does indicate µTP supports,
|
||||
// which we do care about...
|
||||
peer_info->set_utp_supported(true);
|
||||
holepunch_supported = ut_holepunch != 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Transmission doesn't support this extension yet.
|
||||
// But its presence does indicate µTP support,
|
||||
// which we do care about...
|
||||
if (holepunch_supported)
|
||||
{
|
||||
peer_info->set_utp_supported();
|
||||
}
|
||||
// Even though we don't support it, no reason not to
|
||||
// help pass this flag to other peers who do.
|
||||
peer_info->set_holepunch_supported(holepunch_supported);
|
||||
|
||||
// look for metainfo size (BEP 9)
|
||||
if (auto metadata_size = int64_t{}; tr_variantDictFindInt(&*var, TR_KEY_metadata_size, &metadata_size))
|
||||
if (auto metadata_size = int64_t{};
|
||||
peer_supports_metadata_xfer_ && tr_variantDictFindInt(&*var, TR_KEY_metadata_size, &metadata_size))
|
||||
{
|
||||
if (!tr_metadata_download::is_valid_metadata_size(metadata_size))
|
||||
{
|
||||
|
@ -1260,7 +1254,7 @@ void tr_peerMsgsImpl::parse_ltep_handshake(MessageReader& payload)
|
|||
// look for upload_only (BEP 21)
|
||||
if (auto upload_only = int64_t{}; tr_variantDictFindInt(&*var, TR_KEY_upload_only, &upload_only))
|
||||
{
|
||||
pex.flags |= ADDED_F_SEED_FLAG;
|
||||
peer_info->set_upload_only(upload_only != 0);
|
||||
}
|
||||
|
||||
// https://www.bittorrent.org/beps/bep_0010.html
|
||||
|
@ -1275,8 +1269,7 @@ void tr_peerMsgsImpl::parse_ltep_handshake(MessageReader& payload)
|
|||
/* get peer's listening port */
|
||||
if (auto p = int64_t{}; tr_variantDictFindInt(&*var, TR_KEY_p, &p) && p > 0)
|
||||
{
|
||||
port.set_host(p);
|
||||
publish(tr_peer_event::GotPort(port));
|
||||
publish(tr_peer_event::GotPort(tr_port::from_host(p)));
|
||||
logtrace(this, fmt::format("peer's port is now {:d}", p));
|
||||
}
|
||||
|
||||
|
@ -1285,19 +1278,21 @@ void tr_peerMsgsImpl::parse_ltep_handshake(MessageReader& payload)
|
|||
if (io_->is_incoming() && tr_variantDictFindRaw(&*var, TR_KEY_ipv4, &addr_compact, &addr_len) &&
|
||||
addr_len == tr_address::CompactAddrBytes[TR_AF_INET])
|
||||
{
|
||||
std::tie(addr, std::ignore) = tr_address::from_compact_ipv4(addr_compact);
|
||||
auto pex = tr_pex{ peer_info->listen_socket_address(), peer_info->pex_flags() };
|
||||
pex.socket_address.address_ = tr_address::from_compact_ipv4(addr_compact).first;
|
||||
tr_peerMgrAddPex(&tor_, TR_PEER_FROM_LTEP, &pex, 1);
|
||||
}
|
||||
|
||||
if (io_->is_incoming() && tr_variantDictFindRaw(&*var, TR_KEY_ipv6, &addr_compact, &addr_len) &&
|
||||
addr_len == tr_address::CompactAddrBytes[TR_AF_INET6])
|
||||
{
|
||||
std::tie(addr, std::ignore) = tr_address::from_compact_ipv6(addr_compact);
|
||||
auto pex = tr_pex{ peer_info->listen_socket_address(), peer_info->pex_flags() };
|
||||
pex.socket_address.address_ = tr_address::from_compact_ipv6(addr_compact).first;
|
||||
tr_peerMgrAddPex(&tor_, TR_PEER_FROM_LTEP, &pex, 1);
|
||||
}
|
||||
|
||||
/* get peer's maximum request queue size */
|
||||
if (auto reqq_in = int64_t{}; tr_variantDictFindInt(&*var, TR_KEY_reqq, &reqq_in))
|
||||
if (auto reqq_in = int64_t{}; tr_variantDictFindInt(&*var, TR_KEY_reqq, &reqq_in) && reqq_in > 0)
|
||||
{
|
||||
peer_reqq_ = reqq_in;
|
||||
}
|
||||
|
@ -1427,6 +1422,7 @@ ReadResult tr_peerMsgsImpl::process_peer_message(uint8_t id, MessageReader& payl
|
|||
if (!have_.test(ui32))
|
||||
{
|
||||
have_.set(ui32);
|
||||
peer_info->set_seed(is_seed());
|
||||
publish(tr_peer_event::GotHave(ui32));
|
||||
}
|
||||
|
||||
|
@ -1436,6 +1432,7 @@ ReadResult tr_peerMsgsImpl::process_peer_message(uint8_t id, MessageReader& payl
|
|||
logtrace(this, "got a bitfield");
|
||||
have_ = tr_bitfield{ tor_.has_metainfo() ? tor_.piece_count() : std::size(payload) * 8 };
|
||||
have_.set_raw(reinterpret_cast<uint8_t const*>(std::data(payload)), std::size(payload));
|
||||
peer_info->set_seed(is_seed());
|
||||
publish(tr_peer_event::GotBitfield(&have_));
|
||||
break;
|
||||
|
||||
|
@ -1535,6 +1532,7 @@ ReadResult tr_peerMsgsImpl::process_peer_message(uint8_t id, MessageReader& payl
|
|||
if (fext)
|
||||
{
|
||||
have_.set_has_all();
|
||||
peer_info->set_seed();
|
||||
publish(tr_peer_event::GotHaveAll());
|
||||
}
|
||||
else
|
||||
|
@ -1551,6 +1549,7 @@ ReadResult tr_peerMsgsImpl::process_peer_message(uint8_t id, MessageReader& payl
|
|||
if (fext)
|
||||
{
|
||||
have_.set_has_none();
|
||||
peer_info->set_seed(false);
|
||||
publish(tr_peer_event::GotHaveNone());
|
||||
}
|
||||
else
|
||||
|
@ -1619,6 +1618,7 @@ ReadResult tr_peerMsgsImpl::read_piece_data(MessageReader& payload)
|
|||
return { ReadState::Err, len };
|
||||
}
|
||||
|
||||
peer_info->set_latest_piece_data_time(tr_time());
|
||||
publish(tr_peer_event::GotPieceData(len));
|
||||
|
||||
if (loc.block_offset == 0U && len == block_size) // simple case: one message has entire block
|
||||
|
@ -1701,6 +1701,7 @@ void tr_peerMsgsImpl::did_write(tr_peerIo* /*io*/, size_t bytes_written, bool wa
|
|||
|
||||
if (was_piece_data)
|
||||
{
|
||||
msgs->peer_info->set_latest_piece_data_time(tr_time());
|
||||
msgs->publish(tr_peer_event::SentPieceData(bytes_written));
|
||||
}
|
||||
|
||||
|
|
|
@ -2102,9 +2102,9 @@ void tr_torrent::on_tracker_response(tr_tracker_event const* event)
|
|||
break;
|
||||
|
||||
case tr_tracker_event::Type::Counts:
|
||||
if (is_private() && (event->leechers == 0))
|
||||
if (is_private() && (event->leechers == 0 || event->downloaders == 0))
|
||||
{
|
||||
swarm_is_all_seeds_.emit(this);
|
||||
swarm_is_all_upload_only_.emit(this);
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
|
@ -961,7 +961,7 @@ struct tr_torrent
|
|||
libtransmission::SimpleObservable<tr_torrent*> got_metainfo_;
|
||||
libtransmission::SimpleObservable<tr_torrent*> started_;
|
||||
libtransmission::SimpleObservable<tr_torrent*> stopped_;
|
||||
libtransmission::SimpleObservable<tr_torrent*> swarm_is_all_seeds_;
|
||||
libtransmission::SimpleObservable<tr_torrent*> swarm_is_all_upload_only_;
|
||||
libtransmission::SimpleObservable<tr_torrent*, tr_file_index_t const*, tr_file_index_t, tr_priority_t> priority_changed_;
|
||||
libtransmission::SimpleObservable<tr_torrent*, bool> sequential_download_changed_;
|
||||
|
||||
|
|
Loading…
Reference in a new issue