diff --git a/libtransmission/handshake.cc b/libtransmission/handshake.cc index f3a616ba1..19524dc84 100644 --- a/libtransmission/handshake.cc +++ b/libtransmission/handshake.cc @@ -1095,7 +1095,7 @@ static void gotError(tr_peerIo* io, short what, void* vhandshake) handshake->mediator->setUTPFailed(*hash, io->address()); } - if (tr_peerIoReconnect(handshake->io) == 0) + if (handshake->mediator->allowsTCP() && tr_peerIoReconnect(handshake->io) == 0) { auto msg = std::array{}; buildHandshakeMessage(handshake, std::data(msg)); @@ -1109,7 +1109,8 @@ static void gotError(tr_peerIo* io, short what, void* vhandshake) * have encountered a peer that doesn't do encryption... reconnect and * try a plaintext handshake */ if ((handshake->state == AWAITING_YB || handshake->state == AWAITING_VC) && - handshake->encryption_mode != TR_ENCRYPTION_REQUIRED && tr_peerIoReconnect(handshake->io) == 0) + handshake->encryption_mode != TR_ENCRYPTION_REQUIRED && handshake->mediator->allowsTCP() && + tr_peerIoReconnect(handshake->io) == 0) { auto msg = std::array{}; tr_logAddTraceHand(handshake, "handshake failed, trying plaintext..."); diff --git a/libtransmission/handshake.h b/libtransmission/handshake.h index 74c31b600..cef8a4c0f 100644 --- a/libtransmission/handshake.h +++ b/libtransmission/handshake.h @@ -57,6 +57,8 @@ public: [[nodiscard]] virtual bool isDHTEnabled() const = 0; + [[nodiscard]] virtual bool allowsTCP() const = 0; + [[nodiscard]] virtual bool isPeerKnownSeed(tr_torrent_id_t tor_id, tr_address addr) const = 0; [[nodiscard]] virtual size_t pad(void* setme, size_t max_bytes) const = 0; diff --git a/libtransmission/net.cc b/libtransmission/net.cc index 422d1c762..3086b7892 100644 --- a/libtransmission/net.cc +++ b/libtransmission/net.cc @@ -284,6 +284,11 @@ struct tr_peer_socket tr_netOpenPeerSocket(tr_session* session, tr_address const { TR_ASSERT(tr_address_is_valid(addr)); + if (!session->allowsTCP()) + { + return {}; + } + if (!tr_address_is_valid_for_peers(addr, port)) { return {}; diff --git a/libtransmission/peer-io.cc b/libtransmission/peer-io.cc index 38677300c..9da5bf10d 100644 --- a/libtransmission/peer-io.cc +++ b/libtransmission/peer-io.cc @@ -511,6 +511,7 @@ tr_peerIo* tr_peerIoNew( #else TR_ASSERT(socket.type == TR_PEER_SOCKET_TYPE_TCP); #endif + TR_ASSERT(session->allowsTCP() || socket.type != TR_PEER_SOCKET_TYPE_TCP); if (socket.type == TR_PEER_SOCKET_TYPE_TCP) { @@ -588,6 +589,7 @@ tr_peerIo* tr_peerIoNewOutgoing( { TR_ASSERT(session != nullptr); TR_ASSERT(tr_address_is_valid(addr)); + TR_ASSERT(utp || session->allowsTCP()); auto socket = tr_peer_socket{}; @@ -831,6 +833,7 @@ int tr_peerIoReconnect(tr_peerIo* io) { TR_ASSERT(tr_isPeerIo(io)); TR_ASSERT(!io->isIncoming()); + TR_ASSERT(io->session->allowsTCP()); tr_session* session = tr_peerIoGetSession(io); diff --git a/libtransmission/peer-mgr.cc b/libtransmission/peer-mgr.cc index 3a05042d1..1a2cce67a 100644 --- a/libtransmission/peer-mgr.cc +++ b/libtransmission/peer-mgr.cc @@ -113,6 +113,11 @@ public: return tr_dhtEnabled(&session_); } + [[nodiscard]] bool allowsTCP() const override + { + return session_.allowsTCP(); + } + void setUTPFailed(tr_sha1_digest_t const& info_hash, tr_address addr) override { if (auto* const tor = session_.torrents().get(info_hash); tor != nullptr) @@ -2824,6 +2829,11 @@ void initiateConnection(tr_peerMgr* mgr, tr_swarm* s, peer_atom& atom) utp = utp && (atom.flags & ADDED_F_UTP_FLAGS) != 0; } + if (!utp && !mgr->session->allowsTCP()) + { + return; + } + tr_logAddTraceSwarm(s, fmt::format("Starting an OUTGOING {} connection with {}", utp ? " µTP" : "TCP", atom.readable())); tr_peerIo* const io = tr_peerIoNewOutgoing( diff --git a/libtransmission/quark.cc b/libtransmission/quark.cc index 582ab59e5..be11524af 100644 --- a/libtransmission/quark.cc +++ b/libtransmission/quark.cc @@ -18,7 +18,7 @@ using namespace std::literals; namespace { -auto constexpr my_static = std::array{ ""sv, +auto constexpr my_static = std::array{ ""sv, "activeTorrentCount"sv, "activity-date"sv, "activityDate"sv, @@ -361,6 +361,7 @@ auto constexpr my_static = std::array{ ""sv, "status"sv, "statusbar-stats"sv, "tag"sv, + "tcp-enabled"sv, "tier"sv, "time-checked"sv, "torrent-added"sv, diff --git a/libtransmission/quark.h b/libtransmission/quark.h index 4ff5f3e59..4b19583f5 100644 --- a/libtransmission/quark.h +++ b/libtransmission/quark.h @@ -364,6 +364,7 @@ enum TR_KEY_status, TR_KEY_statusbar_stats, TR_KEY_tag, + TR_KEY_tcp_enabled, TR_KEY_tier, TR_KEY_time_checked, TR_KEY_torrent_added, diff --git a/libtransmission/rpcimpl.cc b/libtransmission/rpcimpl.cc index a26b1b899..e725a110a 100644 --- a/libtransmission/rpcimpl.cc +++ b/libtransmission/rpcimpl.cc @@ -2184,6 +2184,10 @@ static void addSessionField(tr_session const* s, tr_variant* d, tr_quark key) tr_variantDictAddBool(d, key, s->allowsPEX()); break; + case TR_KEY_tcp_enabled: + tr_variantDictAddBool(d, key, s->allowsTCP()); + break; + case TR_KEY_utp_enabled: tr_variantDictAddBool(d, key, s->allowsUTP()); break; diff --git a/libtransmission/session.cc b/libtransmission/session.cc index 2090704c1..dc7db6228 100644 --- a/libtransmission/session.cc +++ b/libtransmission/session.cc @@ -303,6 +303,8 @@ static void accept_incoming_peer(evutil_socket_t fd, short /*what*/, void* vsess void tr_bindinfo::bindAndListenForIncomingPeers(tr_session* session) { + TR_ASSERT(session->allowsTCP()); + socket_ = tr_netBindTCP(&addr_, session->private_peer_port, false); if (socket_ != TR_BAD_SOCKET) @@ -323,6 +325,8 @@ static void close_incoming_peer_port(tr_session* session) static void open_incoming_peer_port(tr_session* session) { + TR_ASSERT(session->allowsTCP()); + session->bind_ipv4.bindAndListenForIncomingPeers(session); if (tr_net_hasIPv6(session->private_peer_port)) @@ -453,6 +457,7 @@ void tr_sessionGetSettings(tr_session const* s, tr_variant* setme_dictionary) tr_variantDictAddBool(d, TR_KEY_dht_enabled, s->allowsDHT()); tr_variantDictAddBool(d, TR_KEY_utp_enabled, s->allowsUTP()); tr_variantDictAddBool(d, TR_KEY_lpd_enabled, s->allowsLPD()); + tr_variantDictAddBool(d, TR_KEY_tcp_enabled, s->allowsTCP()); tr_variantDictAddStr(d, TR_KEY_download_dir, tr_sessionGetDownloadDir(s)); tr_variantDictAddStr(d, TR_KEY_default_trackers, s->defaultTrackersStr()); tr_variantDictAddInt(d, TR_KEY_download_queue_size, s->queueSize(TR_DOWN)); @@ -825,6 +830,11 @@ void tr_session::setImpl(init_data& data) tr_sessionSetDHTEnabled(this, boolVal); } + if (tr_variantDictFindBool(settings, TR_KEY_tcp_enabled, &boolVal)) + { + is_tcp_enabled_ = boolVal; + } + if (tr_variantDictFindBool(settings, TR_KEY_utp_enabled, &boolVal)) { tr_sessionSetUTPEnabled(this, boolVal); @@ -1252,7 +1262,12 @@ static void peerPortChanged(tr_session* const session) TR_ASSERT(session != nullptr); close_incoming_peer_port(session); - open_incoming_peer_port(session); + + if (session->allowsTCP()) + { + open_incoming_peer_port(session); + } + tr_sharedPortChanged(*session); for (auto* const tor : session->torrents()) diff --git a/libtransmission/session.h b/libtransmission/session.h index 6ca615f0a..6888ba696 100644 --- a/libtransmission/session.h +++ b/libtransmission/session.h @@ -603,6 +603,11 @@ public: return is_pex_enabled_; } + [[nodiscard]] auto constexpr allowsTCP() const noexcept + { + return is_tcp_enabled_; + } + [[nodiscard]] bool allowsUTP() const noexcept; [[nodiscard]] auto constexpr allowsPrefetch() const noexcept @@ -805,6 +810,7 @@ private: bool is_pex_enabled_ = false; bool is_dht_enabled_ = false; bool is_lpd_enabled_ = false; + bool is_tcp_enabled_ = true; bool is_idle_limited_ = false; bool is_prefetch_enabled_ = false; diff --git a/tests/libtransmission/handshake-test.cc b/tests/libtransmission/handshake-test.cc index 558cab523..ace44f2e0 100644 --- a/tests/libtransmission/handshake-test.cc +++ b/tests/libtransmission/handshake-test.cc @@ -78,6 +78,11 @@ public: return false; } + [[nodiscard]] bool allowsTCP() const override + { + return true; + } + [[nodiscard]] bool isPeerKnownSeed(tr_torrent_id_t /*tor_id*/, tr_address /*addr*/) const override { return false; diff --git a/tests/libtransmission/rpc-test.cc b/tests/libtransmission/rpc-test.cc index d1dd01481..91624fc0a 100644 --- a/tests/libtransmission/rpc-test.cc +++ b/tests/libtransmission/rpc-test.cc @@ -93,7 +93,7 @@ TEST_F(RpcTest, sessionGet) EXPECT_TRUE(tr_variantDictFindDict(&response, TR_KEY_arguments, &args)); // what we expected - auto const expected_keys = std::array{ + auto const expected_keys = std::array{ TR_KEY_alt_speed_down, TR_KEY_alt_speed_enabled, TR_KEY_alt_speed_time_begin, @@ -148,6 +148,7 @@ TEST_F(RpcTest, sessionGet) TR_KEY_speed_limit_up, TR_KEY_speed_limit_up_enabled, TR_KEY_start_added_torrents, + TR_KEY_tcp_enabled, TR_KEY_trash_original_torrent_files, TR_KEY_units, TR_KEY_utp_enabled,