2023-11-01 22:11:11 +01:00
// This file Copyright © Mnemosyne LLC.
2022-02-07 10:25:02 -06:00
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
2022-01-20 12:27:56 -06:00
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
2007-09-20 16:32:01 +00:00
2021-09-19 22:41:35 +02:00
# include <algorithm>
2022-08-24 20:19:21 -05:00
# include <array>
2023-07-05 11:33:50 -05:00
# include <cerrno> // error codes ERANGE, ...
2022-08-11 12:28:37 -05:00
# include <chrono>
2022-02-06 22:28:36 -06:00
# include <cmath>
2023-07-05 11:33:50 -05:00
# include <cstddef> // std::byte
2022-02-19 12:10:43 -06:00
# include <cstdint>
2022-01-31 21:11:47 -06:00
# include <ctime> // time_t
2023-11-21 09:02:03 -06:00
# include <functional>
2022-01-31 21:11:47 -06:00
# include <iterator> // std::back_inserter
2023-11-21 09:02:03 -06:00
# include <memory>
2022-06-04 10:29:24 -05:00
# include <optional>
2022-04-28 10:52:26 -05:00
# include <tuple> // std::tie
2023-07-12 00:57:29 +08:00
# include <unordered_map>
2022-04-27 12:20:55 -05:00
# include <utility>
2021-09-30 10:08:34 -05:00
# include <vector>
2007-09-20 16:32:01 +00:00
2023-07-05 23:16:18 -05:00
# include <small/vector.hpp>
2023-04-16 15:34:19 -05:00
# include <fmt/core.h>
2022-03-13 23:43:35 -05:00
2021-11-19 12:37:38 -06:00
# define LIBTRANSMISSION_PEER_MODULE
2023-04-14 14:33:23 -05:00
# include "libtransmission/transmission.h"
2022-01-31 21:11:47 -06:00
2023-04-14 14:33:23 -05:00
# include "libtransmission/announcer.h"
2023-07-08 23:24:03 +08:00
# include "libtransmission/block-info.h" // tr_block_info
2023-04-14 14:33:23 -05:00
# include "libtransmission/clients.h"
# include "libtransmission/crypto-utils.h"
# include "libtransmission/handshake.h"
2023-07-05 11:33:50 -05:00
# include "libtransmission/interned-string.h"
2023-04-14 14:33:23 -05:00
# include "libtransmission/log.h"
# include "libtransmission/net.h"
2023-07-08 23:24:03 +08:00
# include "libtransmission/observable.h"
# include "libtransmission/peer-common.h"
2023-04-14 14:33:23 -05:00
# include "libtransmission/peer-io.h"
# include "libtransmission/peer-mgr-active-requests.h"
# include "libtransmission/peer-mgr-wishlist.h"
# include "libtransmission/peer-mgr.h"
# include "libtransmission/peer-msgs.h"
2023-07-08 23:24:03 +08:00
# include "libtransmission/peer-socket.h"
2023-07-05 11:33:50 -05:00
# include "libtransmission/quark.h"
2023-04-14 14:33:23 -05:00
# include "libtransmission/session.h"
# include "libtransmission/timer.h"
# include "libtransmission/torrent-magnet.h"
2023-07-05 11:33:50 -05:00
# include "libtransmission/torrent.h"
2023-11-04 01:03:26 +08:00
# include "libtransmission/torrents.h"
2023-04-14 14:33:23 -05:00
# include "libtransmission/tr-assert.h"
2023-07-05 11:33:50 -05:00
# include "libtransmission/tr-macros.h"
2023-04-14 14:33:23 -05:00
# include "libtransmission/utils.h"
2023-11-21 09:02:03 -06:00
# include "libtransmission/values.h"
2023-04-14 14:33:23 -05:00
# include "libtransmission/webseed.h"
2007-09-20 16:32:01 +00:00
2022-08-11 12:28:37 -05:00
using namespace std : : literals ;
2023-11-12 14:38:27 -06:00
using namespace libtransmission : : Values ;
2022-08-11 12:28:37 -05:00
2024-02-13 10:42:19 -06:00
static auto constexpr CancelHistorySec = 60 ;
2007-10-01 16:31:17 +00:00
2023-01-07 16:55:00 -06:00
// ---
2022-07-11 18:29:48 -05:00
2023-08-01 22:56:26 +08:00
namespace
{
2022-12-13 11:59:21 -06:00
class HandshakeMediator final : public tr_handshake : : Mediator
2022-07-11 18:29:48 -05:00
{
private :
2022-12-13 19:58:39 -06:00
[[nodiscard]] static std : : optional < TorrentInfo > torrent ( tr_torrent * tor )
2022-07-11 18:29:48 -05:00
{
if ( tor = = nullptr )
{
return { } ;
}
2022-12-13 11:59:21 -06:00
auto info = TorrentInfo { } ;
2023-04-22 20:25:55 -05:00
info . info_hash = tor - > info_hash ( ) ;
2023-03-15 20:53:48 -05:00
info . client_peer_id = tor - > peer_id ( ) ;
2022-07-11 18:29:48 -05:00
info . id = tor - > id ( ) ;
2023-04-22 20:25:55 -05:00
info . is_done = tor - > is_done ( ) ;
2022-07-11 18:29:48 -05:00
return info ;
}
public :
2023-10-24 15:24:52 -04:00
explicit HandshakeMediator (
tr_session const & session ,
libtransmission : : TimerMaker & timer_maker ,
tr_torrents & torrents ) noexcept
2022-07-11 18:29:48 -05:00
: session_ { session }
2023-10-24 15:24:52 -04:00
, timer_maker_ { timer_maker }
, torrents_ { torrents }
2022-07-11 18:29:48 -05:00
{
}
2022-12-13 19:58:39 -06:00
[[nodiscard]] std : : optional < TorrentInfo > torrent ( tr_sha1_digest_t const & info_hash ) const override
2022-07-11 18:29:48 -05:00
{
2023-10-24 15:24:52 -04:00
return torrent ( torrents_ . get ( info_hash ) ) ;
2022-07-11 18:29:48 -05:00
}
2023-10-24 15:24:52 -04:00
[[nodiscard]] std : : optional < TorrentInfo > torrent_from_obfuscated (
tr_sha1_digest_t const & obfuscated_info_hash ) const override
2022-07-11 18:29:48 -05:00
{
2023-10-24 15:24:52 -04:00
return torrent ( torrents_ . find_from_obfuscated_hash ( obfuscated_info_hash ) ) ;
2022-07-11 18:29:48 -05:00
}
2022-12-13 11:59:21 -06:00
[[nodiscard]] bool allows_dht ( ) const override
2022-07-11 18:29:48 -05:00
{
2022-10-15 08:22:43 -05:00
return session_ . allowsDHT ( ) ;
2022-07-11 18:29:48 -05:00
}
2022-12-13 11:59:21 -06:00
[[nodiscard]] bool allows_tcp ( ) const override
2022-08-25 19:27:11 -07:00
{
return session_ . allowsTCP ( ) ;
}
2023-07-12 10:10:20 -05:00
void set_utp_failed ( tr_sha1_digest_t const & info_hash , tr_socket_address const & socket_address ) override ;
2022-07-11 18:29:48 -05:00
2022-12-13 11:59:21 -06:00
[[nodiscard]] libtransmission : : TimerMaker & timer_maker ( ) override
2022-07-11 18:29:48 -05:00
{
2023-10-24 15:24:52 -04:00
return timer_maker_ ;
2022-07-11 18:29:48 -05:00
}
2022-07-14 19:54:10 -05:00
[[nodiscard]] size_t pad ( void * setme , size_t maxlen ) const override
{
auto const len = tr_rand_int ( maxlen ) ;
tr_rand_buffer ( setme , len ) ;
return len ;
}
2022-07-11 18:29:48 -05:00
private :
2023-10-24 15:24:52 -04:00
tr_session const & session_ ;
libtransmission : : TimerMaker & timer_maker_ ;
tr_torrents & torrents_ ;
2022-07-11 18:29:48 -05:00
} ;
2023-08-01 22:56:26 +08:00
using Handshakes = std : : unordered_map < tr_socket_address , tr_handshake > ;
} // anonymous namespace
2023-10-15 01:58:15 +08:00
void tr_peer_info : : merge ( tr_peer_info & that ) noexcept
{
TR_ASSERT ( is_connectable_ . value_or ( true ) | | ! is_connected ( ) ) ;
TR_ASSERT ( that . is_connectable_ . value_or ( true ) | | ! that . is_connected ( ) ) ;
connection_attempted_at_ = std : : max ( connection_attempted_at_ , that . connection_attempted_at_ ) ;
connection_changed_at_ = std : : max ( connection_changed_at_ , that . connection_changed_at_ ) ;
piece_data_at_ = std : : max ( piece_data_at_ , that . piece_data_at_ ) ;
/* no need to merge blocklist since it gets updated elsewhere */
{
// This part is frankly convoluted and confusing, but the idea is:
// 1. If the two peer info objects agree that this peer is connectable/non-connectable,
// then the answer is straightforward: We keep the agreed value.
// 2. If the two peer info objects disagrees as to whether this peer is connectable,
// then we reset the flag to an empty value, so that we can try for ourselves when
// initiating outgoing connections.
// 3. If one object has knowledge and the other doesn't, then we take the word of the
// peer info object with knowledge with one exception:
// - If the object with knowledge says the peer is not connectable, but we are
// currently connected to the peer, then we give it the benefit of the doubt.
// The connectable flag will be reset to an empty value.
// 4. In case both objects have no knowledge about whether this peer is connectable,
// we shall not make any assumptions: We keep the flag empty.
//
// Truth table:
// +-----------------+---------------+----------------------+--------------------+---------+
// | is_connectable_ | is_connected_ | that.is_connectable_ | that.is_connected_ | Result |
// +=================+===============+======================+====================+=========+
// | T | T | T | T | T |
// | T | T | T | F | T |
// | T | T | F | F | ? |
// | T | T | ? | T | T |
// | T | T | ? | F | T |
// | T | F | T | T | T |
// | T | F | T | F | T |
// | T | F | F | F | ? |
// | T | F | ? | T | T |
// | T | F | ? | F | T |
// | F | F | T | T | ? |
// | F | F | T | F | ? |
// | F | F | F | F | F |
// | F | F | ? | T | ? |
// | F | F | ? | F | F |
// | ? | T | T | T | T |
// | ? | T | T | F | T |
// | ? | T | F | F | ? |
// | ? | T | ? | T | ? |
// | ? | T | ? | F | ? |
// | ? | F | T | T | T |
// | ? | F | T | F | T |
// | ? | F | F | F | F |
// | ? | F | ? | T | ? |
// | ? | F | ? | F | ? |
// | N/A | N/A | F | T | Invalid |
// | F | T | N/A | N/A | Invalid |
// +-----------------+---------------+----------------------+--------------------+---------+
auto const conn_this = is_connectable_ & & * is_connectable_ ;
auto const conn_that = that . is_connectable_ & & * that . is_connectable_ ;
if ( ( ! is_connectable_ & & ! that . is_connectable_ ) | |
is_connectable_ . value_or ( conn_that | | is_connected ( ) ) ! =
that . is_connectable_ . value_or ( conn_this | | that . is_connected ( ) ) )
{
is_connectable_ . reset ( ) ;
}
else
{
set_connectable ( conn_this | | conn_that ) ;
}
}
set_utp_supported ( supports_utp ( ) | | that . supports_utp ( ) ) ;
/* from_first_ should never be modified */
found_at ( that . from_best ( ) ) ;
/* num_consecutive_fails_ is already the latest */
pex_flags_ | = that . pex_flags_ ;
if ( that . is_banned ( ) )
{
ban ( ) ;
}
/* is_connected_ should already be set */
set_seed ( is_seed ( ) | | that . is_seed ( ) ) ;
if ( that . outgoing_handshake_ )
{
if ( outgoing_handshake_ )
{
that . destroy_handshake ( ) ;
}
else
{
outgoing_handshake_ = std : : move ( that . outgoing_handshake_ ) ;
}
}
}
2022-06-28 01:22:34 -05:00
# define tr_logAddDebugSwarm(swarm, msg) tr_logAddDebugTor((swarm)->tor, msg)
# define tr_logAddTraceSwarm(swarm, msg) tr_logAddTraceTor((swarm)->tor, msg)
2023-08-01 22:56:26 +08:00
namespace
{
/* better goes first */
constexpr struct
{
[[nodiscard]] constexpr static int compare ( tr_peer_info const & a , tr_peer_info const & b ) noexcept // <=>
{
if ( auto const val = a . compare_by_piece_data_time ( b ) ; val ! = 0 )
{
return - val ;
}
if ( auto const val = tr_compare_3way ( a . from_best ( ) , b . from_best ( ) ) ; val ! = 0 )
{
return val ;
}
return a . compare_by_failure_count ( b ) ;
}
[[nodiscard]] constexpr bool operator ( ) ( tr_peer_info const & a , tr_peer_info const & b ) const noexcept
{
return compare ( a , b ) < 0 ;
}
[[nodiscard]] constexpr bool operator ( ) ( tr_peer_info const * a , tr_peer_info const * b ) const noexcept
{
return compare ( * a , * b ) < 0 ;
}
} CompareAtomsByUsefulness { } ;
} // namespace
2010-01-19 19:37:00 +00:00
/** @brief Opaque, per-torrent data structure for peer connection information */
2021-10-09 19:13:40 -05:00
class tr_swarm
2007-09-20 16:32:01 +00:00
{
2021-10-09 19:13:40 -05:00
public :
2023-08-01 22:56:26 +08:00
using Peers = std : : vector < tr_peerMsgs * > ;
using Pool = std : : unordered_map < tr_socket_address , tr_peer_info > ;
2024-02-05 14:14:34 +08:00
class WishlistMediator final : public Wishlist : : Mediator
{
public :
explicit WishlistMediator ( tr_swarm & swarm )
: tor_ { * swarm . tor }
, swarm_ { swarm }
{
}
[[nodiscard]] bool client_has_block ( tr_block_index_t block ) const override ;
[[nodiscard]] bool client_wants_piece ( tr_piece_index_t piece ) const override ;
[[nodiscard]] bool is_endgame ( ) const override ;
[[nodiscard]] bool is_sequential_download ( ) const override ;
[[nodiscard]] size_t count_active_requests ( tr_block_index_t block ) const override ;
[[nodiscard]] size_t count_missing_blocks ( tr_piece_index_t piece ) const override ;
[[nodiscard]] size_t count_piece_replication ( tr_piece_index_t piece ) const override ;
[[nodiscard]] tr_block_span_t block_span ( tr_piece_index_t piece ) const override ;
[[nodiscard]] tr_piece_index_t piece_count ( ) const override ;
[[nodiscard]] tr_priority_t priority ( tr_piece_index_t piece ) const override ;
[[nodiscard]] libtransmission : : ObserverTag observe_peer_disconnect (
libtransmission : : SimpleObservable < tr_torrent * , tr_bitfield const & > : : Observer observer ) override ;
[[nodiscard]] libtransmission : : ObserverTag observe_got_bitfield (
libtransmission : : SimpleObservable < tr_torrent * , tr_bitfield const & > : : Observer observer ) override ;
[[nodiscard]] libtransmission : : ObserverTag observe_got_block (
libtransmission : : SimpleObservable < tr_torrent * , tr_piece_index_t , tr_block_index_t > : : Observer observer ) override ;
[[nodiscard]] libtransmission : : ObserverTag observe_got_have (
libtransmission : : SimpleObservable < tr_torrent * , tr_piece_index_t > : : Observer observer ) override ;
[[nodiscard]] libtransmission : : ObserverTag observe_got_have_all (
libtransmission : : SimpleObservable < tr_torrent * > : : Observer observer ) override ;
[[nodiscard]] libtransmission : : ObserverTag observe_piece_completed (
libtransmission : : SimpleObservable < tr_torrent * , tr_piece_index_t > : : Observer observer ) override ;
[[nodiscard]] libtransmission : : ObserverTag observe_priority_changed (
libtransmission : : SimpleObservable < tr_torrent * , tr_file_index_t const * , tr_file_index_t , tr_priority_t > : : Observer
observer ) override ;
[[nodiscard]] libtransmission : : ObserverTag observe_sequential_download_changed (
libtransmission : : SimpleObservable < tr_torrent * , bool > : : Observer observer ) override ;
private :
tr_torrent & tor_ ;
tr_swarm & swarm_ ;
} ;
2023-01-07 16:55:00 -06:00
[[nodiscard]] auto unique_lock ( ) const
{
return tor - > unique_lock ( ) ;
}
2022-04-27 20:06:51 -05:00
tr_swarm ( tr_peerMgr * manager_in , tr_torrent * tor_in ) noexcept
2021-10-09 19:13:40 -05:00
: manager { manager_in }
, tor { tor_in }
2023-07-04 15:47:18 -05:00
, tags_ { {
tor_in - > done_ . observe ( [ this ] ( tr_torrent * , bool ) { on_torrent_done ( ) ; } ) ,
tor_in - > doomed_ . observe ( [ this ] ( tr_torrent * ) { on_torrent_doomed ( ) ; } ) ,
tor_in - > got_bad_piece_ . observe ( [ this ] ( tr_torrent * , tr_piece_index_t p ) { on_got_bad_piece ( p ) ; } ) ,
tor_in - > got_metainfo_ . observe ( [ this ] ( tr_torrent * ) { on_got_metainfo ( ) ; } ) ,
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 ( ) ; } ) ,
} }
2021-10-09 19:13:40 -05:00
{
2023-10-30 02:32:29 -04:00
rebuild_webseeds ( ) ;
2021-10-09 19:13:40 -05:00
}
2013-07-08 16:41:12 +00:00
2023-01-07 16:55:00 -06:00
tr_swarm ( tr_swarm & & ) = delete ;
tr_swarm ( tr_swarm const & ) = delete ;
tr_swarm & operator = ( tr_swarm & & ) = delete ;
tr_swarm & operator = ( tr_swarm const & ) = delete ;
~ tr_swarm ( )
{
auto const lock = unique_lock ( ) ;
TR_ASSERT ( ! is_running ) ;
TR_ASSERT ( std : : empty ( peers ) ) ;
}
2023-10-30 02:32:29 -04:00
void cancel_old_requests ( )
2022-06-28 15:24:39 -05:00
{
auto const now = tr_time ( ) ;
auto const oldest = now - RequestTtlSecs ;
for ( auto const & [ block , peer ] : active_requests . sentBefore ( oldest ) )
{
2023-10-30 02:32:29 -04:00
maybe_send_cancel_request ( peer , block , nullptr ) ;
2022-06-28 15:24:39 -05:00
active_requests . remove ( block , peer ) ;
}
}
2023-10-20 02:05:19 +08:00
void remove_inactive_peer_info ( ) noexcept
{
auto const now = tr_time ( ) ;
for ( auto iter = std : : begin ( connectable_pool ) , end = std : : end ( connectable_pool ) ; iter ! = end ; )
{
auto & [ socket_address , peer_info ] = * iter ;
if ( peer_info . is_inactive ( now ) )
{
2023-11-08 18:17:00 +01:00
- - stats . known_peer_from_count [ peer_info . from_first ( ) ] ;
2023-10-20 02:05:19 +08:00
iter = connectable_pool . erase ( iter ) ;
}
else
{
+ + iter ;
}
}
}
2023-10-30 02:32:29 -04:00
[[nodiscard]] uint16_t count_active_webseeds ( uint64_t now ) const noexcept
2022-06-28 15:24:39 -05:00
{
2023-04-22 20:25:55 -05:00
if ( ! tor - > is_running ( ) | | tor - > is_done ( ) )
2022-06-28 15:24:39 -05:00
{
return { } ;
}
return std : : count_if (
std : : begin ( webseeds ) ,
std : : end ( webseeds ) ,
2023-11-12 14:38:27 -06:00
[ & now ] ( auto const & webseed ) { return webseed - > get_piece_speed ( now , TR_DOWN ) . base_quantity ( ) ! = 0U ; } ) ;
2022-06-28 15:24:39 -05:00
}
2023-04-14 16:03:08 -05:00
[[nodiscard]] TR_CONSTEXPR20 auto peerCount ( ) const noexcept
2022-04-29 08:32:15 -05:00
{
return std : : size ( peers ) ;
}
2023-08-01 22:56:26 +08:00
void remove_peer ( tr_peerMsgs * peer )
2022-06-28 15:24:39 -05:00
{
auto const lock = unique_lock ( ) ;
2024-02-05 14:14:34 +08:00
peer_disconnect . emit ( tor , peer - > has ( ) ) ;
2023-07-14 07:06:25 -05:00
auto * const peer_info = peer - > peer_info ;
2023-08-01 22:56:26 +08:00
auto const socket_address = peer - > socket_address ( ) ;
2023-10-03 00:57:48 +08:00
[[maybe_unused]] auto const is_incoming = peer - > is_incoming_connection ( ) ;
2023-07-14 07:06:25 -05:00
TR_ASSERT ( peer_info ! = nullptr ) ;
2022-06-28 15:24:39 -05:00
2023-08-01 22:56:26 +08:00
- - stats . peer_count ;
- - stats . peer_from_count [ peer_info - > from_first ( ) ] ;
2022-06-28 15:24:39 -05:00
if ( auto iter = std : : find ( std : : begin ( peers ) , std : : end ( peers ) , peer ) ; iter ! = std : : end ( peers ) )
{
peers . erase ( iter ) ;
2023-08-01 22:56:26 +08:00
TR_ASSERT ( stats . peer_count = = peerCount ( ) ) ;
2022-06-28 15:24:39 -05:00
}
delete peer ;
2023-08-01 22:56:26 +08:00
2023-10-03 00:57:48 +08:00
if ( std : : empty ( peer_info - > listen_port ( ) ) ) // is not connectable
2023-08-01 22:56:26 +08:00
{
2023-10-03 00:57:48 +08:00
TR_ASSERT ( is_incoming ) ;
[[maybe_unused]] auto const count = incoming_pool . erase ( socket_address ) ;
TR_ASSERT ( count ! = 0U ) ;
}
else
{
graveyard_pool . erase ( peer_info - > listen_socket_address ( ) ) ;
2023-08-01 22:56:26 +08:00
}
2022-06-28 15:24:39 -05:00
}
2023-08-01 22:56:26 +08:00
void remove_all_peers ( )
2022-06-28 15:24:39 -05:00
{
2023-08-01 22:56:26 +08:00
auto tmp = Peers { } ;
std : : swap ( tmp , peers ) ;
2022-06-28 15:24:39 -05:00
for ( auto * peer : tmp )
{
2023-08-01 22:56:26 +08:00
remove_peer ( peer ) ;
2022-06-28 15:24:39 -05:00
}
TR_ASSERT ( stats . peer_count = = 0 ) ;
}
2023-10-30 02:32:29 -04:00
void update_endgame ( )
2022-06-28 01:22:34 -05:00
{
/* we consider ourselves to be in endgame if the number of bytes
we ' ve got requested is > = the number of bytes left to download */
2023-04-22 20:25:55 -05:00
is_endgame_ = uint64_t ( std : : size ( active_requests ) ) * tr_block_info : : BlockSize > = tor - > left_until_done ( ) ;
2022-06-28 01:22:34 -05:00
}
2023-10-30 02:32:29 -04:00
[[nodiscard]] constexpr auto is_endgame ( ) const noexcept
2022-06-28 01:22:34 -05:00
{
return is_endgame_ ;
}
2023-08-01 22:56:26 +08:00
[[nodiscard]] TR_CONSTEXPR20 auto is_all_seeds ( ) const noexcept
2022-06-28 01:22:34 -05:00
{
if ( ! pool_is_all_seeds_ )
{
2023-06-23 04:21:44 +08:00
pool_is_all_seeds_ = std : : all_of (
2023-08-01 22:56:26 +08:00
std : : begin ( connectable_pool ) ,
std : : end ( connectable_pool ) ,
2023-07-14 07:06:25 -05:00
[ ] ( auto const & key_val ) { return key_val . second . is_seed ( ) ; } ) ;
2022-06-28 01:22:34 -05:00
}
return * pool_is_all_seeds_ ;
}
2023-07-14 07:06:25 -05:00
[[nodiscard]] tr_peer_info * get_existing_peer_info ( tr_socket_address const & socket_address ) noexcept
2023-01-07 16:55:00 -06:00
{
2023-08-01 22:56:26 +08:00
auto & & it = connectable_pool . find ( socket_address ) ;
return it ! = connectable_pool . end ( ) ? & it - > second : nullptr ;
2023-01-07 16:55:00 -06:00
}
2023-08-01 22:56:26 +08:00
tr_peer_info & ensure_info_exists (
tr_socket_address const & socket_address ,
uint8_t const flags ,
tr_peer_from const from ,
bool is_connectable )
2023-01-07 12:58:16 -06:00
{
2023-07-12 17:29:47 -05:00
TR_ASSERT ( socket_address . is_valid ( ) ) ;
2023-01-07 16:55:00 -06:00
TR_ASSERT ( from < TR_PEER_FROM__MAX ) ;
2023-08-01 22:56:26 +08:00
auto & & [ it , is_new ] = is_connectable ? connectable_pool . try_emplace ( socket_address , socket_address , flags , from ) :
incoming_pool . try_emplace ( socket_address , socket_address . address ( ) , flags , from ) ;
auto & peer_info = it - > second ;
2023-06-23 04:21:44 +08:00
if ( ! is_new )
2023-01-07 16:55:00 -06:00
{
2023-07-14 07:06:25 -05:00
peer_info . found_at ( from ) ;
peer_info . set_pex_flags ( flags ) ;
2023-01-07 16:55:00 -06:00
}
2023-11-08 18:17:00 +01:00
else if ( is_connectable )
{
+ + stats . known_peer_from_count [ from ] ;
}
2023-01-07 16:55:00 -06:00
2023-07-04 15:47:18 -05:00
mark_all_seeds_flag_dirty ( ) ;
2023-01-07 16:55:00 -06:00
2023-07-14 07:06:25 -05:00
return peer_info ;
2023-01-07 16:55:00 -06:00
}
2023-08-01 22:56:26 +08:00
static void peer_callback_bt ( tr_peerMsgs * const msgs , tr_peer_event const & event , void * const vs )
{
TR_ASSERT ( msgs ! = nullptr ) ;
auto * s = static_cast < tr_swarm * > ( vs ) ;
TR_ASSERT ( msgs - > swarm = = s ) ;
auto const lock = s - > unique_lock ( ) ;
2023-01-07 16:55:00 -06:00
switch ( event . type )
2023-01-07 12:58:16 -06:00
{
2023-01-07 16:55:00 -06:00
case tr_peer_event : : Type : : ClientSentPieceData :
{
auto const now = tr_time ( ) ;
auto * const tor = s - > tor ;
2023-11-15 18:53:43 -06:00
tor - > bytes_uploaded_ + = event . length ;
2023-01-07 16:55:00 -06:00
tr_announcerAddBytes ( tor , TR_ANN_UP , event . length ) ;
2023-04-22 20:25:55 -05:00
tor - > set_date_active ( now ) ;
2023-05-05 23:11:05 -05:00
tor - > session - > add_uploaded ( event . length ) ;
2023-01-07 16:55:00 -06:00
2023-08-01 22:56:26 +08:00
msgs - > peer_info - > set_latest_piece_data_time ( now ) ;
2023-01-07 16:55:00 -06:00
}
2023-08-01 22:56:26 +08:00
break ;
2023-01-07 16:55:00 -06:00
case tr_peer_event : : Type : : ClientGotPieceData :
{
auto const now = tr_time ( ) ;
2023-08-01 22:56:26 +08:00
on_client_got_piece_data ( s - > tor , event . length , now ) ;
msgs - > peer_info - > set_latest_piece_data_time ( now ) ;
2023-01-07 16:55:00 -06:00
}
2023-08-01 22:56:26 +08:00
break ;
2023-01-07 16:55:00 -06:00
case tr_peer_event : : Type : : ClientGotHave :
2024-02-05 14:14:34 +08:00
s - > got_have . emit ( s - > tor , event . pieceIndex ) ;
break ;
2023-01-07 16:55:00 -06:00
case tr_peer_event : : Type : : ClientGotHaveAll :
2024-02-05 14:14:34 +08:00
s - > got_have_all . emit ( s - > tor ) ;
break ;
2023-01-07 16:55:00 -06:00
case tr_peer_event : : Type : : ClientGotHaveNone :
2024-02-05 14:14:34 +08:00
// no-op
break ;
2023-01-07 16:55:00 -06:00
case tr_peer_event : : Type : : ClientGotBitfield :
2024-02-05 14:14:34 +08:00
s - > got_bitfield . emit ( s - > tor , msgs - > has ( ) ) ;
2023-01-07 16:55:00 -06:00
break ;
case tr_peer_event : : Type : : ClientGotChoke :
2023-08-01 22:56:26 +08:00
s - > active_requests . remove ( msgs ) ;
2023-01-07 16:55:00 -06:00
break ;
case tr_peer_event : : Type : : ClientGotPort :
2023-08-01 22:56:26 +08:00
if ( std : : empty ( event . port ) )
2023-01-07 16:55:00 -06:00
{
2023-08-01 22:56:26 +08:00
// Do nothing
}
// If we don't know the listening port of this peer (i.e. incoming connection and first time ClientGotPort)
else if ( auto const & info = * msgs - > peer_info ; std : : empty ( info . listen_port ( ) ) )
{
s - > on_got_port ( msgs , event , false ) ;
}
// If we got a new listening port from a known connectable peer
else if ( info . listen_port ( ) ! = event . port )
{
s - > on_got_port ( msgs , event , true ) ;
2023-01-07 16:55:00 -06:00
}
break ;
case tr_peer_event : : Type : : ClientGotSuggest :
case tr_peer_event : : Type : : ClientGotAllowedFast :
// not currently supported
break ;
2023-08-01 22:56:26 +08:00
default :
peer_callback_common ( msgs , event , s ) ;
2023-01-07 16:55:00 -06:00
break ;
}
2023-01-07 12:58:16 -06:00
}
2024-02-05 14:14:34 +08:00
libtransmission : : SimpleObservable < tr_torrent * , tr_bitfield const & > peer_disconnect ;
libtransmission : : SimpleObservable < tr_torrent * , tr_bitfield const & > got_bitfield ;
libtransmission : : SimpleObservable < tr_torrent * , tr_piece_index_t , tr_block_index_t > got_block ;
libtransmission : : SimpleObservable < tr_torrent * , tr_piece_index_t > got_have ;
libtransmission : : SimpleObservable < tr_torrent * > got_have_all ;
2022-11-22 14:00:09 -06:00
mutable tr_swarm_stats stats = { } ;
2009-06-24 03:54:11 +00:00
2022-04-27 20:06:51 -05:00
uint8_t optimistic_unchoke_time_scaler = 0 ;
bool is_running = false ;
tr_peerMgr * const manager ;
2022-06-28 01:22:34 -05:00
tr_torrent * const tor ;
2023-02-12 00:30:30 -06:00
ActiveRequests active_requests ;
// depends-on: active_requests
2022-04-27 20:06:51 -05:00
std : : vector < std : : unique_ptr < tr_peer > > webseeds ;
2023-02-12 00:30:30 -06:00
// depends-on: active_requests
2023-08-01 22:56:26 +08:00
Peers peers ;
2022-06-04 10:29:24 -05:00
2024-02-05 14:14:34 +08:00
std : : unique_ptr < Wishlist > wishlist ;
2023-08-01 22:56:26 +08:00
// tr_peerMsgs hold pointers to the items in these containers,
2023-06-23 04:21:44 +08:00
// therefore references to elements within cannot invalidate
2023-08-01 22:56:26 +08:00
Pool incoming_pool ;
Pool connectable_pool ;
2009-06-24 03:54:11 +00:00
2021-10-09 19:13:40 -05:00
tr_peerMsgs * optimistic = nullptr ; /* the optimistic peer, or nullptr if none */
2009-11-08 23:20:00 +00:00
2022-06-28 01:22:34 -05:00
private :
2023-10-30 02:32:29 -04:00
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 ( ) ;
webseeds . clear ( ) ;
webseeds . reserve ( n ) ;
for ( size_t i = 0 ; i < n ; + + i )
{
2024-03-16 08:52:09 +08:00
webseeds . emplace_back ( tr_webseedNew ( * tor , tor - > webseed ( i ) , & tr_swarm : : peer_callback_webseed , this ) ) ;
2023-10-30 02:32:29 -04:00
}
webseeds . shrink_to_fit ( ) ;
stats . active_webseed_count = 0 ;
}
void add_strike ( tr_peerMsgs * peer ) const
{
tr_logAddTraceSwarm (
this ,
fmt : : format ( " increasing peer {} strike count to {} " , peer - > display_name ( ) , peer - > strikes + 1 ) ) ;
if ( + + peer - > strikes > = MaxBadPiecesPerPeer )
{
peer - > peer_info - > ban ( ) ;
peer - > do_purge = true ;
tr_logAddTraceSwarm ( this , fmt : : format ( " banning peer {} " , peer - > display_name ( ) ) ) ;
}
}
void stop ( )
{
auto const lock = unique_lock ( ) ;
is_running = false ;
remove_all_peers ( ) ;
for ( auto & [ sockaddr , peer_info ] : connectable_pool )
{
peer_info . destroy_handshake ( ) ;
}
}
static void maybe_send_cancel_request ( tr_peer * peer , tr_block_index_t block , tr_peer const * muted )
2022-06-28 15:24:39 -05:00
{
2024-03-16 08:52:09 +08:00
if ( peer ! = nullptr & & peer ! = muted )
2022-06-28 15:24:39 -05:00
{
2024-03-16 08:52:09 +08:00
peer - > cancel_block_request ( block ) ;
2022-06-28 15:24:39 -05:00
}
}
2023-10-30 02:32:29 -04:00
void cancel_all_requests_for_block ( tr_block_index_t block , tr_peer const * no_notify )
{
for ( auto * peer : active_requests . remove ( block ) )
{
maybe_send_cancel_request ( peer , block , no_notify ) ;
}
}
2023-07-04 15:47:18 -05:00
void mark_all_seeds_flag_dirty ( ) noexcept
{
pool_is_all_seeds_ . reset ( ) ;
}
void on_torrent_doomed ( )
{
2023-10-23 11:34:30 -04:00
auto const lock = unique_lock ( ) ;
2023-07-04 15:47:18 -05:00
stop ( ) ;
tor - > swarm = nullptr ;
delete this ;
}
void on_torrent_done ( )
{
std : : for_each ( std : : begin ( peers ) , std : : end ( peers ) , [ ] ( auto * const peer ) { peer - > set_interested ( false ) ; } ) ;
2024-02-05 14:14:34 +08:00
wishlist . reset ( ) ;
2023-07-04 15:47:18 -05:00
}
void on_swarm_is_all_seeds ( )
{
2023-10-23 11:34:30 -04:00
auto const lock = unique_lock ( ) ;
2023-07-04 15:47:18 -05:00
2023-08-01 22:56:26 +08:00
for ( auto & [ socket_address , atom ] : connectable_pool )
2023-07-04 15:47:18 -05:00
{
2023-07-14 07:06:25 -05:00
mark_peer_as_seed ( atom ) ;
2023-07-04 15:47:18 -05:00
}
mark_all_seeds_flag_dirty ( ) ;
}
void on_piece_completed ( tr_piece_index_t piece )
{
bool piece_came_from_peers = false ;
for ( auto * const peer : peers )
{
// notify the peer that we now have this piece
peer - > on_piece_completed ( piece ) ;
if ( ! piece_came_from_peers )
{
piece_came_from_peers = peer - > blame . test ( piece ) ;
}
}
if ( piece_came_from_peers ) /* webseed downloads don't belong in announce totals */
{
tr_announcerAddBytes ( tor , TR_ANN_DOWN , tor - > piece_size ( piece ) ) ;
}
}
void on_got_bad_piece ( tr_piece_index_t piece )
{
auto const byte_count = tor - > piece_size ( piece ) ;
for ( auto * const peer : peers )
{
if ( peer - > blame . test ( piece ) )
{
tr_logAddTraceSwarm (
this ,
fmt : : format (
" peer {} contributed to corrupt piece ({}); now has {} strikes " ,
peer - > display_name ( ) ,
piece ,
peer - > strikes + 1 ) ) ;
2023-10-30 02:32:29 -04:00
add_strike ( peer ) ;
2023-07-04 15:47:18 -05:00
}
}
tr_announcerAddBytes ( tor , TR_ANN_CORRUPT , byte_count ) ;
}
void on_got_metainfo ( )
{
// the webseed list may have changed...
2023-10-30 02:32:29 -04:00
rebuild_webseeds ( ) ;
2023-07-04 15:47:18 -05:00
// 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 )
{
2024-03-16 08:52:09 +08:00
peer - > on_torrent_got_metainfo ( ) ;
2023-07-04 15:47:18 -05:00
2024-03-16 08:52:09 +08:00
if ( peer - > is_seed ( ) )
2023-07-04 15:47:18 -05:00
{
2023-07-14 07:06:25 -05:00
mark_peer_as_seed ( * peer - > peer_info ) ;
2023-07-04 15:47:18 -05:00
}
}
}
void on_torrent_started ( ) ;
void on_torrent_stopped ( ) ;
2023-08-01 22:56:26 +08:00
// ---
2023-10-30 02:32:29 -04:00
static void peer_callback_webseed ( tr_peer * const peer , tr_peer_event const & event , void * const vs )
{
TR_ASSERT ( peer ! = nullptr ) ;
auto * s = static_cast < tr_swarm * > ( vs ) ;
auto const lock = s - > unique_lock ( ) ;
switch ( event . type )
{
case tr_peer_event : : Type : : ClientGotPieceData :
on_client_got_piece_data ( s - > tor , event . length , tr_time ( ) ) ;
break ;
default :
peer_callback_common ( peer , event , s ) ;
break ;
}
}
2023-08-01 22:56:26 +08:00
static void peer_callback_common ( tr_peer * const peer , tr_peer_event const & event , tr_swarm * const s )
{
switch ( event . type )
{
case tr_peer_event : : Type : : ClientGotRej :
s - > active_requests . remove ( s - > tor - > piece_loc ( event . pieceIndex , event . offset ) . block , peer ) ;
break ;
case tr_peer_event : : Type : : ClientGotBlock :
{
auto * const tor = s - > tor ;
auto const loc = tor - > piece_loc ( event . pieceIndex , event . offset ) ;
2023-10-30 02:32:29 -04:00
s - > cancel_all_requests_for_block ( loc . block , peer ) ;
2023-08-01 22:56:26 +08:00
peer - > blocks_sent_to_client . add ( tr_time ( ) , 1 ) ;
2023-11-25 20:00:20 -06:00
tor - > on_block_received ( loc . block ) ;
2024-02-05 14:14:34 +08:00
s - > got_block . emit ( tor , event . pieceIndex , loc . block ) ;
2023-08-01 22:56:26 +08:00
}
break ;
case tr_peer_event : : Type : : Error :
if ( event . err = = ERANGE | | event . err = = EMSGSIZE | | event . err = = ENOTCONN )
{
/* some protocol error from the peer */
peer - > do_purge = true ;
tr_logAddDebugSwarm (
s ,
fmt : : format (
2023-10-01 23:43:17 +08:00
" setting {} do_purge flag because we got [({}) {}] " ,
peer - > display_name ( ) ,
event . err ,
tr_strerror ( event . err ) ) ) ;
2023-08-01 22:56:26 +08:00
}
else
{
2023-10-01 23:43:17 +08:00
tr_logAddDebugSwarm ( s , fmt : : format ( " unhandled error: ({}) {} " , event . err , tr_strerror ( event . err ) ) ) ;
2023-08-01 22:56:26 +08:00
}
break ;
default :
TR_ASSERT_MSG ( false , " This should be unreachable code " ) ;
break ;
}
}
static void on_client_got_piece_data ( tr_torrent * const tor , uint32_t const sent_length , time_t const now )
{
2023-11-15 18:53:43 -06:00
tor - > bytes_downloaded_ + = sent_length ;
2023-08-01 22:56:26 +08:00
tor - > set_date_active ( now ) ;
tor - > session - > add_downloaded ( sent_length ) ;
}
void on_got_port ( tr_peerMsgs * const msgs , tr_peer_event const & event , bool was_connectable )
{
auto & info_this = * msgs - > peer_info ;
TR_ASSERT ( info_this . is_connected ( ) ) ;
TR_ASSERT ( was_connectable ! = std : : empty ( info_this . listen_port ( ) ) ) ;
// If we already know about this peer, merge the info objects without invalidating references
if ( auto it_that = connectable_pool . find ( { info_this . listen_address ( ) , event . port } ) ;
it_that ! = std : : end ( connectable_pool ) )
{
auto & info_that = it_that - > second ;
TR_ASSERT ( it_that - > first = = info_that . listen_socket_address ( ) ) ;
TR_ASSERT ( it_that - > first . address ( ) = = info_this . listen_address ( ) ) ;
TR_ASSERT ( it_that - > first . port ( ) ! = info_this . listen_port ( ) ) ;
// If there is an existing connection to this peer, keep the better one
if ( info_that . is_connected ( ) & & on_got_port_duplicate_connection ( msgs , it_that , was_connectable ) )
{
return ;
}
info_this . merge ( info_that ) ;
2023-11-08 18:17:00 +01:00
auto from = info_that . from_first ( ) ;
stats . known_peer_from_count [ from ] - = connectable_pool . erase ( info_that . listen_socket_address ( ) ) ;
2023-08-01 22:56:26 +08:00
}
else if ( ! was_connectable )
{
info_this . set_connectable ( ) ;
}
auto nh = was_connectable ? connectable_pool . extract ( info_this . listen_socket_address ( ) ) :
incoming_pool . extract ( msgs - > socket_address ( ) ) ;
TR_ASSERT ( ! std : : empty ( nh ) ) ;
if ( was_connectable )
{
TR_ASSERT ( nh . key ( ) = = nh . mapped ( ) . listen_socket_address ( ) ) ;
}
else
{
2023-11-08 18:17:00 +01:00
+ + stats . known_peer_from_count [ nh . mapped ( ) . from_first ( ) ] ;
2023-08-01 22:56:26 +08:00
TR_ASSERT ( nh . key ( ) . address ( ) = = nh . mapped ( ) . listen_address ( ) ) ;
}
nh . key ( ) . port_ = event . port ;
[[maybe_unused]] auto const inserted = connectable_pool . insert ( std : : move ( nh ) ) . inserted ;
TR_ASSERT ( inserted ) ;
info_this . set_listen_port ( event . port ) ;
mark_all_seeds_flag_dirty ( ) ;
}
bool on_got_port_duplicate_connection ( tr_peerMsgs * const msgs , Pool : : iterator & it_that , bool was_connectable )
{
auto & info_this = * msgs - > peer_info ;
auto & info_that = it_that - > second ;
TR_ASSERT ( info_that . is_connected ( ) ) ;
if ( CompareAtomsByUsefulness ( info_this , info_that ) )
{
auto it = std : : find_if (
std : : begin ( peers ) ,
std : : end ( peers ) ,
[ & info_that ] ( tr_peerMsgs const * const peer ) { return peer - > peer_info = = & info_that ; } ) ;
TR_ASSERT ( it ! = std : : end ( peers ) ) ;
( * it ) - > do_purge = true ;
2023-11-08 18:17:00 +01:00
- - stats . known_peer_from_count [ info_that . from_first ( ) ] ;
2023-09-11 10:12:11 +08:00
// Note that it_that is invalid after this point
graveyard_pool . insert ( connectable_pool . extract ( it_that ) ) ;
2023-08-01 22:56:26 +08:00
return false ;
}
info_that . merge ( info_this ) ;
msgs - > do_purge = true ;
if ( was_connectable )
{
2023-11-08 18:17:00 +01:00
- - stats . known_peer_from_count [ info_this . from_first ( ) ] ;
2023-08-01 22:56:26 +08:00
graveyard_pool . insert ( connectable_pool . extract ( info_this . listen_socket_address ( ) ) ) ;
}
mark_all_seeds_flag_dirty ( ) ;
return true ;
}
// ---
2022-06-28 01:22:34 -05:00
// number of bad pieces a peer is allowed to send before we ban them
2024-02-13 10:42:19 -06:00
static auto constexpr MaxBadPiecesPerPeer = 5 ;
2022-06-28 01:22:34 -05:00
2022-06-28 15:24:39 -05:00
// how long we'll let requests we've made linger before we cancel them
2024-02-13 10:42:19 -06:00
static auto constexpr RequestTtlSecs = 90 ;
2022-06-28 15:24:39 -05:00
2023-07-04 15:47:18 -05:00
std : : array < libtransmission : : ObserverTag , 8 > const tags_ ;
2023-10-30 02:32:29 -04:00
// tr_peerMsgs hold pointers to the items in these containers,
// therefore references to elements within cannot invalidate
Pool graveyard_pool ;
2022-06-28 01:22:34 -05:00
mutable std : : optional < bool > pool_is_all_seeds_ ;
bool is_endgame_ = false ;
2021-10-06 09:26:07 -05:00
} ;
2007-09-20 16:32:01 +00:00
2024-02-05 14:14:34 +08:00
bool tr_swarm : : WishlistMediator : : client_has_block ( tr_block_index_t block ) const
{
return tor_ . has_block ( block ) ;
}
bool tr_swarm : : WishlistMediator : : client_wants_piece ( tr_piece_index_t piece ) const
{
return tor_ . piece_is_wanted ( piece ) ;
}
bool tr_swarm : : WishlistMediator : : is_endgame ( ) const
{
return swarm_ . is_endgame ( ) ;
}
bool tr_swarm : : WishlistMediator : : is_sequential_download ( ) const
{
return tor_ . is_sequential_download ( ) ;
}
size_t tr_swarm : : WishlistMediator : : count_active_requests ( tr_block_index_t block ) const
{
return swarm_ . active_requests . count ( block ) ;
}
size_t tr_swarm : : WishlistMediator : : count_missing_blocks ( tr_piece_index_t piece ) const
{
return tor_ . count_missing_blocks_in_piece ( piece ) ;
}
size_t tr_swarm : : WishlistMediator : : count_piece_replication ( tr_piece_index_t piece ) const
{
return std : : accumulate (
std : : begin ( swarm_ . peers ) ,
std : : end ( swarm_ . peers ) ,
size_t { } ,
2024-03-16 08:52:09 +08:00
[ piece ] ( size_t acc , tr_peer * peer ) { return acc + ( peer - > has_piece ( piece ) ? 1U : 0U ) ; } ) ;
2024-02-05 14:14:34 +08:00
}
tr_block_span_t tr_swarm : : WishlistMediator : : block_span ( tr_piece_index_t piece ) const
{
return tor_ . block_span_for_piece ( piece ) ;
}
tr_piece_index_t tr_swarm : : WishlistMediator : : piece_count ( ) const
{
return tor_ . piece_count ( ) ;
}
tr_priority_t tr_swarm : : WishlistMediator : : priority ( tr_piece_index_t piece ) const
{
return tor_ . piece_priority ( piece ) ;
}
libtransmission : : ObserverTag tr_swarm : : WishlistMediator : : observe_peer_disconnect (
libtransmission : : SimpleObservable < tr_torrent * , tr_bitfield const & > : : Observer observer )
{
return swarm_ . peer_disconnect . observe ( std : : move ( observer ) ) ;
}
libtransmission : : ObserverTag tr_swarm : : WishlistMediator : : observe_got_bitfield (
libtransmission : : SimpleObservable < tr_torrent * , tr_bitfield const & > : : Observer observer )
{
return swarm_ . got_bitfield . observe ( std : : move ( observer ) ) ;
}
libtransmission : : ObserverTag tr_swarm : : WishlistMediator : : observe_got_block (
libtransmission : : SimpleObservable < tr_torrent * , tr_piece_index_t , tr_block_index_t > : : Observer observer )
{
return swarm_ . got_block . observe ( std : : move ( observer ) ) ;
}
libtransmission : : ObserverTag tr_swarm : : WishlistMediator : : observe_got_have (
libtransmission : : SimpleObservable < tr_torrent * , tr_piece_index_t > : : Observer observer )
{
return swarm_ . got_have . observe ( std : : move ( observer ) ) ;
}
libtransmission : : ObserverTag tr_swarm : : WishlistMediator : : observe_got_have_all (
libtransmission : : SimpleObservable < tr_torrent * > : : Observer observer )
{
return swarm_ . got_have_all . observe ( std : : move ( observer ) ) ;
}
libtransmission : : ObserverTag tr_swarm : : WishlistMediator : : observe_piece_completed (
libtransmission : : SimpleObservable < tr_torrent * , tr_piece_index_t > : : Observer observer )
{
return tor_ . piece_completed_ . observe ( std : : move ( observer ) ) ;
}
libtransmission : : ObserverTag tr_swarm : : WishlistMediator : : observe_priority_changed (
libtransmission : : SimpleObservable < tr_torrent * , tr_file_index_t const * , tr_file_index_t , tr_priority_t > : : Observer observer )
{
return tor_ . priority_changed_ . observe ( std : : move ( observer ) ) ;
}
libtransmission : : ObserverTag tr_swarm : : WishlistMediator : : observe_sequential_download_changed (
libtransmission : : SimpleObservable < tr_torrent * , bool > : : Observer observer )
{
return tor_ . sequential_download_changed_ . observe ( std : : move ( observer ) ) ;
}
// ---
2007-09-20 16:32:01 +00:00
struct tr_peerMgr
{
2023-07-05 23:16:18 -05:00
private :
static auto constexpr BandwidthTimerPeriod = 500 ms ;
static auto constexpr RechokePeriod = 10 s ;
static auto constexpr RefillUpkeepPeriod = 10 s ;
// Max number of outbound peer connections to initiate.
// This throttle is an arbitrary number to avoid overloading routers.
static auto constexpr MaxConnectionsPerSecond = size_t { 18U } ;
static auto constexpr MaxConnectionsPerPulse = size_t ( MaxConnectionsPerSecond * BandwidthTimerPeriod / 1 s ) ;
// Building a peer candidate list is expensive, so cache it across pulses.
// We want to cache it long enough to avoid excess CPU cycles,
// but short enough that the data isn't too stale.
static auto constexpr OutboundCandidatesListTtl = BandwidthTimerPeriod * 4U ;
// How big the candidate list should be when we create it.
static auto constexpr OutboundCandidateListCapacity = MaxConnectionsPerPulse * OutboundCandidatesListTtl /
BandwidthTimerPeriod ;
public :
// The peers we might try connecting to in the next few seconds.
// This list is cached between pulses so use resilient keys, e.g.
// a `tr_torrent_id_t` instead of a `tr_torrent*` that can be freed.
using OutboundCandidates = small : :
max_size_vector < std : : pair < tr_torrent_id_t , tr_socket_address > , OutboundCandidateListCapacity > ;
2023-10-31 19:20:01 -04:00
explicit tr_peerMgr (
tr_session * session_in ,
libtransmission : : TimerMaker & timer_maker ,
tr_torrents & torrents ,
libtransmission : : Blocklists & blocklist )
2022-02-26 00:03:32 -06:00
: session { session_in }
2023-10-24 15:24:52 -04:00
, torrents_ { torrents }
2023-10-31 19:20:01 -04:00
, blocklists_ { blocklist }
2023-10-24 15:24:52 -04:00
, handshake_mediator_ { * session , timer_maker , torrents }
2023-10-30 02:32:29 -04:00
, bandwidth_timer_ { timer_maker . create ( [ this ] ( ) { bandwidth_pulse ( ) ; } ) }
, rechoke_timer_ { timer_maker . create ( [ this ] ( ) { rechoke_pulse_marshall ( ) ; } ) }
, refill_upkeep_timer_ { timer_maker . create ( [ this ] ( ) { refill_upkeep ( ) ; } ) }
2023-10-31 19:20:01 -04:00
, blocklists_tag_ { blocklist . observe_changes ( [ this ] ( ) { on_blocklists_changed ( ) ; } ) }
2022-02-26 00:03:32 -06:00
{
2023-07-05 23:16:18 -05:00
bandwidth_timer_ - > start_repeating ( BandwidthTimerPeriod ) ;
2023-05-05 23:11:05 -05:00
rechoke_timer_ - > start_repeating ( RechokePeriod ) ;
refill_upkeep_timer_ - > start_repeating ( RefillUpkeepPeriod ) ;
2022-02-26 00:03:32 -06:00
}
2022-08-03 12:03:28 -05:00
tr_peerMgr ( tr_peerMgr & & ) = delete ;
tr_peerMgr ( tr_peerMgr const & ) = delete ;
tr_peerMgr & operator = ( tr_peerMgr & & ) = delete ;
tr_peerMgr & operator = ( tr_peerMgr const & ) = delete ;
2022-01-23 16:47:41 -06:00
[[nodiscard]] auto unique_lock ( ) const
2021-11-20 15:20:45 -06:00
{
return session - > unique_lock ( ) ;
}
2022-06-27 21:15:15 -05:00
~ tr_peerMgr ( )
{
auto const lock = unique_lock ( ) ;
2022-12-13 11:59:21 -06:00
incoming_handshakes . clear ( ) ;
2022-06-27 21:15:15 -05:00
}
void rechokeSoon ( ) noexcept
{
2023-05-05 23:11:05 -05:00
rechoke_timer_ - > set_interval ( 100 ms ) ;
2022-06-27 21:15:15 -05:00
}
2023-01-07 12:58:16 -06:00
[[nodiscard]] tr_swarm * get_existing_swarm ( tr_sha1_digest_t const & hash ) const
{
2023-10-24 15:24:52 -04:00
auto * const tor = torrents_ . get ( hash ) ;
2023-01-07 12:58:16 -06:00
return tor = = nullptr ? nullptr : tor - > swarm ;
}
2022-02-26 00:03:32 -06:00
tr_session * const session ;
2023-10-24 15:24:52 -04:00
tr_torrents & torrents_ ;
2023-10-31 19:20:01 -04:00
libtransmission : : Blocklists const & blocklists_ ;
2022-04-27 12:20:55 -05:00
Handshakes incoming_handshakes ;
2022-06-27 21:15:15 -05:00
2022-12-13 11:59:21 -06:00
HandshakeMediator handshake_mediator_ ;
2022-12-05 11:47:11 -06:00
2022-06-27 21:15:15 -05:00
private :
2023-10-30 02:32:29 -04:00
void bandwidth_pulse ( ) ;
void make_new_peer_connections ( ) ;
void rechoke_pulse ( ) const ;
void reconnect_pulse ( ) ;
void refill_upkeep ( ) const ;
void rechoke_pulse_marshall ( )
2022-11-07 14:15:35 +08:00
{
2023-10-30 02:32:29 -04:00
rechoke_pulse ( ) ;
2023-05-05 23:11:05 -05:00
rechoke_timer_ - > set_interval ( RechokePeriod ) ;
2022-11-07 14:15:35 +08:00
}
2023-10-31 19:20:01 -04:00
void on_blocklists_changed ( ) const
2023-07-04 15:47:18 -05:00
{
/* we cache whether or not a peer is blocklisted...
since the blocklist has changed , erase that cached value */
2023-10-24 15:24:52 -04:00
for ( auto * const tor : torrents_ )
2023-07-04 15:47:18 -05:00
{
2023-08-01 22:56:26 +08:00
for ( auto & pool : { std : : ref ( tor - > swarm - > connectable_pool ) , std : : ref ( tor - > swarm - > incoming_pool ) } )
2023-07-04 15:47:18 -05:00
{
2023-08-01 22:56:26 +08:00
for ( auto & [ socket_address , atom ] : pool . get ( ) )
{
atom . set_blocklisted_dirty ( ) ;
}
2023-07-04 15:47:18 -05:00
}
}
}
2023-07-05 23:16:18 -05:00
OutboundCandidates outbound_candidates_ ;
2022-08-11 12:28:37 -05:00
std : : unique_ptr < libtransmission : : Timer > const bandwidth_timer_ ;
std : : unique_ptr < libtransmission : : Timer > const rechoke_timer_ ;
std : : unique_ptr < libtransmission : : Timer > const refill_upkeep_timer_ ;
2022-06-27 21:15:15 -05:00
2023-10-31 19:20:01 -04:00
libtransmission : : ObserverTag const blocklists_tag_ ;
2007-09-20 16:32:01 +00:00
} ;
2023-01-22 13:21:30 -06:00
// --- tr_peer virtual functions
2013-02-04 16:23:33 +00:00
2024-03-16 08:52:09 +08:00
tr_peer : : tr_peer ( tr_torrent const & tor )
: session { tor . session }
, swarm { tor . swarm }
, blame { tor . block_count ( ) }
2013-02-04 16:23:33 +00:00
{
}
2021-10-07 08:33:55 -05:00
tr_peer : : ~ tr_peer ( )
2013-02-04 16:23:33 +00:00
{
2021-10-07 08:33:55 -05:00
if ( swarm ! = nullptr )
2017-04-19 15:04:45 +03:00
{
2021-11-19 12:37:38 -06:00
swarm - > active_requests . remove ( this ) ;
2017-04-19 15:04:45 +03:00
}
2013-02-04 16:23:33 +00:00
}
2023-01-07 16:55:00 -06:00
// ---
2007-09-20 16:32:01 +00:00
2017-04-19 15:04:45 +03:00
tr_peerMgr * tr_peerMgrNew ( tr_session * session )
2007-09-20 16:32:01 +00:00
{
2023-10-31 19:20:01 -04:00
return new tr_peerMgr { session , session - > timerMaker ( ) , session - > torrents ( ) , session - > blocklist ( ) } ;
2007-09-20 16:32:01 +00:00
}
2017-04-19 15:04:45 +03:00
void tr_peerMgrFree ( tr_peerMgr * manager )
2007-09-20 16:32:01 +00:00
{
2022-02-26 00:03:32 -06:00
delete manager ;
2007-09-20 16:32:01 +00:00
}
2023-01-07 16:55:00 -06:00
// ---
2010-06-14 12:01:50 +00:00
2009-11-08 23:20:00 +00:00
/**
2023-01-22 13:21:30 -06:00
* REQUESTS
*
* There are two data structures associated with managing block requests :
2023-04-14 18:47:54 +02:00
*
2023-01-22 13:21:30 -06:00
* 1. tr_swarm : : active_requests , an opaque class that tracks what requests
* we currently have , i . e . which blocks and from which peers .
* This is used for cancelling requests that have been waiting
* for too long and avoiding duplicate requests .
*
2024-02-05 14:14:34 +08:00
* 2. tr_swarm : : wishlist , a class that tracks the pieces that we want to
* request . It ' s used to decide which blocks to return next when
* tr_peerMgrGetNextRequests ( ) is called .
2023-01-22 13:21:30 -06:00
*/
2009-11-08 23:20:00 +00:00
2023-01-22 13:21:30 -06:00
// --- struct block_request
2008-10-25 02:20:16 +00:00
2021-11-19 12:37:38 -06:00
// TODO: if we keep this, add equivalent API to ActiveRequest
2021-11-25 12:26:51 -06:00
void tr_peerMgrClientSentRequests ( tr_torrent * torrent , tr_peer * peer , tr_block_span_t span )
2011-03-28 16:31:05 +00:00
{
2021-11-19 12:37:38 -06:00
auto const now = tr_time ( ) ;
2017-04-19 15:04:45 +03:00
2021-11-25 12:26:51 -06:00
for ( tr_block_index_t block = span . begin ; block < span . end ; + + block )
2017-04-19 15:04:45 +03:00
{
2021-11-19 12:37:38 -06:00
torrent - > swarm - > active_requests . add ( block , peer , now ) ;
2017-04-19 15:04:45 +03:00
}
2009-02-25 13:04:51 +00:00
}
2021-12-01 17:11:57 -06:00
std : : vector < tr_block_span_t > tr_peerMgrGetNextRequests ( tr_torrent * torrent , tr_peer const * peer , size_t numwant )
2009-11-01 02:10:47 +00:00
{
2024-02-05 14:14:34 +08:00
TR_ASSERT ( ! torrent - > is_done ( ) ) ;
tr_swarm & swarm = * torrent - > swarm ;
if ( ! swarm . wishlist )
2011-02-17 05:14:53 +00:00
{
2024-02-05 14:14:34 +08:00
swarm . wishlist = std : : make_unique < Wishlist > ( std : : make_unique < tr_swarm : : WishlistMediator > ( swarm ) ) ;
}
2024-02-16 22:28:37 +08:00
swarm . update_endgame ( ) ;
2024-02-05 14:14:34 +08:00
return swarm . wishlist - > next (
numwant ,
2024-03-16 08:52:09 +08:00
[ peer ] ( tr_piece_index_t p ) { return peer - > has_piece ( p ) ; } ,
2024-02-05 14:14:34 +08:00
[ peer , & swarm ] ( tr_block_index_t b ) { return swarm . active_requests . has ( b , peer ) ; } ) ;
2010-03-03 04:16:18 +00:00
}
2010-01-12 20:17:22 +00:00
2023-01-22 13:21:30 -06:00
// --- Piece List Manipulation / Accessors
2010-01-12 20:17:22 +00:00
2021-11-19 12:37:38 -06:00
bool tr_peerMgrDidPeerRequest ( tr_torrent const * tor , tr_peer const * peer , tr_block_index_t block )
{
return tor - > swarm - > active_requests . has ( block , peer ) ;
2011-02-17 05:14:53 +00:00
}
2010-03-03 04:16:18 +00:00
2021-11-19 12:37:38 -06:00
size_t tr_peerMgrCountActiveRequestsToPeer ( tr_torrent const * tor , tr_peer const * peer )
2007-09-20 16:32:01 +00:00
{
2021-11-19 12:37:38 -06:00
return tor - > swarm - > active_requests . count ( peer ) ;
2009-11-08 23:20:00 +00:00
}
2023-10-30 02:32:29 -04:00
void tr_peerMgr : : refill_upkeep ( ) const
2009-11-08 23:20:00 +00:00
{
2022-06-27 21:15:15 -05:00
auto const lock = unique_lock ( ) ;
2009-11-08 23:20:00 +00:00
2023-10-24 15:24:52 -04:00
for ( auto * const tor : torrents_ )
2022-06-27 21:15:15 -05:00
{
2023-10-30 02:32:29 -04:00
tor - > swarm - > cancel_old_requests ( ) ;
2023-10-20 02:05:19 +08:00
tor - > swarm - > remove_inactive_peer_info ( ) ;
2022-06-27 21:15:15 -05:00
}
2008-10-11 04:07:50 +00:00
}
2023-01-07 16:55:00 -06:00
namespace
2008-06-07 21:26:41 +00:00
{
2023-01-07 16:55:00 -06:00
namespace handshake_helpers
2007-10-01 05:32:34 +00:00
{
2024-03-16 08:52:09 +08:00
void create_bit_torrent_peer ( tr_torrent & tor , std : : shared_ptr < tr_peerIo > io , tr_peer_info * peer_info , tr_interned_string client )
2013-02-04 16:23:33 +00:00
{
2023-07-14 07:06:25 -05:00
TR_ASSERT ( peer_info ! = nullptr ) ;
2024-03-16 08:52:09 +08:00
TR_ASSERT ( tor . swarm ! = nullptr ) ;
2013-02-04 16:23:33 +00:00
2024-03-16 08:52:09 +08:00
tr_swarm * swarm = tor . swarm ;
2013-02-04 16:23:33 +00:00
2024-03-16 08:52:09 +08:00
auto * peer = tr_peerMsgs : : create ( tor , peer_info , std : : move ( io ) , client , & tr_swarm : : peer_callback_bt , swarm ) ;
2013-02-04 16:23:33 +00:00
2022-04-29 08:32:15 -05:00
swarm - > peers . push_back ( peer ) ;
2022-04-27 20:06:51 -05:00
+ + swarm - > stats . peer_count ;
2023-07-14 07:06:25 -05:00
+ + swarm - > stats . peer_from_count [ peer_info - > from_first ( ) ] ;
2013-05-27 21:04:48 +00:00
2022-04-29 08:32:15 -05:00
TR_ASSERT ( swarm - > stats . peer_count = = swarm - > peerCount ( ) ) ;
2023-07-14 07:06:25 -05:00
TR_ASSERT ( swarm - > stats . peer_from_count [ peer_info - > from_first ( ) ] < = swarm - > stats . peer_count ) ;
2013-02-04 16:23:33 +00:00
}
2007-10-02 16:12:44 +00:00
/* FIXME: this is kind of a mess. */
2023-08-15 05:46:09 +08:00
[[nodiscard]] bool on_handshake_done ( tr_peerMgr * const manager , tr_handshake : : Result const & result )
2017-04-19 15:04:45 +03:00
{
2023-11-09 19:13:43 -06:00
auto const lock = manager - > unique_lock ( ) ;
2017-04-19 15:04:45 +03:00
2023-11-09 19:13:43 -06:00
TR_ASSERT ( result . io ! = nullptr ) ;
2023-06-23 04:21:44 +08:00
auto const & socket_address = result . io - > socket_address ( ) ;
2023-11-09 19:13:43 -06:00
auto * const swarm = manager - > get_existing_swarm ( result . io - > torrent_hash ( ) ) ;
2023-11-14 23:13:20 +08:00
auto * info = swarm ! = nullptr ? swarm - > get_existing_peer_info ( socket_address ) : nullptr ;
2023-10-15 01:58:15 +08:00
2022-12-16 01:23:12 -06:00
if ( result . io - > is_incoming ( ) )
2017-04-19 15:04:45 +03:00
{
2023-06-23 04:21:44 +08:00
manager - > incoming_handshakes . erase ( socket_address ) ;
2017-04-19 15:04:45 +03:00
}
2023-11-14 23:13:20 +08:00
else if ( info ! = nullptr )
2017-04-19 15:04:45 +03:00
{
2023-11-14 23:13:20 +08:00
info - > destroy_handshake ( ) ;
2017-04-19 15:04:45 +03:00
}
2023-11-09 19:13:43 -06:00
if ( ! result . is_connected | | swarm = = nullptr | | ! swarm - > is_running )
2017-04-19 15:04:45 +03:00
{
2023-11-14 23:13:20 +08:00
if ( info ! = nullptr & & ! info - > is_connected ( ) )
2017-04-19 15:04:45 +03:00
{
2023-11-14 23:13:20 +08:00
info - > on_connection_failed ( ) ;
2010-04-20 21:54:03 +00:00
2023-10-15 01:58:15 +08:00
if ( ! result . read_anything_from_peer )
{
tr_logAddTraceSwarm (
2023-11-09 19:13:43 -06:00
swarm ,
2023-10-15 01:58:15 +08:00
fmt : : format (
" marking peer {} as unreachable... num_fails is {} " ,
2023-11-14 23:13:20 +08:00
info - > display_name ( ) ,
info - > connection_failure_count ( ) ) ) ;
info - > set_connectable ( false ) ;
2010-04-20 21:54:03 +00:00
}
2007-11-08 04:11:09 +00:00
}
2023-11-09 19:13:43 -06:00
return false ;
2007-09-20 16:32:01 +00:00
}
2023-11-09 19:13:43 -06:00
2023-11-14 23:13:20 +08:00
if ( result . io - > is_incoming ( ) )
2007-09-29 06:37:03 +00:00
{
2023-11-14 23:13:20 +08:00
info = & swarm - > ensure_info_exists ( socket_address , 0U , TR_PEER_FROM_INCOMING , false ) ;
2023-11-09 19:13:43 -06:00
}
2007-10-11 00:09:58 +00:00
2023-11-14 23:13:20 +08:00
if ( info = = nullptr )
2023-11-09 19:13:43 -06:00
{
return false ;
}
2010-12-14 18:33:48 +00:00
2023-11-09 19:13:43 -06:00
if ( ! result . io - > is_incoming ( ) )
{
2023-11-14 23:13:20 +08:00
info - > set_connectable ( ) ;
2023-11-09 19:13:43 -06:00
}
2011-02-18 00:24:48 +00:00
2023-11-09 19:13:43 -06:00
// If we're connected via µTP, then we know the peer supports µTP...
if ( result . io - > is_utp ( ) )
{
2023-11-14 23:13:20 +08:00
info - > set_utp_supported ( ) ;
2023-11-09 19:13:43 -06:00
}
2008-11-25 21:35:17 +00:00
2023-11-14 23:13:20 +08:00
if ( info - > is_banned ( ) )
2023-11-09 19:13:43 -06:00
{
2023-11-14 23:13:20 +08:00
tr_logAddTraceSwarm ( swarm , fmt : : format ( " banned peer {} tried to reconnect " , info - > display_name ( ) ) ) ;
2023-11-09 19:13:43 -06:00
return false ;
}
2008-09-19 17:03:25 +00:00
2023-11-09 19:13:43 -06:00
if ( swarm - > peerCount ( ) > = swarm - > tor - > peer_limit ( ) ) // too many peers already
{
return false ;
}
2023-11-14 23:13:20 +08:00
if ( info - > is_connected ( ) ) // we're already connected to this peer; do nothing
2023-11-09 19:13:43 -06:00
{
return false ;
2007-09-20 16:32:01 +00:00
}
2007-09-29 06:37:03 +00:00
2023-11-09 19:13:43 -06:00
auto client = tr_interned_string { } ;
if ( result . peer_id )
{
auto buf = std : : array < char , 128 > { } ;
tr_clientForId ( std : : data ( buf ) , sizeof ( buf ) , * result . peer_id ) ;
client = tr_interned_string { tr_quark_new ( std : : data ( buf ) ) } ;
}
2023-11-25 20:00:20 -06:00
result . io - > set_bandwidth ( & swarm - > tor - > bandwidth ( ) ) ;
2024-03-16 08:52:09 +08:00
create_bit_torrent_peer ( * swarm - > tor , result . io , info , client ) ;
2023-11-09 19:13:43 -06:00
return true ;
2007-09-20 16:32:01 +00:00
}
2023-01-07 16:55:00 -06:00
} // namespace handshake_helpers
} // namespace
2007-09-20 16:32:01 +00:00
2022-12-06 10:28:28 -06:00
void tr_peerMgrAddIncoming ( tr_peerMgr * manager , tr_peer_socket & & socket )
2007-09-20 16:32:01 +00:00
{
2023-01-07 16:55:00 -06:00
using namespace handshake_helpers ;
2021-11-20 15:20:45 -06:00
auto const lock = manager - > unique_lock ( ) ;
2007-09-29 06:37:03 +00:00
2023-10-31 19:20:01 -04:00
if ( manager - > blocklists_ . contains ( socket . address ( ) ) )
2008-03-30 00:57:55 +00:00
{
2022-12-08 16:44:19 -06:00
tr_logAddTrace ( fmt : : format ( " Banned IP address '{}' tried to connect to us " , socket . display_name ( ) ) ) ;
2023-01-04 15:37:55 -06:00
socket . close ( ) ;
2008-03-30 00:57:55 +00:00
}
2023-08-01 22:56:26 +08:00
else if ( manager - > incoming_handshakes . count ( socket . socket_address ( ) ) ! = 0U )
2007-10-25 13:38:34 +00:00
{
2023-01-04 15:37:55 -06:00
socket . close ( ) ;
2007-10-25 13:38:34 +00:00
}
2023-10-31 19:20:01 -04:00
else // we don't have a connection to them yet...
2007-09-20 16:32:01 +00:00
{
2023-10-31 19:20:01 -04:00
auto const socket_address = socket . socket_address ( ) ;
auto * const session = manager - > session ;
2022-12-13 11:59:21 -06:00
manager - > incoming_handshakes . try_emplace (
2023-07-12 17:29:47 -05:00
socket_address ,
2022-12-13 11:59:21 -06:00
& manager - > handshake_mediator_ ,
2022-12-16 01:23:12 -06:00
tr_peerIo : : new_incoming ( session , & session - > top_bandwidth_ , std : : move ( socket ) ) ,
2022-12-13 11:59:21 -06:00
session - > encryptionMode ( ) ,
[ manager ] ( tr_handshake : : Result const & result ) { return on_handshake_done ( manager , result ) ; } ) ;
2007-09-20 16:32:01 +00:00
}
}
2023-07-14 07:06:25 -05:00
size_t tr_peerMgrAddPex ( tr_torrent * tor , tr_peer_from from , tr_pex const * pex , size_t n_pex )
2021-09-09 15:25:30 -05:00
{
size_t n_used = 0 ;
tr_swarm * s = tor - > swarm ;
2021-11-20 15:20:45 -06:00
auto const lock = s - > manager - > unique_lock ( ) ;
2021-09-09 15:25:30 -05:00
for ( tr_pex const * const end = pex + n_pex ; pex ! = end ; + + pex )
{
2022-07-09 18:44:20 -05:00
if ( tr_isPex ( pex ) & & /* safeguard against corrupt data */
2023-10-31 19:20:01 -04:00
! s - > manager - > blocklists_ . contains ( pex - > socket_address . address ( ) ) & & pex - > is_valid_for_peers ( ) & &
2023-08-18 11:13:01 +08:00
from ! = TR_PEER_FROM_INCOMING & & ( from ! = TR_PEER_FROM_PEX | | ( pex - > flags & ADDED_F_CONNECTABLE ) ! = 0 ) )
2017-04-19 15:04:45 +03:00
{
2023-08-01 22:56:26 +08:00
// 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
2023-08-18 11:13:01 +08:00
s - > ensure_info_exists ( pex - > socket_address , pex - > flags , from , true ) ;
2021-09-09 15:25:30 -05:00
+ + n_used ;
2017-04-19 15:04:45 +03:00
}
2008-12-17 03:38:02 +00:00
}
2021-09-09 15:25:30 -05:00
return n_used ;
2007-09-20 16:32:01 +00:00
}
2022-12-08 20:27:52 -06:00
std : : vector < tr_pex > tr_pex : : from_compact_ipv4 (
void const * compact ,
size_t compact_len ,
uint8_t const * added_f ,
size_t added_f_len )
2022-01-22 23:41:01 -06:00
{
2023-07-17 21:56:57 +08:00
size_t const n = compact_len / tr_socket_address : : CompactSockAddrBytes [ TR_AF_INET ] ;
2022-10-24 13:40:12 -05:00
auto const * walk = static_cast < std : : byte const * > ( compact ) ;
2022-01-22 23:41:01 -06:00
auto pex = std : : vector < tr_pex > ( n ) ;
for ( size_t i = 0 ; i < n ; + + i )
{
2023-08-18 11:13:01 +08:00
std : : tie ( pex [ i ] . socket_address , walk ) = tr_socket_address : : from_compact_ipv4 ( walk ) ;
2022-01-22 23:41:01 -06:00
if ( added_f ! = nullptr & & n = = added_f_len )
{
pex [ i ] . flags = added_f [ i ] ;
}
}
return pex ;
}
2022-12-08 20:27:52 -06:00
std : : vector < tr_pex > tr_pex : : from_compact_ipv6 (
void const * compact ,
size_t compact_len ,
uint8_t const * added_f ,
size_t added_f_len )
2022-01-22 23:41:01 -06:00
{
2023-07-17 21:56:57 +08:00
size_t const n = compact_len / tr_socket_address : : CompactSockAddrBytes [ TR_AF_INET6 ] ;
2022-10-24 13:40:12 -05:00
auto const * walk = static_cast < std : : byte const * > ( compact ) ;
2022-01-22 23:41:01 -06:00
auto pex = std : : vector < tr_pex > ( n ) ;
for ( size_t i = 0 ; i < n ; + + i )
{
2023-08-18 11:13:01 +08:00
std : : tie ( pex [ i ] . socket_address , walk ) = tr_socket_address : : from_compact_ipv6 ( walk ) ;
2022-01-22 23:41:01 -06:00
if ( added_f ! = nullptr & & n = = added_f_len )
{
pex [ i ] . flags = added_f [ i ] ;
}
}
return pex ;
}
2023-01-07 16:55:00 -06:00
// ---
2007-09-20 16:32:01 +00:00
2023-01-27 14:25:08 -06:00
namespace
{
2022-06-28 15:24:39 -05:00
namespace get_peers_helpers
{
2023-07-14 07:06:25 -05:00
[[nodiscard]] bool is_peer_interesting ( tr_torrent const * tor , tr_peer_info const & info )
2011-03-29 15:23:54 +00:00
{
2023-07-14 07:06:25 -05:00
if ( tor - > is_done ( ) & & info . is_seed ( ) )
2017-04-19 15:04:45 +03:00
{
return false ;
}
2011-03-29 15:23:54 +00:00
2023-10-15 01:58:15 +08:00
if ( info . is_in_use ( ) )
2017-04-19 15:04:45 +03:00
{
return true ;
}
2011-03-29 15:23:54 +00:00
2023-10-31 19:20:01 -04:00
if ( info . is_blocklisted ( tor - > session - > blocklist ( ) ) )
2017-04-19 15:04:45 +03:00
{
return false ;
}
2011-03-29 15:23:54 +00:00
2023-07-14 07:06:25 -05:00
if ( info . is_banned ( ) )
2017-04-19 15:04:45 +03:00
{
return false ;
}
2011-03-29 15:23:54 +00:00
2017-04-19 15:04:45 +03:00
return true ;
2011-03-29 15:23:54 +00:00
}
2022-06-28 15:24:39 -05:00
} // namespace get_peers_helpers
2023-01-27 14:25:08 -06:00
} // namespace
2022-06-28 15:24:39 -05:00
2022-08-03 01:15:37 -05:00
std : : vector < tr_pex > tr_peerMgrGetPeers ( tr_torrent const * tor , uint8_t address_type , uint8_t list_mode , size_t max_peer_count )
2007-09-20 16:32:01 +00:00
{
2022-06-28 15:24:39 -05:00
using namespace get_peers_helpers ;
2017-06-13 05:24:09 +03:00
TR_ASSERT ( tr_isTorrent ( tor ) ) ;
2021-11-20 15:20:45 -06:00
auto const lock = tor - > unique_lock ( ) ;
2022-08-03 01:15:37 -05:00
TR_ASSERT ( address_type = = TR_AF_INET | | address_type = = TR_AF_INET6 ) ;
2017-06-13 05:24:09 +03:00
TR_ASSERT ( list_mode = = TR_PEERS_CONNECTED | | list_mode = = TR_PEERS_INTERESTING ) ;
2017-04-20 19:02:19 +03:00
tr_swarm const * s = tor - > swarm ;
2007-09-29 06:37:03 +00:00
2023-08-14 11:02:11 +08:00
// build a list of peer info objects
2009-11-20 07:47:31 +00:00
2023-07-14 07:06:25 -05:00
auto infos = std : : vector < tr_peer_info const * > { } ;
2017-04-19 15:04:45 +03:00
if ( list_mode = = TR_PEERS_CONNECTED ) /* connected peers only */
2008-10-05 22:51:18 +00:00
{
2023-04-14 16:03:08 -05:00
auto const & peers = s - > peers ;
2023-07-14 07:06:25 -05:00
infos . reserve ( std : : size ( peers ) ) ;
2023-08-14 11:02:11 +08:00
for ( auto const * peer : peers )
{
if ( peer - > socket_address ( ) . address ( ) . type = = address_type )
{
infos . emplace_back ( peer - > peer_info ) ;
}
}
2009-11-20 07:47:31 +00:00
}
2017-04-19 15:04:45 +03:00
else /* TR_PEERS_INTERESTING */
2009-11-20 07:47:31 +00:00
{
2023-08-14 11:02:11 +08:00
auto const & pool = s - > connectable_pool ;
infos . reserve ( std : : size ( pool ) ) ;
for ( auto const & [ socket_address , peer_info ] : pool )
2017-04-19 15:04:45 +03:00
{
2023-08-14 11:02:11 +08:00
TR_ASSERT ( socket_address = = peer_info . listen_socket_address ( ) ) ;
if ( socket_address . address ( ) . type = = address_type & & is_peer_interesting ( tor , peer_info ) )
2017-04-19 15:04:45 +03:00
{
2023-08-14 11:02:11 +08:00
infos . emplace_back ( & peer_info ) ;
2017-04-19 15:04:45 +03:00
}
}
2009-11-20 07:47:31 +00:00
}
2007-09-20 16:32:01 +00:00
2023-07-15 21:18:37 +08:00
// add the N most useful peers into our return list
2009-11-20 07:47:31 +00:00
2023-07-14 07:06:25 -05:00
auto const n = std : : min ( std : : size ( infos ) , max_peer_count ) ;
2022-05-26 20:29:10 -05:00
auto pex = std : : vector < tr_pex > { } ;
pex . reserve ( n ) ;
2009-11-20 07:47:31 +00:00
2023-07-15 21:18:37 +08:00
std : : partial_sort ( std : : begin ( infos ) , std : : begin ( infos ) + n , std : : end ( infos ) , CompareAtomsByUsefulness ) ;
infos . resize ( n ) ;
for ( auto const * const info : infos )
2009-11-20 07:47:31 +00:00
{
2023-08-18 11:13:01 +08:00
auto const & socket_address = info - > listen_socket_address ( ) ;
2023-09-16 09:23:34 +08:00
[[maybe_unused]] auto const & addr = socket_address . address ( ) ;
2017-04-19 15:04:45 +03:00
2023-08-14 11:02:11 +08:00
TR_ASSERT ( addr . is_valid ( ) ) ;
TR_ASSERT ( addr . type = = address_type ) ;
2023-08-18 11:13:01 +08:00
pex . emplace_back ( socket_address , info - > pex_flags ( ) ) ;
2009-11-20 07:47:31 +00:00
}
2007-09-20 16:32:01 +00:00
2022-05-26 20:29:10 -05:00
std : : sort ( std : : begin ( pex ) , std : : end ( pex ) ) ;
return pex ;
2007-09-20 16:32:01 +00:00
}
2023-07-04 15:47:18 -05:00
void tr_swarm : : on_torrent_started ( )
2007-09-20 16:32:01 +00:00
{
2023-10-23 11:34:30 -04:00
auto const lock = unique_lock ( ) ;
2023-07-04 15:47:18 -05:00
is_running = true ;
manager - > rechokeSoon ( ) ;
2007-09-20 16:32:01 +00:00
}
2023-07-04 15:47:18 -05:00
void tr_swarm : : on_torrent_stopped ( )
2007-09-29 06:37:03 +00:00
{
2023-07-04 15:47:18 -05:00
stop ( ) ;
2007-09-20 16:32:01 +00:00
}
2017-04-19 15:04:45 +03:00
void tr_peerMgrAddTorrent ( tr_peerMgr * manager , tr_torrent * tor )
2007-09-20 16:32:01 +00:00
{
2017-06-08 10:24:12 +03:00
TR_ASSERT ( tr_isTorrent ( tor ) ) ;
2021-11-20 15:20:45 -06:00
auto const lock = tor - > unique_lock ( ) ;
2021-09-15 02:18:09 +02:00
TR_ASSERT ( tor - > swarm = = nullptr ) ;
2007-09-20 16:32:01 +00:00
2022-06-28 15:24:39 -05:00
tor - > swarm = new tr_swarm { manager , tor } ;
2007-09-20 16:32:01 +00:00
}
2022-06-11 12:06:07 -05:00
int8_t tr_peerMgrPieceAvailability ( tr_torrent const * tor , tr_piece_index_t piece )
{
2023-04-22 20:25:55 -05:00
if ( ! tor - > has_metainfo ( ) )
2022-06-11 12:06:07 -05:00
{
return 0 ;
}
2023-04-22 20:25:55 -05:00
if ( tor - > is_seed ( ) | | tor - > has_piece ( piece ) )
2022-06-11 12:06:07 -05:00
{
return - 1 ;
}
auto const & peers = tor - > swarm - > peers ;
2024-03-16 08:52:09 +08:00
return std : : count_if ( std : : begin ( peers ) , std : : end ( peers ) , [ piece ] ( auto const * peer ) { return peer - > has_piece ( piece ) ; } ) ;
2022-06-11 12:06:07 -05:00
}
void tr_peerMgrTorrentAvailability ( tr_torrent const * tor , int8_t * tab , unsigned int n_tabs )
2011-01-18 22:51:29 +00:00
{
2017-06-08 10:24:12 +03:00
TR_ASSERT ( tr_isTorrent ( tor ) ) ;
2021-09-15 02:18:09 +02:00
TR_ASSERT ( tab ! = nullptr ) ;
2022-06-11 12:06:07 -05:00
TR_ASSERT ( n_tabs > 0 ) ;
2007-09-20 16:32:01 +00:00
2022-06-11 12:06:07 -05:00
std : : fill_n ( tab , n_tabs , int8_t { } ) ;
2012-12-14 20:04:37 +00:00
2023-04-22 20:25:55 -05:00
auto const interval = tor - > piece_count ( ) / static_cast < float > ( n_tabs ) ;
2022-06-11 12:06:07 -05:00
for ( tr_piece_index_t i = 0 ; i < n_tabs ; + + i )
2007-09-20 16:32:01 +00:00
{
2022-06-11 12:06:07 -05:00
auto const piece = static_cast < tr_piece_index_t > ( i * interval ) ;
tab [ i ] = tr_peerMgrPieceAvailability ( tor , piece ) ;
2007-09-20 16:32:01 +00:00
}
}
2023-04-14 16:03:08 -05:00
tr_swarm_stats tr_swarmGetStats ( tr_swarm const * const swarm )
2013-07-08 16:41:12 +00:00
{
2021-09-15 02:18:09 +02:00
TR_ASSERT ( swarm ! = nullptr ) ;
2013-07-08 16:41:12 +00:00
2023-04-14 16:03:08 -05:00
auto count_active_peers = [ & swarm ] ( tr_direction dir )
2017-04-19 15:04:45 +03:00
{
2023-04-14 16:03:08 -05:00
return std : : count_if (
std : : begin ( swarm - > peers ) ,
std : : end ( swarm - > peers ) ,
[ dir ] ( auto const & peer ) { return peer - > is_active ( dir ) ; } ) ;
} ;
2013-07-08 16:41:12 +00:00
2023-04-14 16:03:08 -05:00
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 ) ;
2023-10-30 02:32:29 -04:00
stats . active_webseed_count = swarm - > count_active_webseeds ( tr_time_msec ( ) ) ;
2023-04-14 16:03:08 -05:00
return stats ;
2013-07-08 16:41:12 +00:00
}
2011-07-02 13:20:17 +00:00
/* count how many bytes we want that connected peers have */
2017-04-20 19:02:19 +03:00
uint64_t tr_peerMgrGetDesiredAvailable ( tr_torrent const * tor )
2011-03-28 16:31:05 +00:00
{
2017-06-08 10:24:12 +03:00
TR_ASSERT ( tr_isTorrent ( tor ) ) ;
2011-03-28 16:31:05 +00:00
2021-10-25 10:29:19 -05:00
// common shortcuts...
2011-03-28 16:31:05 +00:00
2023-04-22 20:25:55 -05:00
if ( ! tor - > is_running ( ) | | tor - > is_stopping ( ) | | tor - > is_done ( ) | | ! tor - > has_metainfo ( ) )
2017-04-19 15:04:45 +03:00
{
return 0 ;
}
2011-03-28 16:31:05 +00:00
2022-11-22 23:26:10 -06:00
tr_swarm const * const swarm = tor - > swarm ;
2023-04-14 16:03:08 -05:00
if ( swarm = = nullptr | | std : : empty ( swarm - > peers ) )
2013-01-27 21:03:52 +00:00
{
2017-04-19 15:04:45 +03:00
return 0 ;
2013-01-27 21:03:52 +00:00
}
2017-04-19 15:04:45 +03:00
2022-11-22 23:26:10 -06:00
auto available = swarm - > peers . front ( ) - > has ( ) ;
for ( auto const * const peer : swarm - > peers )
2013-01-27 21:03:52 +00:00
{
2022-11-22 23:26:10 -06:00
available | = peer - > has ( ) ;
2011-03-28 16:31:05 +00:00
}
2023-04-22 20:25:55 -05:00
if ( available . has_all ( ) )
2021-10-14 14:26:38 -05:00
{
2023-04-22 20:25:55 -05:00
return tor - > left_until_done ( ) ;
2017-04-19 15:04:45 +03:00
}
2021-10-25 10:29:19 -05:00
auto desired_available = uint64_t { } ;
2017-04-19 15:04:45 +03:00
2023-04-22 20:25:55 -05:00
for ( tr_piece_index_t i = 0 , n = tor - > piece_count ( ) ; i < n ; + + i )
2017-04-19 15:04:45 +03:00
{
2023-04-22 20:25:55 -05:00
if ( tor - > piece_is_wanted ( i ) & & available . test ( i ) )
2017-04-19 15:04:45 +03:00
{
2023-04-22 20:25:55 -05:00
desired_available + = tor - > count_missing_bytes_in_piece ( i ) ;
2017-04-19 15:04:45 +03:00
}
}
2023-04-22 20:25:55 -05:00
TR_ASSERT ( desired_available < = tor - > total_size ( ) ) ;
2021-10-25 10:29:19 -05:00
return desired_available ;
2017-04-19 15:04:45 +03:00
}
2021-12-07 12:11:28 -06:00
tr_webseed_view tr_peerMgrWebseed ( tr_torrent const * tor , size_t i )
2008-06-10 01:38:12 +00:00
{
2017-06-08 10:24:12 +03:00
TR_ASSERT ( tr_isTorrent ( tor ) ) ;
2021-12-07 12:11:28 -06:00
TR_ASSERT ( tor - > swarm ! = nullptr ) ;
2022-04-17 17:29:06 -05:00
size_t const n = std : : size ( tor - > swarm - > webseeds ) ;
2021-12-07 12:11:28 -06:00
TR_ASSERT ( i < n ) ;
2012-12-12 20:22:57 +00:00
2022-04-17 17:29:06 -05:00
return i > = n ? tr_webseed_view { } : tr_webseedView ( tor - > swarm - > webseeds [ i ] . get ( ) ) ;
2007-09-20 16:32:01 +00:00
}
2023-01-27 14:25:08 -06:00
namespace
{
2022-06-28 15:24:39 -05:00
namespace peer_stat_helpers
{
2023-04-14 16:03:08 -05:00
[[nodiscard]] auto get_peer_stats ( tr_peerMsgs const * peer , time_t now , uint64_t now_msec )
2007-09-20 16:32:01 +00:00
{
2021-10-27 20:16:24 -05:00
auto stats = tr_peer_stat { } ;
2012-12-12 20:22:57 +00:00
2023-08-01 22:56:26 +08:00
auto const [ addr , port ] = peer - > socket_address ( ) ;
2022-06-01 21:33:33 -05:00
2022-12-08 16:44:19 -06:00
addr . display_name ( stats . addr , sizeof ( stats . addr ) ) ;
2023-04-14 16:03:08 -05:00
stats . client = peer - > user_agent ( ) . c_str ( ) ;
2022-06-01 21:33:33 -05:00
stats . port = port . host ( ) ;
2023-07-14 07:06:25 -05:00
stats . from = peer - > peer_info - > from_first ( ) ;
2024-03-16 08:52:09 +08:00
stats . progress = peer - > percent_done ( ) ;
2021-10-27 20:16:24 -05:00
stats . isUTP = peer - > is_utp_connection ( ) ;
stats . isEncrypted = peer - > is_encrypted ( ) ;
2023-11-12 14:38:27 -06:00
stats . rateToPeer_KBps = peer - > get_piece_speed ( now_msec , TR_CLIENT_TO_PEER ) . count ( Speed : : Units : : KByps ) ;
stats . rateToClient_KBps = peer - > get_piece_speed ( now_msec , TR_PEER_TO_CLIENT ) . count ( Speed : : Units : : KByps ) ;
2023-04-14 16:03:08 -05:00
stats . peerIsChoked = peer - > peer_is_choked ( ) ;
stats . peerIsInterested = peer - > peer_is_interested ( ) ;
stats . clientIsChoked = peer - > client_is_choked ( ) ;
stats . clientIsInterested = peer - > client_is_interested ( ) ;
2021-10-27 20:16:24 -05:00
stats . isIncoming = peer - > is_incoming_connection ( ) ;
stats . isDownloadingFrom = peer - > is_active ( TR_PEER_TO_CLIENT ) ;
stats . isUploadingTo = peer - > is_active ( TR_CLIENT_TO_PEER ) ;
2024-03-16 08:52:09 +08:00
stats . isSeed = peer - > is_seed ( ) ;
2017-06-13 05:24:09 +03:00
2022-06-27 14:12:31 -05:00
stats . blocksToPeer = peer - > blocks_sent_to_peer . count ( now , CancelHistorySec ) ;
stats . blocksToClient = peer - > blocks_sent_to_client . count ( now , CancelHistorySec ) ;
stats . cancelsToPeer = peer - > cancels_sent_to_peer . count ( now , CancelHistorySec ) ;
stats . cancelsToClient = peer - > cancels_sent_to_client . count ( now , CancelHistorySec ) ;
2012-12-12 20:22:57 +00:00
2024-03-16 08:52:09 +08:00
stats . activeReqsToPeer = peer - > active_req_count ( TR_CLIENT_TO_PEER ) ;
stats . activeReqsToClient = peer - > active_req_count ( TR_PEER_TO_CLIENT ) ;
2021-10-27 20:16:24 -05:00
char * pch = stats . flagStr ;
if ( stats . isUTP )
2012-12-14 20:04:37 +00:00
{
2021-10-27 20:16:24 -05:00
* pch + + = ' T ' ;
}
2017-04-19 15:04:45 +03:00
2021-10-27 20:16:24 -05:00
if ( peer - > swarm - > optimistic = = peer )
{
* pch + + = ' O ' ;
}
2017-04-19 15:04:45 +03:00
2021-10-27 20:16:24 -05:00
if ( stats . isDownloadingFrom )
{
* pch + + = ' D ' ;
}
else if ( stats . clientIsInterested )
{
* pch + + = ' d ' ;
}
2017-04-19 15:04:45 +03:00
2021-10-27 20:16:24 -05:00
if ( stats . isUploadingTo )
{
* pch + + = ' U ' ;
}
else if ( stats . peerIsInterested )
{
* pch + + = ' u ' ;
}
2017-04-19 15:04:45 +03:00
2021-10-27 20:16:24 -05:00
if ( ! stats . clientIsChoked & & ! stats . clientIsInterested )
{
* pch + + = ' K ' ;
}
2017-04-19 15:04:45 +03:00
2021-10-27 20:16:24 -05:00
if ( ! stats . peerIsChoked & & ! stats . peerIsInterested )
{
* pch + + = ' ? ' ;
}
2012-12-14 20:04:37 +00:00
2021-10-27 20:16:24 -05:00
if ( stats . isEncrypted )
{
* pch + + = ' E ' ;
}
2012-12-14 20:04:37 +00:00
2021-10-27 20:16:24 -05:00
if ( stats . from = = TR_PEER_FROM_DHT )
{
* pch + + = ' H ' ;
}
else if ( stats . from = = TR_PEER_FROM_PEX )
{
* pch + + = ' X ' ;
}
2012-12-14 20:04:37 +00:00
2021-10-27 20:16:24 -05:00
if ( stats . isIncoming )
{
* pch + + = ' I ' ;
}
2017-04-19 15:04:45 +03:00
2021-10-27 20:16:24 -05:00
* pch = ' \0 ' ;
return stats ;
}
2022-06-28 15:24:39 -05:00
} // namespace peer_stat_helpers
2023-01-27 14:25:08 -06:00
} // namespace
2022-06-28 15:24:39 -05:00
2022-10-26 00:14:42 +08:00
tr_peer_stat * tr_peerMgrPeerStats ( tr_torrent const * tor , size_t * setme_count )
2021-10-27 20:16:24 -05:00
{
2022-06-28 15:24:39 -05:00
using namespace peer_stat_helpers ;
2021-10-27 20:16:24 -05:00
TR_ASSERT ( tr_isTorrent ( tor ) ) ;
TR_ASSERT ( tor - > swarm - > manager ! = nullptr ) ;
2023-04-14 16:03:08 -05:00
auto const peers = tor - > swarm - > peers ;
auto const n = std : : size ( peers ) ;
2022-08-11 19:59:58 -05:00
auto * const ret = new tr_peer_stat [ n ] ;
2021-10-27 20:16:24 -05:00
2024-03-16 08:04:43 +08:00
// TODO: re-implement as a callback solution (similar to tr_sessionSetCompletenessCallback) in case present call to run_in_session_thread is causing hangs when the peers info window is displayed.
auto done_promise = std : : promise < void > { } ;
auto done_future = done_promise . get_future ( ) ;
tor - > session - > run_in_session_thread (
[ & peers , & ret , & done_promise ] ( )
{
auto const now = tr_time ( ) ;
auto const now_msec = tr_time_msec ( ) ;
std : : transform (
std : : begin ( peers ) ,
std : : end ( peers ) ,
ret ,
[ & now , & now_msec ] ( auto const * peer ) { return get_peer_stats ( peer , now , now_msec ) ; } ) ;
done_promise . set_value ( ) ;
} ) ;
done_future . wait ( ) ;
2012-12-14 20:04:37 +00:00
2022-04-29 08:32:15 -05:00
* setme_count = n ;
2017-04-19 15:04:45 +03:00
return ret ;
2007-09-20 16:32:01 +00:00
}
2022-06-28 15:24:39 -05:00
namespace
{
2023-03-10 13:21:51 -06:00
namespace update_interest_helpers
2023-01-27 14:25:08 -06:00
{
2010-03-08 04:29:58 +00:00
/* does this peer have any pieces that we want? */
2022-06-28 15:24:39 -05:00
[[nodiscard]] bool isPeerInteresting (
2022-09-06 12:52:58 -05:00
tr_torrent const * const tor ,
2022-08-11 19:59:58 -05:00
std : : vector < bool > const & piece_is_interesting ,
2022-06-28 15:24:39 -05:00
tr_peerMsgs const * const peer )
2010-03-08 04:29:58 +00:00
{
2017-04-19 15:04:45 +03:00
/* these cases should have already been handled by the calling code... */
2023-04-22 20:25:55 -05:00
TR_ASSERT ( ! tor - > is_done ( ) ) ;
TR_ASSERT ( tor - > client_can_download ( ) ) ;
2010-03-08 04:29:58 +00:00
2024-03-16 08:52:09 +08:00
if ( peer - > is_seed ( ) )
2017-04-19 15:04:45 +03:00
{
return true ;
}
2010-03-08 04:29:58 +00:00
2023-04-22 20:25:55 -05:00
for ( tr_piece_index_t i = 0 ; i < tor - > piece_count ( ) ; + + i )
2017-04-19 15:04:45 +03:00
{
2024-03-16 08:52:09 +08:00
if ( piece_is_interesting [ i ] & & peer - > has_piece ( i ) )
2017-04-19 15:04:45 +03:00
{
return true ;
}
}
2010-03-08 04:29:58 +00:00
2017-04-19 15:04:45 +03:00
return false ;
2010-03-08 04:29:58 +00:00
}
2023-03-10 13:21:51 -06:00
// determine which peers to show interest in
void updateInterest ( tr_swarm * swarm )
2017-04-19 15:04:45 +03:00
{
2023-03-10 13:21:51 -06:00
// sometimes this function isn't necessary
auto const * const tor = swarm - > tor ;
2023-04-22 20:25:55 -05:00
if ( tor - > is_done ( ) | | ! tor - > client_can_download ( ) )
2017-04-19 15:04:45 +03:00
{
return ;
}
2023-04-14 16:03:08 -05:00
if ( auto const & peers = swarm - > peers ; ! std : : empty ( peers ) )
2017-04-19 15:04:45 +03:00
{
2024-03-16 08:52:09 +08:00
auto const n = tor - > piece_count ( ) ;
2017-04-19 15:04:45 +03:00
2023-03-10 13:21:51 -06:00
// build a bitfield of interesting pieces...
2023-07-13 22:10:43 +08:00
auto piece_is_interesting = std : : vector < bool > ( n ) ;
2024-03-16 08:52:09 +08:00
for ( tr_piece_index_t i = 0U ; i < n ; + + i )
2017-04-19 15:04:45 +03:00
{
2023-04-22 20:25:55 -05:00
piece_is_interesting [ i ] = tor - > piece_is_wanted ( i ) & & ! tor - > has_piece ( i ) ;
2017-04-19 15:04:45 +03:00
}
2011-04-29 23:25:12 +00:00
2023-04-14 16:03:08 -05:00
for ( auto * const peer : peers )
2017-04-19 15:04:45 +03:00
{
2023-03-10 13:21:51 -06:00
peer - > set_interested ( isPeerInteresting ( tor , piece_is_interesting , peer ) ) ;
2011-04-29 23:25:12 +00:00
}
2010-03-08 04:29:58 +00:00
}
}
2023-03-10 13:21:51 -06:00
} // namespace update_interest_helpers
2023-01-27 14:25:08 -06:00
} // namespace
2022-06-28 15:24:39 -05:00
2023-01-07 16:55:00 -06:00
// ---
2022-06-28 15:24:39 -05:00
namespace
{
2023-01-27 14:25:08 -06:00
namespace rechoke_uploads_helpers
{
2007-10-03 16:42:43 +00:00
struct ChokeData
2007-09-20 16:32:01 +00:00
{
2023-11-12 14:38:27 -06:00
ChokeData ( tr_peerMsgs * msgs , Speed rate , uint8_t salt , bool is_interested , bool was_choked , bool is_choked )
: msgs_ { msgs }
, rate_ { rate }
, salt_ { salt }
, is_interested_ { is_interested }
, was_choked_ { was_choked }
, is_choked_ { is_choked }
2022-08-11 19:59:58 -05:00
{
}
2023-11-12 14:38:27 -06:00
tr_peerMsgs * msgs_ ;
Speed rate_ ;
uint8_t salt_ ;
bool is_interested_ ;
bool was_choked_ ;
bool is_choked_ ;
2008-06-27 02:42:44 +00:00
2022-07-27 18:08:18 -05:00
[[nodiscard]] constexpr auto compare ( ChokeData const & that ) const noexcept // <=>
2017-04-19 15:04:45 +03:00
{
2023-07-06 18:51:08 -05:00
// prefer higher overall speeds
2023-11-12 14:38:27 -06:00
if ( auto const val = tr_compare_3way ( rate_ , that . rate_ ) ; val ! = 0 )
2022-07-27 18:08:18 -05:00
{
2023-07-06 18:51:08 -05:00
return - val ;
2022-07-27 18:08:18 -05:00
}
2008-11-15 00:46:51 +00:00
2023-11-12 14:38:27 -06:00
if ( was_choked_ ! = that . was_choked_ ) // prefer unchoked
2022-07-27 18:08:18 -05:00
{
2023-11-12 14:38:27 -06:00
return was_choked_ ? 1 : - 1 ;
2022-07-27 18:08:18 -05:00
}
2023-11-12 14:38:27 -06:00
return tr_compare_3way ( salt_ , that . salt_ ) ;
2017-04-19 15:04:45 +03:00
}
2008-06-27 02:42:44 +00:00
2022-07-27 18:08:18 -05:00
[[nodiscard]] constexpr auto operator < ( ChokeData const & that ) const noexcept
2017-04-19 15:04:45 +03:00
{
2022-07-27 18:08:18 -05:00
return compare ( that ) < 0 ;
2017-04-19 15:04:45 +03:00
}
2022-07-27 18:08:18 -05:00
} ;
2007-09-20 16:32:01 +00:00
2010-06-23 04:36:16 +00:00
/* get a rate for deciding which peers to choke and unchoke. */
2023-11-12 14:38:27 -06:00
[[nodiscard]] auto get_rate ( tr_torrent const * tor , tr_peer const * peer , uint64_t now )
2010-06-23 04:36:16 +00:00
{
2023-04-22 20:25:55 -05:00
if ( tor - > is_done ( ) )
2017-04-19 15:04:45 +03:00
{
2023-11-12 14:38:27 -06:00
return peer - > get_piece_speed ( now , TR_CLIENT_TO_PEER ) ;
2017-04-19 15:04:45 +03:00
}
2022-06-01 22:36:00 -05:00
2023-11-12 14:38:27 -06:00
// downloading a private torrent... take upload speed into account
// because there may only be a small window of opportunity to share
2023-04-22 20:25:55 -05:00
if ( tor - > is_private ( ) )
2017-04-19 15:04:45 +03:00
{
2023-11-12 14:38:27 -06:00
return peer - > get_piece_speed ( now , TR_PEER_TO_CLIENT ) + peer - > get_piece_speed ( now , TR_CLIENT_TO_PEER ) ;
2017-04-19 15:04:45 +03:00
}
2010-06-23 04:36:16 +00:00
2023-11-12 14:38:27 -06:00
// downloading a public torrent
return peer - > get_piece_speed ( now , TR_PEER_TO_CLIENT ) ;
2010-06-23 04:36:16 +00:00
}
2022-06-28 15:24:39 -05:00
// an optimistically unchoked peer is immune from rechoking
// for this many calls to rechokeUploads().
auto constexpr OptimisticUnchokeMultiplier = uint8_t { 4 } ;
2021-10-14 14:26:38 -05:00
2022-06-28 15:24:39 -05:00
void rechokeUploads ( tr_swarm * s , uint64_t const now )
2013-01-27 21:03:52 +00:00
{
2022-06-28 15:24:39 -05:00
auto const lock = s - > unique_lock ( ) ;
2017-06-13 05:24:09 +03:00
2022-04-29 08:32:15 -05:00
auto & peers = s - > peers ;
2023-04-14 16:03:08 -05:00
auto const peer_count = std : : size ( peers ) ;
2022-08-11 19:59:58 -05:00
auto choked = std : : vector < ChokeData > { } ;
2022-09-07 11:04:28 -05:00
choked . reserve ( peer_count ) ;
2022-04-29 08:32:15 -05:00
auto const * const session = s - > manager - > session ;
2023-04-22 20:25:55 -05:00
bool const choke_all = ! s - > tor - > client_can_upload ( ) ;
2023-11-25 20:00:20 -06:00
bool const is_maxed_out = s - > tor - > bandwidth ( ) . is_maxed_out ( TR_UP , now ) ;
2013-01-27 21:03:52 +00:00
2017-04-19 15:04:45 +03:00
/* an optimistic unchoke peer's "optimistic"
2017-04-21 10:40:57 +03:00
* state lasts for N calls to rechokeUploads ( ) . */
2022-04-27 20:06:51 -05:00
if ( s - > optimistic_unchoke_time_scaler > 0 )
2017-04-19 15:04:45 +03:00
{
2022-04-27 20:06:51 -05:00
- - s - > optimistic_unchoke_time_scaler ;
2017-04-19 15:04:45 +03:00
}
else
{
2021-09-15 02:18:09 +02:00
s - > optimistic = nullptr ;
2017-04-19 15:04:45 +03:00
}
2010-10-13 03:56:25 +00:00
2017-04-19 15:04:45 +03:00
/* sort the peers by preference and rate */
2022-07-28 10:01:59 -05:00
auto salter = tr_salt_shaker { } ;
2022-04-29 08:32:15 -05:00
for ( auto * const peer : peers )
2007-09-22 00:22:10 +00:00
{
2024-03-16 08:52:09 +08:00
if ( peer - > is_seed ( ) )
2008-12-02 19:46:51 +00:00
{
2017-04-19 15:04:45 +03:00
/* choke seeds and partial seeds */
2021-10-07 08:33:55 -05:00
peer - > set_choke ( true ) ;
2008-12-02 19:46:51 +00:00
}
2022-09-07 11:04:28 -05:00
else if ( choke_all )
2008-12-02 19:46:51 +00:00
{
2017-04-19 15:04:45 +03:00
/* choke everyone if we're not uploading */
2021-10-07 08:33:55 -05:00
peer - > set_choke ( true ) ;
2008-12-02 19:46:51 +00:00
}
2021-10-07 08:33:55 -05:00
else if ( peer ! = s - > optimistic )
2008-12-02 19:46:51 +00:00
{
2022-08-11 19:59:58 -05:00
choked . emplace_back (
peer ,
2023-11-12 14:38:27 -06:00
get_rate ( s - > tor , peer , now ) ,
2022-08-11 19:59:58 -05:00
salter ( ) ,
2023-04-14 16:03:08 -05:00
peer - > peer_is_interested ( ) ,
peer - > peer_is_choked ( ) ,
2022-08-11 19:59:58 -05:00
true ) ;
2007-11-18 06:15:13 +00:00
}
2007-09-20 16:32:01 +00:00
}
2007-09-22 00:22:10 +00:00
2022-08-11 19:59:58 -05:00
std : : sort ( std : : begin ( choked ) , std : : end ( choked ) ) ;
2017-04-19 15:04:45 +03:00
/**
* Reciprocation and number of uploads capping is managed by unchoking
* the N peers which have the best upload rate and are interested .
* This maximizes the client ' s download rate . These N peers are
* referred to as downloaders , because they are interested in downloading
* from the client .
*
* Peers which have a better upload rate ( as compared to the downloaders )
* but aren ' t interested get unchoked . If they become interested , the
* downloader with the worst upload rate gets choked . If a client has
* a complete file , it uses its upload rate rather than its download
* rate to decide which peers to unchoke .
*
* If our bandwidth is maxed out , don ' t unchoke any more peers .
*/
2022-08-11 19:59:58 -05:00
auto checked_choke_count = size_t { 0U } ;
auto unchoked_interested = size_t { 0U } ;
2007-09-20 16:32:01 +00:00
2022-08-11 19:59:58 -05:00
for ( auto & item : choked )
2013-01-27 21:03:52 +00:00
{
2022-08-15 12:48:05 -05:00
if ( unchoked_interested > = session - > uploadSlotsPerTorrent ( ) )
2022-08-11 19:59:58 -05:00
{
break ;
}
2023-11-12 14:38:27 -06:00
item . is_choked_ = is_maxed_out ? item . was_choked_ : false ;
2017-04-19 15:04:45 +03:00
2022-08-11 19:59:58 -05:00
+ + checked_choke_count ;
2020-11-03 18:59:19 -06:00
2023-11-12 14:38:27 -06:00
if ( item . is_interested_ )
2017-04-19 15:04:45 +03:00
{
2022-08-11 19:59:58 -05:00
+ + unchoked_interested ;
2017-04-19 15:04:45 +03:00
}
2007-11-18 06:15:13 +00:00
}
2007-09-20 16:32:01 +00:00
2017-04-19 15:04:45 +03:00
/* optimistic unchoke */
2022-09-07 11:04:28 -05:00
if ( s - > optimistic = = nullptr & & ! is_maxed_out & & checked_choke_count < std : : size ( choked ) )
2008-01-11 02:09:20 +00:00
{
2022-08-11 19:59:58 -05:00
auto rand_pool = std : : vector < ChokeData * > { } ;
2008-06-03 04:29:56 +00:00
2022-08-11 19:59:58 -05:00
for ( auto i = checked_choke_count , n = std : : size ( choked ) ; i < n ; + + i )
2008-01-11 02:09:20 +00:00
{
2023-11-12 14:38:27 -06:00
if ( choked [ i ] . is_interested_ )
2008-06-03 04:29:56 +00:00
{
2022-12-02 10:39:46 -06:00
rand_pool . push_back ( & choked [ i ] ) ;
2008-06-03 04:29:56 +00:00
}
2008-01-11 02:09:20 +00:00
}
2008-06-03 04:29:56 +00:00
2022-08-11 19:59:58 -05:00
if ( auto const n = std : : size ( rand_pool ) ; n ! = 0 )
2008-06-03 04:29:56 +00:00
{
2022-12-15 02:21:56 +08:00
auto * c = rand_pool [ tr_rand_int ( n ) ] ;
2023-11-12 14:38:27 -06:00
c - > is_choked_ = false ;
s - > optimistic = c - > msgs_ ;
2022-04-27 20:06:51 -05:00
s - > optimistic_unchoke_time_scaler = OptimisticUnchokeMultiplier ;
2008-06-03 04:29:56 +00:00
}
2007-09-20 16:32:01 +00:00
}
2022-08-11 19:59:58 -05:00
for ( auto & item : choked )
2017-04-19 15:04:45 +03:00
{
2023-11-12 14:38:27 -06:00
item . msgs_ - > set_choke ( item . is_choked_ ) ;
2017-04-19 15:04:45 +03:00
}
2007-09-20 16:32:01 +00:00
}
2022-06-28 15:24:39 -05:00
} // namespace rechoke_uploads_helpers
2023-01-27 14:25:08 -06:00
} // namespace
2022-06-28 15:24:39 -05:00
2023-10-30 02:32:29 -04:00
void tr_peerMgr : : rechoke_pulse ( ) const
2007-09-20 16:32:01 +00:00
{
2023-03-10 13:21:51 -06:00
using namespace update_interest_helpers ;
2022-06-28 15:24:39 -05:00
using namespace rechoke_uploads_helpers ;
2022-06-27 21:15:15 -05:00
auto const lock = unique_lock ( ) ;
auto const now = tr_time_msec ( ) ;
2011-01-29 18:59:23 +00:00
2023-10-24 15:24:52 -04:00
for ( auto * const tor : torrents_ )
2013-01-27 21:03:52 +00:00
{
2023-04-22 20:25:55 -05:00
if ( tor - > is_running ( ) )
2013-01-27 21:03:52 +00:00
{
2022-09-01 21:16:43 -05:00
// possibly stop torrents that have seeded enough
2023-11-25 20:00:20 -06:00
tor - > stop_if_seed_limit_reached ( ) ;
2022-06-27 21:15:15 -05:00
}
2013-07-08 16:41:12 +00:00
2023-04-22 20:25:55 -05:00
if ( tor - > is_running ( ) )
2022-06-27 21:15:15 -05:00
{
2022-09-01 21:16:43 -05:00
if ( auto * const swarm = tor - > swarm ; swarm - > stats . peer_count > 0 )
{
rechokeUploads ( swarm , now ) ;
2023-03-10 13:21:51 -06:00
updateInterest ( swarm ) ;
2022-09-01 21:16:43 -05:00
}
2010-03-06 14:56:15 +00:00
}
}
2007-09-20 16:32:01 +00:00
}
2007-09-22 00:22:10 +00:00
2023-01-22 13:21:30 -06:00
// --- Life and Death
2007-09-30 23:55:49 +00:00
2022-06-28 15:24:39 -05:00
namespace
{
2023-01-27 14:25:08 -06:00
namespace disconnect_helpers
{
2022-06-28 15:24:39 -05:00
// when many peers are available, keep idle ones this long
2022-10-26 00:14:42 +08:00
auto constexpr MinUploadIdleSecs = time_t { 60 } ;
2022-06-28 15:24:39 -05:00
// when few peers are available, keep idle ones this long
2022-10-26 00:14:42 +08:00
auto constexpr MaxUploadIdleSecs = time_t { 60 * 5 } ;
2022-06-28 15:24:39 -05:00
2022-10-26 00:14:42 +08:00
[[nodiscard]] bool shouldPeerBeClosed ( tr_swarm const * s , tr_peerMsgs const * peer , size_t peer_count , time_t const now )
2017-04-19 15:04:45 +03:00
{
/* if it's marked for purging, close it */
2022-06-27 14:12:31 -05:00
if ( peer - > do_purge )
2017-04-19 15:04:45 +03:00
{
2022-12-08 16:44:19 -06:00
tr_logAddTraceSwarm ( s , fmt : : format ( " purging peer {} because its do_purge flag is set " , peer - > display_name ( ) ) ) ;
2013-01-27 21:03:52 +00:00
return true ;
2017-04-19 15:04:45 +03:00
}
2022-06-01 21:33:33 -05:00
auto const * tor = s - > tor ;
2023-07-14 07:06:25 -05:00
auto const * const info = peer - > peer_info ;
2022-06-01 21:33:33 -05:00
2017-04-19 15:04:45 +03:00
/* disconnect if we're both seeds and enough time has passed for PEX */
2024-03-16 08:52:09 +08:00
if ( tor - > is_done ( ) & & peer - > is_seed ( ) )
2017-04-19 15:04:45 +03:00
{
2023-07-14 07:06:25 -05:00
return ! tor - > allows_pex ( ) | | info - > idle_secs ( now ) . value_or ( 0U ) > = 30U ;
2017-04-19 15:04:45 +03:00
}
/* disconnect if it's been too long since piece data has been transferred.
* this is on a sliding scale based on number of available peers . . . */
{
2023-04-22 20:25:55 -05:00
auto const relax_strictness_if_fewer_than_n = static_cast < size_t > ( std : : lround ( tor - > peer_limit ( ) * 0.9 ) ) ;
2017-04-19 15:04:45 +03:00
/* if we have >= relaxIfFewerThan, strictness is 100%.
* if we have zero connections , strictness is 0 % */
2022-09-07 11:04:28 -05:00
float const strictness = peer_count > = relax_strictness_if_fewer_than_n ?
1.0 :
peer_count / ( float ) relax_strictness_if_fewer_than_n ;
2022-10-26 00:14:42 +08:00
auto const lo = MinUploadIdleSecs ;
auto const hi = MaxUploadIdleSecs ;
time_t const limit = hi - ( hi - lo ) * strictness ;
2017-04-19 15:04:45 +03:00
2023-07-14 07:06:25 -05:00
if ( auto const idle_secs = info - > idle_secs ( now ) ; idle_secs & & * idle_secs > limit )
2017-04-19 15:04:45 +03:00
{
2022-03-11 15:09:22 -06:00
tr_logAddTraceSwarm (
2022-03-09 23:51:14 -06:00
s ,
2022-12-08 16:44:19 -06:00
fmt : : format (
" purging peer {} because it's been {} secs since we shared anything " ,
peer - > display_name ( ) ,
2023-07-14 07:06:25 -05:00
* idle_secs ) ) ;
2017-04-19 15:04:45 +03:00
return true ;
}
}
2007-09-22 14:18:52 +00:00
2017-04-19 15:04:45 +03:00
return false ;
2007-10-13 13:54:05 +00:00
}
2023-08-01 22:56:26 +08:00
void close_peer ( tr_peerMsgs * peer )
2009-01-20 15:47:25 +00:00
{
2021-09-15 02:18:09 +02:00
TR_ASSERT ( peer ! = nullptr ) ;
2023-08-01 22:56:26 +08:00
peer - > swarm - > remove_peer ( peer ) ;
2009-02-04 16:58:52 +00:00
}
2007-09-30 23:55:49 +00:00
2023-06-30 02:13:25 +08:00
constexpr struct
2009-07-05 18:28:36 +00:00
{
2023-08-01 22:56:26 +08:00
[[nodiscard]] constexpr static int compare ( tr_peerMsgs const * a , tr_peerMsgs const * b ) // <=>
2017-04-19 15:04:45 +03:00
{
2022-06-27 14:12:31 -05:00
if ( a - > do_purge ! = b - > do_purge )
2017-04-19 15:04:45 +03:00
{
2022-06-27 14:12:31 -05:00
return a - > do_purge ? 1 : - 1 ;
2017-04-19 15:04:45 +03:00
}
2009-07-05 18:28:36 +00:00
2023-07-14 07:06:25 -05:00
return - a - > peer_info - > compare_by_piece_data_time ( * b - > peer_info ) ;
2021-10-27 21:05:00 -05:00
}
2009-07-05 18:28:36 +00:00
2023-08-01 22:56:26 +08:00
[[nodiscard]] constexpr bool operator ( ) ( tr_peerMsgs const * a , tr_peerMsgs const * b ) const // less than
2021-10-27 21:05:00 -05:00
{
return compare ( a , b ) < 0 ;
}
2023-07-05 23:10:04 +08:00
} ComparePeerByMostActive { } ;
2023-08-01 22:56:26 +08:00
constexpr auto ComparePeerByLeastActive = [ ] ( tr_peerMsgs const * a , tr_peerMsgs const * b )
2023-07-05 23:10:04 +08:00
{
return ComparePeerByMostActive ( b , a ) ;
} ;
2009-10-27 20:06:55 +00:00
2023-08-01 22:56:26 +08:00
using bad_peers_t = small : : vector < tr_peerMsgs * , 512U > ;
2023-07-14 17:46:10 -05:00
bad_peers_t & get_peers_to_close ( tr_swarm const * const swarm , time_t const now_sec , bad_peers_t & bad_peers_buf )
2022-06-28 15:24:39 -05:00
{
2023-04-14 16:03:08 -05:00
auto const & peers = swarm - > peers ;
auto const peer_count = std : : size ( peers ) ;
2022-06-28 15:24:39 -05:00
2023-07-14 17:46:10 -05:00
bad_peers_buf . clear ( ) ;
bad_peers_buf . reserve ( peer_count ) ;
2022-06-28 15:24:39 -05:00
for ( auto * peer : swarm - > peers )
{
if ( shouldPeerBeClosed ( swarm , peer , peer_count , now_sec ) )
{
2023-07-14 17:46:10 -05:00
bad_peers_buf . emplace_back ( peer ) ;
2022-06-28 15:24:39 -05:00
}
}
2023-07-14 17:46:10 -05:00
return bad_peers_buf ;
2022-06-28 15:24:39 -05:00
}
2023-07-14 17:46:10 -05:00
void close_bad_peers ( tr_swarm * s , time_t const now_sec , bad_peers_t & bad_peers_buf )
2022-06-28 15:24:39 -05:00
{
2023-07-14 17:46:10 -05:00
for ( auto * peer : get_peers_to_close ( s , now_sec , bad_peers_buf ) )
2022-06-28 15:24:39 -05:00
{
2023-08-01 22:56:26 +08:00
tr_logAddTraceSwarm ( peer - > swarm , fmt : : format ( " removing bad peer {} " , peer - > display_name ( ) ) ) ;
2023-07-14 17:46:10 -05:00
close_peer ( peer ) ;
2022-06-28 15:24:39 -05:00
}
}
2022-12-22 17:43:36 -06:00
void enforceSwarmPeerLimit ( tr_swarm * swarm , size_t max )
2009-07-05 18:28:36 +00:00
{
2021-10-27 21:05:00 -05:00
// do we have too many peers?
2023-07-05 23:10:04 +08:00
auto const n = swarm - > peerCount ( ) ;
if ( n < = max )
2009-07-05 18:28:36 +00:00
{
2021-10-27 21:05:00 -05:00
return ;
2009-07-05 18:28:36 +00:00
}
2021-10-27 21:05:00 -05:00
// close all but the `max` most active
2023-07-13 22:10:43 +08:00
auto peers = std : : vector < tr_peerMsgs * > ( n - max ) ;
2023-07-05 23:10:04 +08:00
std : : partial_sort_copy (
std : : begin ( swarm - > peers ) ,
std : : end ( swarm - > peers ) ,
std : : begin ( peers ) ,
std : : end ( peers ) ,
ComparePeerByLeastActive ) ;
2023-07-14 17:46:10 -05:00
std : : for_each ( std : : begin ( peers ) , std : : end ( peers ) , close_peer ) ;
2009-07-05 18:28:36 +00:00
}
2023-10-23 11:34:30 -04:00
void enforceSessionPeerLimit ( size_t global_peer_limit , tr_torrents & torrents )
2009-07-05 18:28:36 +00:00
{
2023-10-23 11:34:30 -04:00
// if we're under the limit, then no action needed
auto const current_size = tr_peerMsgs : : size ( ) ;
if ( current_size < = global_peer_limit )
2017-04-19 15:04:45 +03:00
{
2021-10-27 21:05:00 -05:00
return ;
2017-04-19 15:04:45 +03:00
}
2013-01-27 21:03:52 +00:00
2023-10-23 11:34:30 -04:00
// make a list of all the peers
2023-08-01 22:56:26 +08:00
auto peers = std : : vector < tr_peerMsgs * > { } ;
2023-10-23 11:34:30 -04:00
peers . reserve ( current_size ) ;
for ( auto const * const tor : torrents )
2013-01-27 21:03:52 +00:00
{
2022-04-29 08:32:15 -05:00
peers . insert ( std : : end ( peers ) , std : : begin ( tor - > swarm - > peers ) , std : : end ( tor - > swarm - > peers ) ) ;
2009-07-05 18:28:36 +00:00
}
2021-10-27 21:05:00 -05:00
2023-10-23 11:34:30 -04:00
TR_ASSERT ( current_size = = std : : size ( peers ) ) ;
if ( std : : size ( peers ) > global_peer_limit )
2022-12-22 17:43:36 -06:00
{
2023-10-23 11:34:30 -04:00
std : : partial_sort ( std : : begin ( peers ) , std : : begin ( peers ) + global_peer_limit , std : : end ( peers ) , ComparePeerByMostActive ) ;
std : : for_each ( std : : begin ( peers ) + global_peer_limit , std : : end ( peers ) , close_peer ) ;
2022-12-22 17:43:36 -06:00
}
2009-07-05 18:28:36 +00:00
}
2022-06-28 15:24:39 -05:00
} // namespace disconnect_helpers
2023-01-27 14:25:08 -06:00
} // namespace
2022-06-28 15:24:39 -05:00
2023-10-30 02:32:29 -04:00
void tr_peerMgr : : reconnect_pulse ( )
2009-02-04 16:58:52 +00:00
{
2022-06-28 15:24:39 -05:00
using namespace disconnect_helpers ;
2023-10-24 15:24:52 -04:00
auto const lock = unique_lock ( ) ;
2022-06-28 15:24:39 -05:00
auto const now_sec = tr_time ( ) ;
2009-07-05 18:28:36 +00:00
2021-10-27 21:05:00 -05:00
// remove crappy peers
2023-07-14 17:46:10 -05:00
auto bad_peers_buf = bad_peers_t { } ;
2023-10-31 19:20:01 -04:00
for ( auto * const tor : torrents_ )
2017-04-19 15:04:45 +03:00
{
2022-11-22 14:00:09 -06:00
auto * const swarm = tor - > swarm ;
if ( ! swarm - > is_running )
2017-04-19 15:04:45 +03:00
{
2023-08-01 22:56:26 +08:00
swarm - > remove_all_peers ( ) ;
2017-04-19 15:04:45 +03:00
}
else
{
2023-07-14 17:46:10 -05:00
close_bad_peers ( swarm , now_sec , bad_peers_buf ) ;
2017-04-19 15:04:45 +03:00
}
}
2009-12-12 17:05:31 +00:00
2021-10-27 21:05:00 -05:00
// if we're over the per-torrent peer limits, cull some peers
2023-10-31 19:20:01 -04:00
for ( auto * const tor : torrents_ )
2021-10-27 21:05:00 -05:00
{
2023-04-22 20:25:55 -05:00
if ( tor - > is_running ( ) )
2021-10-27 21:05:00 -05:00
{
2023-04-22 20:25:55 -05:00
enforceSwarmPeerLimit ( tor - > swarm , tor - > peer_limit ( ) ) ;
2021-10-27 21:05:00 -05:00
}
}
// if we're over the per-session peer limits, cull some peers
2023-10-31 19:20:01 -04:00
enforceSessionPeerLimit ( session - > peerLimit ( ) , torrents_ ) ;
2021-10-27 21:05:00 -05:00
// try to make new peer connections
2023-07-05 23:16:18 -05:00
make_new_peer_connections ( ) ;
2007-09-22 00:22:10 +00:00
}
2008-09-17 19:44:24 +00:00
2023-01-22 13:21:30 -06:00
// --- Bandwidth Allocation
2008-09-17 19:44:24 +00:00
2023-01-27 14:25:08 -06:00
namespace
{
2022-06-28 15:24:39 -05:00
namespace bandwidth_helpers
{
void pumpAllPeers ( tr_peerMgr * mgr )
2008-11-24 04:21:23 +00:00
{
2023-10-24 15:24:52 -04:00
for ( auto * const tor : mgr - > torrents_ )
2008-11-24 04:21:23 +00:00
{
2022-04-29 08:32:15 -05:00
for ( auto * const peer : tor - > swarm - > peers )
2017-04-19 15:04:45 +03:00
{
2022-04-29 08:32:15 -05:00
peer - > pulse ( ) ;
2017-04-19 15:04:45 +03:00
}
2008-11-24 04:21:23 +00:00
}
}
2022-06-28 15:24:39 -05:00
} // namespace bandwidth_helpers
2023-01-27 14:25:08 -06:00
} // namespace
2022-06-28 15:24:39 -05:00
2023-10-30 02:32:29 -04:00
void tr_peerMgr : : bandwidth_pulse ( )
2008-09-17 19:44:24 +00:00
{
2022-06-28 15:24:39 -05:00
using namespace bandwidth_helpers ;
2022-06-27 21:15:15 -05:00
auto const lock = unique_lock ( ) ;
2008-09-17 19:44:24 +00:00
2022-06-27 21:15:15 -05:00
pumpAllPeers ( this ) ;
2008-12-20 22:19:34 +00:00
2022-12-20 13:49:37 -06:00
// allocate bandwidth to the peers
2023-07-05 23:16:18 -05:00
static auto constexpr Msec = std : : chrono : : duration_cast < std : : chrono : : milliseconds > ( BandwidthTimerPeriod ) . count ( ) ;
2022-12-20 13:49:37 -06:00
session - > top_bandwidth_ . allocate ( Msec ) ;
2017-04-19 15:04:45 +03:00
2022-12-21 17:21:28 -06:00
// torrent upkeep
2023-10-24 15:24:52 -04:00
for ( auto * const tor : torrents_ )
2023-02-13 12:33:33 -06:00
{
tor - > do_idle_work ( ) ;
}
2009-08-13 17:25:26 +00:00
2023-10-30 02:32:29 -04:00
reconnect_pulse ( ) ;
2008-09-17 19:44:24 +00:00
}
2009-11-26 05:13:58 +00:00
2023-01-07 16:55:00 -06:00
// ---
2022-06-28 15:24:39 -05:00
namespace
{
2023-01-27 14:25:08 -06:00
namespace connect_helpers
{
2010-04-20 21:54:03 +00:00
/* is this atom someone that we'd want to initiate a connection to? */
2023-08-01 22:56:26 +08:00
[[nodiscard]] bool is_peer_candidate ( tr_torrent const * tor , tr_peer_info const & peer_info , time_t const now )
2010-04-20 21:54:03 +00:00
{
2022-06-04 10:29:24 -05:00
// have we already tried and failed to connect?
2023-07-14 07:06:25 -05:00
if ( auto const conn = peer_info . is_connectable ( ) ; conn & & ! * conn )
2017-04-19 15:04:45 +03:00
{
return false ;
}
2010-07-04 20:40:34 +00:00
2022-06-04 10:29:24 -05:00
// not if we're both seeds
2023-07-14 07:06:25 -05:00
if ( tor - > is_done ( ) & & peer_info . is_seed ( ) )
2017-04-19 15:04:45 +03:00
{
return false ;
}
2010-07-04 20:40:34 +00:00
2022-06-04 10:29:24 -05:00
// not if we've already got a connection to them...
2023-10-15 01:58:15 +08:00
if ( peer_info . is_in_use ( ) )
2017-04-19 15:04:45 +03:00
{
return false ;
}
2010-06-14 12:48:28 +00:00
2022-06-04 10:29:24 -05:00
// not if we just tried them already
2023-07-14 07:06:25 -05:00
if ( ! peer_info . reconnect_interval_has_passed ( now ) )
2017-04-19 15:04:45 +03:00
{
return false ;
}
2010-04-20 21:54:03 +00:00
2022-06-04 10:29:24 -05:00
// not if they're blocklisted
2023-10-31 19:20:01 -04:00
if ( peer_info . is_blocklisted ( tor - > session - > blocklist ( ) ) )
2022-06-04 10:29:24 -05:00
{
return false ;
}
// not if they're banned...
2023-07-14 07:06:25 -05:00
if ( peer_info . is_banned ( ) )
2017-04-19 15:04:45 +03:00
{
return false ;
}
2010-06-14 11:57:46 +00:00
2017-04-19 15:04:45 +03:00
return true ;
2010-04-20 21:54:03 +00:00
}
struct peer_candidate
{
2023-07-05 23:16:18 -05:00
peer_candidate ( ) = default ;
2023-08-01 22:56:26 +08:00
peer_candidate ( uint64_t score_in , tr_torrent const * const tor_in , tr_peer_info const * const peer_info_in )
2023-07-05 23:16:18 -05:00
: score { score_in }
, tor { tor_in }
2023-07-14 07:06:25 -05:00
, peer_info { peer_info_in }
2023-07-05 23:16:18 -05:00
{
}
2017-04-19 15:04:45 +03:00
uint64_t score ;
2023-08-01 22:56:26 +08:00
tr_torrent const * tor ;
tr_peer_info const * peer_info ;
2010-04-20 21:54:03 +00:00
} ;
2023-11-23 19:52:53 -06:00
[[nodiscard]] constexpr uint64_t addValToKey ( uint64_t value , unsigned int width , uint64_t addme )
2010-04-20 21:54:03 +00:00
{
2023-11-23 19:52:53 -06:00
value < < = width ;
2017-04-19 15:04:45 +03:00
value | = addme ;
return value ;
2010-06-14 01:56:03 +00:00
}
/* smaller value is better */
2023-07-14 07:06:25 -05:00
[[nodiscard]] uint64_t getPeerCandidateScore ( tr_torrent const * tor , tr_peer_info const & peer_info , uint8_t salt )
2010-06-14 01:56:03 +00:00
{
2021-10-13 10:08:37 -05:00
auto i = uint64_t { } ;
auto score = uint64_t { } ;
2010-06-14 01:56:03 +00:00
2017-04-19 15:04:45 +03:00
/* prefer peers we've connected to, or never tried, over peers we failed to connect to. */
2023-07-14 07:06:25 -05:00
i = peer_info . connection_failure_count ( ) ! = 0U ? 1U : 0U ;
2023-11-23 19:52:53 -06:00
score = addValToKey ( score , 1U , i ) ;
2010-06-14 01:56:03 +00:00
2017-04-19 15:04:45 +03:00
/* prefer the one we attempted least recently (to cycle through all peers) */
2023-07-14 07:06:25 -05:00
i = peer_info . connection_attempt_time ( ) ;
2023-11-23 19:52:53 -06:00
score = addValToKey ( score , 32U , i ) ;
2010-06-14 01:56:03 +00:00
2017-04-19 15:04:45 +03:00
/* prefer peers belonging to a torrent of a higher priority */
2023-04-22 20:25:55 -05:00
switch ( tor - > get_priority ( ) )
2013-01-27 21:03:52 +00:00
{
2017-04-19 15:04:45 +03:00
case TR_PRI_HIGH :
i = 0 ;
break ;
case TR_PRI_NORMAL :
i = 1 ;
break ;
case TR_PRI_LOW :
i = 2 ;
break ;
2023-11-28 09:59:26 +08:00
default :
TR_ASSERT_MSG ( false , " invalid priority " ) ;
break ;
2010-06-14 01:56:03 +00:00
}
2023-11-23 19:52:53 -06:00
score = addValToKey ( score , 4U , i ) ;
2010-06-14 01:56:03 +00:00
2023-11-23 19:52:53 -06:00
// prefer recently-started torrents
i = tor - > started_recently ( tr_time ( ) ) ? 0 : 1 ;
score = addValToKey ( score , 1U , i ) ;
2010-06-26 18:49:31 +00:00
2017-04-19 15:04:45 +03:00
/* prefer torrents we're downloading with */
2023-04-22 20:25:55 -05:00
i = tor - > is_done ( ) ? 1 : 0 ;
2023-11-23 19:52:53 -06:00
score = addValToKey ( score , 1U , i ) ;
2010-12-14 18:33:48 +00:00
2017-04-19 15:04:45 +03:00
/* prefer peers that are known to be connectible */
2023-07-14 07:06:25 -05:00
i = peer_info . is_connectable ( ) . value_or ( false ) ? 0 : 1 ;
2023-11-23 19:52:53 -06:00
score = addValToKey ( score , 1U , i ) ;
2010-06-14 01:56:03 +00:00
2021-09-09 15:25:30 -05:00
/* prefer peers that we might be able to upload to */
2023-07-14 07:06:25 -05:00
i = peer_info . is_seed ( ) ? 0 : 1 ;
2023-11-23 19:52:53 -06:00
score = addValToKey ( score , 1U , i ) ;
2010-06-14 01:56:03 +00:00
2017-04-19 15:04:45 +03:00
/* Prefer peers that we got from more trusted sources.
2023-01-24 00:26:11 +08:00
* lower ` fromBest ` values indicate more trusted sources */
2023-11-23 19:52:53 -06:00
score = addValToKey ( score , 4U , peer_info . from_best ( ) ) ;
2010-06-14 01:56:03 +00:00
2017-04-19 15:04:45 +03:00
/* salt */
2023-11-23 19:52:53 -06:00
score = addValToKey ( score , 8U , salt ) ;
2017-04-19 15:04:45 +03:00
return score ;
2010-06-14 01:56:03 +00:00
}
2023-10-23 11:34:30 -04:00
void get_peer_candidates ( size_t global_peer_limit , tr_torrents & torrents , tr_peerMgr : : OutboundCandidates & setme )
2017-04-19 15:04:45 +03:00
{
2023-10-16 19:39:14 -05:00
setme . clear ( ) ;
2022-05-27 16:27:47 -05:00
auto const now = tr_time ( ) ;
auto const now_msec = tr_time_msec ( ) ;
// leave 5% of connection slots for incoming connections -- ticket #2609
2023-10-23 11:34:30 -04:00
if ( auto const max_candidates = static_cast < size_t > ( global_peer_limit * 0.95 ) ; max_candidates < = tr_peerMsgs : : size ( ) )
2013-01-27 21:03:52 +00:00
{
2023-10-16 19:39:14 -05:00
return ;
2010-04-20 21:54:03 +00:00
}
2021-10-25 00:15:04 -05:00
auto candidates = std : : vector < peer_candidate > { } ;
2023-08-01 22:56:26 +08:00
candidates . reserve ( tr_peer_info : : known_connectable_count ( ) ) ;
2017-04-19 15:04:45 +03:00
/* populate the candidate array */
2022-07-28 10:01:59 -05:00
auto salter = tr_salt_shaker { } ;
2023-10-23 11:34:30 -04:00
for ( auto * const tor : torrents )
2010-04-20 21:54:03 +00:00
{
2022-11-22 14:00:09 -06:00
auto * const swarm = tor - > swarm ;
if ( ! swarm - > is_running )
2017-04-19 15:04:45 +03:00
{
continue ;
}
2010-04-20 21:54:03 +00:00
2023-08-14 22:56:43 +08:00
/* if everyone in the swarm is seeds and pex is disabled,
* then don ' t initiate connections */
2023-04-22 20:25:55 -05:00
bool const seeding = tor - > is_done ( ) ;
2023-08-14 22:56:43 +08:00
if ( seeding & & swarm - > is_all_seeds ( ) & & ! tor - > allows_pex ( ) )
2021-09-09 15:25:30 -05:00
{
continue ;
}
2017-04-19 15:04:45 +03:00
/* if we've already got enough peers in this torrent... */
2023-04-22 20:25:55 -05:00
if ( tor - > peer_limit ( ) < = swarm - > peerCount ( ) )
2017-04-19 15:04:45 +03:00
{
continue ;
}
2010-04-20 21:54:03 +00:00
2017-04-19 15:04:45 +03:00
/* if we've already got enough speed in this torrent... */
2023-11-25 20:00:20 -06:00
if ( seeding & & tor - > bandwidth ( ) . is_maxed_out ( TR_UP , now_msec ) )
2017-04-19 15:04:45 +03:00
{
continue ;
}
2010-04-20 21:54:03 +00:00
2023-08-01 22:56:26 +08:00
for ( auto const & [ socket_address , atom ] : swarm - > connectable_pool )
2010-04-20 21:54:03 +00:00
{
2023-08-01 22:56:26 +08:00
if ( is_peer_candidate ( tor , atom , now ) )
2010-04-20 21:54:03 +00:00
{
2023-07-05 23:16:18 -05:00
candidates . emplace_back ( getPeerCandidateScore ( tor , atom , salter ( ) ) , tor , & atom ) ;
2010-04-20 21:54:03 +00:00
}
}
}
2021-10-25 00:15:04 -05:00
// only keep the best `max` candidates
2023-07-05 23:16:18 -05:00
if ( auto const max = tr_peerMgr : : OutboundCandidates : : requested_inline_size ; max < std : : size ( candidates ) )
2017-04-19 15:04:45 +03:00
{
2021-09-15 09:32:07 -05:00
std : : partial_sort (
2021-10-25 00:15:04 -05:00
std : : begin ( candidates ) ,
std : : begin ( candidates ) + max ,
std : : end ( candidates ) ,
2021-09-15 09:32:07 -05:00
[ ] ( auto const & a , auto const & b ) { return a . score < b . score ; } ) ;
2021-10-25 00:15:04 -05:00
candidates . resize ( max ) ;
2017-04-19 15:04:45 +03:00
}
2011-12-31 21:28:53 +00:00
2023-09-10 23:31:49 -04:00
// put the best candidates at the end of the list
2023-07-09 02:09:34 +08:00
for ( auto it = std : : crbegin ( candidates ) , end = std : : crend ( candidates ) ; it ! = end ; + + it )
2023-07-05 23:16:18 -05:00
{
2023-10-16 19:39:14 -05:00
setme . emplace_back ( it - > tor - > id ( ) , it - > peer_info - > listen_socket_address ( ) ) ;
2023-07-05 23:16:18 -05:00
}
2010-04-20 21:54:03 +00:00
}
2023-08-01 22:56:26 +08:00
void initiate_connection ( tr_peerMgr * mgr , tr_swarm * s , tr_peer_info & peer_info )
2010-04-20 21:54:03 +00:00
{
2023-01-07 16:55:00 -06:00
using namespace handshake_helpers ;
auto const now = tr_time ( ) ;
2023-07-14 07:06:25 -05:00
auto const utp = mgr - > session - > allowsUTP ( ) & & peer_info . supports_utp ( ) . value_or ( true ) ;
2023-01-04 15:37:55 -06:00
auto * const session = mgr - > session ;
if ( tr_peer_socket : : limit_reached ( session ) | | ( ! utp & & ! session - > allowsTCP ( ) ) )
2022-08-25 19:27:11 -07:00
{
return ;
}
2022-12-08 16:44:19 -06:00
tr_logAddTraceSwarm (
s ,
2023-07-14 07:06:25 -05:00
fmt : : format ( " Starting an OUTGOING {} connection with {} " , utp ? " µTP " : " TCP " , peer_info . display_name ( ) ) ) ;
2010-04-20 21:54:03 +00:00
2022-12-16 01:23:12 -06:00
auto peer_io = tr_peerIo : : new_outgoing (
2023-01-04 15:37:55 -06:00
session ,
& session - > top_bandwidth_ ,
2023-08-01 22:56:26 +08:00
peer_info . listen_socket_address ( ) ,
2023-04-22 20:25:55 -05:00
s - > tor - > info_hash ( ) ,
2023-08-01 22:56:26 +08:00
s - > tor - > is_seed ( ) ,
2021-08-15 12:41:48 +03:00
utp ) ;
2010-04-20 21:54:03 +00:00
2022-12-13 11:59:21 -06:00
if ( ! peer_io )
2010-04-20 21:54:03 +00:00
{
2023-07-14 07:06:25 -05:00
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 ( ) ;
2010-04-20 21:54:03 +00:00
}
2017-04-19 15:04:45 +03:00
else
2010-04-20 21:54:03 +00:00
{
2023-10-15 01:58:15 +08:00
peer_info . start_handshake (
2022-12-13 11:59:21 -06:00
& mgr - > handshake_mediator_ ,
peer_io ,
2023-01-04 15:37:55 -06:00
session - > encryptionMode ( ) ,
2022-12-13 11:59:21 -06:00
[ mgr ] ( tr_handshake : : Result const & result ) { return on_handshake_done ( mgr , result ) ; } ) ;
2010-04-20 21:54:03 +00:00
}
2023-07-14 07:06:25 -05:00
peer_info . set_connection_attempt_time ( now ) ;
2010-04-20 21:54:03 +00:00
}
2022-06-28 15:24:39 -05:00
} // namespace connect_helpers
2023-01-27 14:25:08 -06:00
} // namespace
2022-06-28 15:24:39 -05:00
2023-07-05 23:16:18 -05:00
void tr_peerMgr : : make_new_peer_connections ( )
2010-04-20 21:54:03 +00:00
{
2022-06-28 15:24:39 -05:00
using namespace connect_helpers ;
2023-10-24 15:24:52 -04:00
auto const lock = unique_lock ( ) ;
2022-11-22 14:00:09 -06:00
2023-07-05 23:16:18 -05:00
// get the candidates if we need to
2023-07-09 02:09:34 +08:00
auto & candidates = outbound_candidates_ ;
if ( std : : empty ( candidates ) )
2023-07-05 23:16:18 -05:00
{
2023-10-24 15:24:52 -04:00
get_peer_candidates ( session - > peerLimit ( ) , torrents_ , candidates ) ;
2023-07-05 23:16:18 -05:00
}
2023-07-09 02:09:34 +08:00
// initiate connections to the last N candidates
auto const n_this_pass = std : : min ( std : : size ( candidates ) , MaxConnectionsPerPulse ) ;
auto const it_end = std : : crbegin ( candidates ) + n_this_pass ;
for ( auto it = std : : crbegin ( candidates ) ; it ! = it_end ; + + it )
2017-04-19 15:04:45 +03:00
{
2023-07-09 02:09:34 +08:00
auto const & [ tor_id , sock_addr ] = * it ;
2023-07-08 09:32:20 -05:00
2023-10-24 15:24:52 -04:00
if ( auto * const tor = torrents_ . get ( tor_id ) ; tor ! = nullptr )
2023-07-05 23:16:18 -05:00
{
2023-07-14 07:06:25 -05:00
if ( auto * const peer_info = tor - > swarm - > get_existing_peer_info ( sock_addr ) ; peer_info ! = nullptr )
2023-07-08 09:32:20 -05:00
{
2023-08-01 22:56:26 +08:00
initiate_connection ( this , tor - > swarm , * peer_info ) ;
2023-07-08 09:32:20 -05:00
}
2023-07-05 23:16:18 -05:00
}
2017-04-19 15:04:45 +03:00
}
2023-07-05 23:16:18 -05:00
2023-07-09 02:09:34 +08:00
// remove the N candidates that we just consumed
candidates . resize ( std : : size ( candidates ) - n_this_pass ) ;
2010-04-20 21:54:03 +00:00
}
2023-07-12 10:10:20 -05:00
void HandshakeMediator : : set_utp_failed ( tr_sha1_digest_t const & info_hash , tr_socket_address const & socket_address )
{
2023-10-24 15:24:52 -04:00
if ( auto * const tor = torrents_ . get ( info_hash ) ; tor ! = nullptr )
2023-07-12 10:10:20 -05:00
{
2023-07-14 07:06:25 -05:00
if ( auto * const peer_info = tor - > swarm - > get_existing_peer_info ( socket_address ) ; peer_info ! = nullptr )
2023-07-12 10:10:20 -05:00
{
2023-07-14 07:06:25 -05:00
peer_info - > set_utp_supported ( false ) ;
2023-07-12 10:10:20 -05:00
}
}
}