fix: properly reconnect on handshake error (#6950)

* fix: clear read buffer when closing connection

* fix: clear write buffer when closing connection

* fix: disable encryption when reconnecting

* refactor: dedupe code

* fix: maybe reconnect using MSE handshake if it was an utp failure

* chore: misc housekeeping

* chore: removed `tr_peerIo::utp_supported_`

* refactor: more logs in `tr_handshake::on_error()`
This commit is contained in:
Yat Ho 2024-10-22 06:45:58 +08:00 committed by GitHub
parent b5cc6916ef
commit ab66f73c74
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 34 additions and 34 deletions

View File

@ -627,7 +627,7 @@ void tr_handshake::on_error(tr_peerIo* io, tr_error const& error, void* vhandsha
{ {
auto* handshake = static_cast<tr_handshake*>(vhandshake); auto* handshake = static_cast<tr_handshake*>(vhandshake);
auto const retry = [&]() auto const retry_plain = [&]()
{ {
handshake->send_handshake(io); handshake->send_handshake(io);
handshake->set_state(State::AwaitingHandshake); handshake->set_state(State::AwaitingHandshake);
@ -638,6 +638,8 @@ void tr_handshake::on_error(tr_peerIo* io, tr_error const& error, void* vhandsha
handshake->done(false); handshake->done(false);
}; };
handshake->maybe_recycle_dh();
if (io->is_utp() && !io->is_incoming() && handshake->is_state(State::AwaitingYb)) if (io->is_utp() && !io->is_incoming() && handshake->is_state(State::AwaitingYb))
{ {
// the peer probably doesn't speak µTP. // the peer probably doesn't speak µTP.
@ -653,7 +655,15 @@ void tr_handshake::on_error(tr_peerIo* io, tr_error const& error, void* vhandsha
if (handshake->mediator_->allows_tcp() && io->reconnect()) if (handshake->mediator_->allows_tcp() && io->reconnect())
{ {
retry(); tr_logAddTraceHand(handshake, "uTP connection failed, trying TCP...");
if (handshake->encryption_mode_ != TR_CLEAR_PREFERRED)
{
handshake->send_ya(io);
}
else
{
retry_plain();
}
return; return;
} }
@ -667,8 +677,8 @@ void tr_handshake::on_error(tr_peerIo* io, tr_error const& error, void* vhandsha
if (handshake->is_state(State::AwaitingYb) && handshake->encryption_mode_ != TR_ENCRYPTION_REQUIRED && if (handshake->is_state(State::AwaitingYb) && handshake->encryption_mode_ != TR_ENCRYPTION_REQUIRED &&
handshake->mediator_->allows_tcp() && io->reconnect()) handshake->mediator_->allows_tcp() && io->reconnect())
{ {
tr_logAddTraceHand(handshake, "handshake failed, trying plaintext..."); tr_logAddTraceHand(handshake, "MSE handshake failed, trying plaintext...");
retry(); retry_plain();
return; return;
} }

View File

@ -229,6 +229,11 @@ void tr_peerIo::close()
socket_.close(); socket_.close();
event_write_.reset(); event_write_.reset();
event_read_.reset(); event_read_.reset();
inbuf_.clear();
outbuf_.clear();
outbuf_info_.clear();
encrypt_disable();
decrypt_disable();
} }
void tr_peerIo::clear() void tr_peerIo::clear()
@ -241,28 +246,20 @@ void tr_peerIo::clear()
bool tr_peerIo::reconnect() bool tr_peerIo::reconnect()
{ {
TR_ASSERT(!this->is_incoming()); TR_ASSERT(!is_incoming());
TR_ASSERT(this->session_->allowsTCP()); TR_ASSERT(session_->allowsTCP());
short int const pending_events = this->pending_events_; auto const pending_events = pending_events_;
event_disable(EV_READ | EV_WRITE); event_disable(EV_READ | EV_WRITE);
close(); close();
if (tr_peer_socket::limit_reached(session_))
{
return false;
}
auto sock = tr_netOpenPeerSocket(session_, socket_address(), client_is_seed()); auto sock = tr_netOpenPeerSocket(session_, socket_address(), client_is_seed());
if (!sock.is_tcp()) if (!sock.is_tcp())
{ {
return false; return false;
} }
socket_ = std::move(sock); set_socket(std::move(sock));
this->event_read_.reset(event_new(session_->event_base(), socket_.handle.tcp, EV_READ, event_read_cb, this));
this->event_write_.reset(event_new(session_->event_base(), socket_.handle.tcp, EV_WRITE, event_write_cb, this));
event_enable(pending_events); event_enable(pending_events);
@ -609,23 +606,23 @@ void tr_peerIo::read_bytes(void* bytes, size_t n_bytes)
{ {
auto walk = reinterpret_cast<std::byte*>(bytes); auto walk = reinterpret_cast<std::byte*>(bytes);
n_bytes = std::min(n_bytes, std::size(inbuf_)); n_bytes = std::min(n_bytes, std::size(inbuf_));
if (decrypt_remain_len_) if (n_decrypt_remain_)
{ {
if (*decrypt_remain_len_ <= n_bytes) if (auto& n_remain = *n_decrypt_remain_; n_remain <= n_bytes)
{ {
filter_.decrypt(std::data(inbuf_), *decrypt_remain_len_, walk); filter_.decrypt(std::data(inbuf_), n_remain, walk);
inbuf_.drain(*decrypt_remain_len_); inbuf_.drain(n_remain);
if (walk != nullptr) if (walk != nullptr)
{ {
walk += *decrypt_remain_len_; walk += n_remain;
} }
n_bytes -= *decrypt_remain_len_; n_bytes -= n_remain;
filter_.decrypt_disable(); filter_.decrypt_disable();
decrypt_remain_len_.reset(); n_decrypt_remain_.reset();
} }
else else
{ {
*decrypt_remain_len_ -= n_bytes; n_remain -= n_bytes;
} }
} }
filter_.decrypt(std::data(inbuf_), n_bytes, walk); filter_.decrypt(std::data(inbuf_), n_bytes, walk);
@ -655,7 +652,6 @@ void tr_peerIo::on_utp_state_change(int state)
if (state == UTP_STATE_CONNECT) if (state == UTP_STATE_CONNECT)
{ {
tr_logAddTraceIo(this, "utp_on_state_change -- changed to connected"); tr_logAddTraceIo(this, "utp_on_state_change -- changed to connected");
utp_supported_ = true;
} }
else if (state == UTP_STATE_WRITABLE) else if (state == UTP_STATE_WRITABLE)
{ {

View File

@ -256,11 +256,6 @@ public:
/// ///
[[nodiscard]] constexpr auto supports_utp() const noexcept
{
return utp_supported_;
}
[[nodiscard]] constexpr auto is_incoming() const noexcept [[nodiscard]] constexpr auto is_incoming() const noexcept
{ {
return is_incoming_; return is_incoming_;
@ -290,14 +285,14 @@ public:
void decrypt_init(bool is_incoming, DH const& dh, tr_sha1_digest_t const& info_hash) void decrypt_init(bool is_incoming, DH const& dh, tr_sha1_digest_t const& info_hash)
{ {
decrypt_remain_len_.reset(); n_decrypt_remain_.reset();
filter_.decrypt_init(is_incoming, dh, info_hash); filter_.decrypt_init(is_incoming, dh, info_hash);
} }
TR_CONSTEXPR20 void decrypt_disable(size_t decrypt_len = 0U) noexcept TR_CONSTEXPR20 void decrypt_disable(size_t decrypt_len = 0U) noexcept
{ {
// optionally decrypt decrypt_len more bytes before disabling decryption // optionally decrypt decrypt_len more bytes before disabling decryption
decrypt_remain_len_ = decrypt_len; n_decrypt_remain_ = decrypt_len;
} }
void encrypt_init(bool is_incoming, DH const& dh, tr_sha1_digest_t const& info_hash) void encrypt_init(bool is_incoming, DH const& dh, tr_sha1_digest_t const& info_hash)
@ -371,7 +366,7 @@ private:
bool is_seed); bool is_seed);
Filter filter_; Filter filter_;
std::optional<size_t> decrypt_remain_len_; std::optional<size_t> n_decrypt_remain_;
std::deque<std::pair<size_t /*n_bytes*/, bool /*is_piece_data*/>> outbuf_info_; std::deque<std::pair<size_t /*n_bytes*/, bool /*is_piece_data*/>> outbuf_info_;
@ -401,7 +396,6 @@ private:
bool const client_is_seed_; bool const client_is_seed_;
bool const is_incoming_; bool const is_incoming_;
bool utp_supported_ = false;
bool dht_supported_ = false; bool dht_supported_ = false;
bool extended_protocol_supported_ = false; bool extended_protocol_supported_ = false;
bool fast_extension_supported_ = false; bool fast_extension_supported_ = false;