diff --git a/libtransmission/handshake.cc b/libtransmission/handshake.cc index 1d89b04a5..05f054e72 100644 --- a/libtransmission/handshake.cc +++ b/libtransmission/handshake.cc @@ -833,6 +833,8 @@ void tr_handshake::on_error(tr_peerIo* io, tr_error const& error, void* vhandsha bool tr_handshake::fire_done(bool is_connected) { + maybe_recycle_dh(); + if (!on_done_) { return false; @@ -910,7 +912,7 @@ uint32_t tr_handshake::crypto_provide() const noexcept **/ tr_handshake::tr_handshake(Mediator* mediator, std::shared_ptr peer_io, tr_encryption_mode mode, DoneFunc on_done) - : dh_{ mediator->private_key() } + : dh_{ tr_handshake::get_dh(mediator) } , on_done_{ std::move(on_done) } , peer_io_{ std::move(peer_io) } , timeout_timer_{ mediator->timer_maker().create([this]() { fire_done(false); }) } diff --git a/libtransmission/handshake.h b/libtransmission/handshake.h index a9a6573c4..0733c9e25 100644 --- a/libtransmission/handshake.h +++ b/libtransmission/handshake.h @@ -16,6 +16,7 @@ #include // for std::byte, size_t #include #include +#include #include #include @@ -248,6 +249,51 @@ private: using vc_t = std::array; static auto constexpr VC = vc_t{}; + /// DH pool. Keys are expensive, so we recycle them iff the peer was unreachable + + static constexpr auto DhPoolMaxSize = size_t{ 32 }; + static inline auto dh_pool_size_ = size_t{}; + static inline auto dh_pool_ = std::array{}; + static inline auto dh_pool_mutex_ = std::mutex{}; + + [[nodiscard]] static DH get_dh(Mediator* mediator) + { + auto lock = std::unique_lock(dh_pool_mutex_); + + if (dh_pool_size_ > 0U) + { + auto dh = DH{}; + std::swap(dh, dh_pool_[dh_pool_size_]); + --dh_pool_size_; + return dh; + } + + return DH{ mediator->private_key() }; + } + + static void add_dh(DH&& dh) + { + auto lock = std::unique_lock(dh_pool_mutex_); + + if (dh_pool_size_ < std::size(dh_pool_)) + { + dh_pool_[dh_pool_size_] = std::move(dh); + ++dh_pool_size_; + } + } + + void maybe_recycle_dh() + { + if (have_read_anything_from_peer_) + { + return; + } + + auto dh = DH{}; + std::swap(dh_, dh); + add_dh(std::move(dh)); + } + /// DH dh_ = {}; diff --git a/libtransmission/peer-mse.h b/libtransmission/peer-mse.h index 0cf912c65..9c0cde867 100644 --- a/libtransmission/peer-mse.h +++ b/libtransmission/peer-mse.h @@ -70,7 +70,7 @@ public: [[nodiscard]] static private_key_bigend_t randomPrivateKey() noexcept; private: - private_key_bigend_t const private_key_; + private_key_bigend_t private_key_; key_bigend_t public_key_ = {}; key_bigend_t secret_ = {}; };