diff --git a/nano/lib/stats_enums.hpp b/nano/lib/stats_enums.hpp index 62a68f3f..f838326b 100644 --- a/nano/lib/stats_enums.hpp +++ b/nano/lib/stats_enums.hpp @@ -383,6 +383,7 @@ enum class detail connect_rejected, connect_success, attempt_timeout, + handshake_timeout, not_a_peer, // tcp_channel diff --git a/nano/node/transport/tcp_listener.cpp b/nano/node/transport/tcp_listener.cpp index 3222c735..c922b6ea 100644 --- a/nano/node/transport/tcp_listener.cpp +++ b/nano/node/transport/tcp_listener.cpp @@ -207,18 +207,35 @@ void nano::transport::tcp_listener::timeout () { debug_assert (!mutex.try_lock ()); - auto const cutoff = std::chrono::steady_clock::now () - config.connect_timeout; + auto const now = std::chrono::steady_clock::now (); + auto const connect_cutoff = now - config.connect_timeout; + auto const handshake_cutoff = now - config.handshake_timeout; // Cancel timed out attempts for (auto & attempt : attempts) { - if (!attempt.task.ready () && attempt.start < cutoff) + if (!attempt.task.ready () && attempt.start < connect_cutoff) { - attempt.task.cancel (); // Cancel is non-blocking and will return immediately, safe to call under lock - stats.inc (nano::stat::type::tcp_listener, nano::stat::detail::attempt_timeout); logger.debug (nano::log::type::tcp_listener, "Connection attempt timed out: {} (started {}s ago)", - attempt.endpoint, nano::log::seconds_delta (attempt.start)); + attempt.endpoint, + nano::log::seconds_delta (attempt.start)); + + attempt.task.cancel (); // Cancel is non-blocking and will return immediately, safe to call under lock + } + } + + // Cancel too long handshakes + for (auto & connection : connections) + { + if (connection.socket->type () == nano::transport::socket_type::undefined && connection.socket->get_time_connected () < handshake_cutoff) + { + stats.inc (nano::stat::type::tcp_listener, nano::stat::detail::handshake_timeout); + logger.debug (nano::log::type::tcp_listener, "Handshake timed out: {} (connected {}s ago)", + connection.endpoint, + nano::log::seconds_delta (connection.socket->get_time_connected ())); + + connection.socket->close (); // Schedule socket close, this is non-blocking, safe to call under lock } } } diff --git a/nano/node/transport/tcp_socket.cpp b/nano/node/transport/tcp_socket.cpp index d5686c68..4fe42e24 100644 --- a/nano/node/transport/tcp_socket.cpp +++ b/nano/node/transport/tcp_socket.cpp @@ -36,6 +36,7 @@ nano::transport::tcp_socket::tcp_socket (nano::node & node_a, boost::asio::ip::t default_timeout{ node_a.config.tcp_io_timeout }, silent_connection_tolerance_time{ node_a.network_params.network.silent_connection_tolerance_time } { + time_connected = std::chrono::steady_clock::now (); } nano::transport::tcp_socket::~tcp_socket () @@ -77,6 +78,7 @@ void nano::transport::tcp_socket::async_connect (nano::tcp_endpoint const & endp } else { + this_l->time_connected = std::chrono::steady_clock::now (); this_l->set_last_completion (); { // Best effort attempt to get endpoint address diff --git a/nano/node/transport/tcp_socket.hpp b/nano/node/transport/tcp_socket.hpp index 6e1ba40d..ebb7fe78 100644 --- a/nano/node/transport/tcp_socket.hpp +++ b/nano/node/transport/tcp_socket.hpp @@ -140,6 +140,10 @@ public: { return !is_closed (); } + std::chrono::steady_clock::time_point get_time_connected () const + { + return time_connected.load (); + } private: size_t const queue_size; @@ -188,6 +192,8 @@ protected: /** Updated only from strand, but stored as atomic so it can be read from outside */ std::atomic write_in_progress{ false }; + std::atomic time_connected; + void close_internal (); void write_queued_messages (); void set_default_timeout ();