Merge branch 'handshake-v2-merged' into develop-rewrite

This commit is contained in:
Colin LeMahieu 2023-03-08 15:45:17 +00:00
commit 76fe3bf5ef
No known key found for this signature in database
GPG key ID: 43708520C8DFB938
12 changed files with 456 additions and 55 deletions

View file

@ -464,4 +464,173 @@ TEST (message, asc_pull_ack_serialization_account_info)
ASSERT_EQ (original_payload.account_conf_height, message_payload.account_conf_height);
ASSERT_TRUE (nano::at_end (stream));
}
TEST (message, node_id_handshake_query_serialization)
{
nano::node_id_handshake::query_payload query{};
query.cookie = 7;
nano::node_id_handshake original{ nano::dev::network_params.network, query };
// Serialize
std::vector<uint8_t> bytes;
{
nano::vectorstream stream{ bytes };
original.serialize (stream);
}
nano::bufferstream stream{ bytes.data (), bytes.size () };
// Header
bool error = false;
nano::message_header header (error, stream);
ASSERT_FALSE (error);
ASSERT_EQ (nano::message_type::node_id_handshake, header.type);
// Message
nano::node_id_handshake message{ error, stream, header };
ASSERT_FALSE (error);
ASSERT_TRUE (message.query);
ASSERT_FALSE (message.response);
ASSERT_EQ (original.query->cookie, message.query->cookie);
ASSERT_TRUE (nano::at_end (stream));
}
TEST (message, node_id_handshake_response_serialization)
{
nano::node_id_handshake::response_payload response{};
response.node_id = nano::account{ 7 };
response.signature = nano::signature{ 11 };
nano::node_id_handshake original{ nano::dev::network_params.network, std::nullopt, response };
// Serialize
std::vector<uint8_t> bytes;
{
nano::vectorstream stream{ bytes };
original.serialize (stream);
}
nano::bufferstream stream{ bytes.data (), bytes.size () };
// Header
bool error = false;
nano::message_header header (error, stream);
ASSERT_FALSE (error);
ASSERT_EQ (nano::message_type::node_id_handshake, header.type);
// Message
nano::node_id_handshake message{ error, stream, header };
ASSERT_FALSE (error);
ASSERT_FALSE (message.query);
ASSERT_TRUE (message.response);
ASSERT_FALSE (message.response->v2);
ASSERT_EQ (original.response->node_id, message.response->node_id);
ASSERT_EQ (original.response->signature, message.response->signature);
ASSERT_TRUE (nano::at_end (stream));
}
TEST (message, node_id_handshake_response_v2_serialization)
{
nano::node_id_handshake::response_payload response{};
response.node_id = nano::account{ 7 };
response.signature = nano::signature{ 11 };
nano::node_id_handshake::response_payload::v2_payload v2_pld{};
v2_pld.salt = 17;
v2_pld.genesis = nano::block_hash{ 13 };
response.v2 = v2_pld;
nano::node_id_handshake original{ nano::dev::network_params.network, std::nullopt, response };
// Serialize
std::vector<uint8_t> bytes;
{
nano::vectorstream stream{ bytes };
original.serialize (stream);
}
nano::bufferstream stream{ bytes.data (), bytes.size () };
// Header
bool error = false;
nano::message_header header (error, stream);
ASSERT_FALSE (error);
ASSERT_EQ (nano::message_type::node_id_handshake, header.type);
// Message
nano::node_id_handshake message{ error, stream, header };
ASSERT_FALSE (error);
ASSERT_FALSE (message.query);
ASSERT_TRUE (message.response);
ASSERT_TRUE (message.response->v2);
ASSERT_EQ (original.response->node_id, message.response->node_id);
ASSERT_EQ (original.response->signature, message.response->signature);
ASSERT_EQ (original.response->v2->salt, message.response->v2->salt);
ASSERT_EQ (original.response->v2->genesis, message.response->v2->genesis);
ASSERT_TRUE (nano::at_end (stream));
}
TEST (handshake, signature)
{
nano::keypair node_id{};
nano::keypair node_id_2{};
auto cookie = nano::random_pool::generate<nano::uint256_union> ();
auto cookie_2 = nano::random_pool::generate<nano::uint256_union> ();
nano::node_id_handshake::response_payload response{};
response.node_id = node_id.pub;
response.sign (cookie, node_id);
ASSERT_TRUE (response.validate (cookie));
// Invalid cookie
ASSERT_FALSE (response.validate (cookie_2));
// Invalid node id
response.node_id = node_id_2.pub;
ASSERT_FALSE (response.validate (cookie));
}
TEST (handshake, signature_v2)
{
nano::keypair node_id{};
nano::keypair node_id_2{};
auto cookie = nano::random_pool::generate<nano::uint256_union> ();
auto cookie_2 = nano::random_pool::generate<nano::uint256_union> ();
nano::node_id_handshake::response_payload original{};
original.node_id = node_id.pub;
original.v2 = nano::node_id_handshake::response_payload::v2_payload{};
original.v2->genesis = nano::test::random_hash ();
original.v2->salt = nano::random_pool::generate<nano::uint256_union> ();
original.sign (cookie, node_id);
ASSERT_TRUE (original.validate (cookie));
// Invalid cookie
ASSERT_FALSE (original.validate (cookie_2));
// Invalid node id
{
auto message = original;
ASSERT_TRUE (message.validate (cookie));
message.node_id = node_id_2.pub;
ASSERT_FALSE (message.validate (cookie));
}
// Invalid genesis
{
auto message = original;
ASSERT_TRUE (message.validate (cookie));
message.v2->genesis = nano::test::random_hash ();
ASSERT_FALSE (message.validate (cookie));
}
// Invalid salt
{
auto message = original;
ASSERT_TRUE (message.validate (cookie));
message.v2->salt = nano::random_pool::generate<nano::uint256_union> ();
ASSERT_FALSE (message.validate (cookie));
}
}

View file

@ -509,7 +509,8 @@ TEST (telemetry, ongoing_broadcasts)
ASSERT_TIMELY (5s, node2.stats.count (nano::stat::type::telemetry, nano::stat::detail::process) >= 3)
}
TEST (telemetry, mismatched_genesis)
// TODO: With handshake V2, nodes with mismatched genesis will refuse to connect while setting up the system
TEST (telemetry, DISABLED_mismatched_genesis)
{
// Only second node will broadcast telemetry
nano::test::system system;

View file

@ -19,6 +19,22 @@ public:
static uint64_t generate_word64 (uint64_t min, uint64_t max);
static unsigned char generate_byte ();
/** Fills variable with random data */
template <class T>
static void generate (T & out)
{
generate_block (reinterpret_cast<uint8_t *> (&out), sizeof (T));
}
/** Returns variable with random data */
template <class T>
static T generate ()
{
T t;
generate (t);
return t;
}
public:
random_pool () = delete;
random_pool (random_pool const &) = delete;
random_pool & operator= (random_pool const &) = delete;

View file

@ -43,6 +43,7 @@ enum class type : uint8_t
unchecked,
election_scheduler,
optimistic_scheduler,
handshake,
_last // Must be the last enum
};
@ -53,6 +54,7 @@ enum class detail : uint8_t
all = 0,
// common
ok,
loop,
total,
process,
@ -273,6 +275,11 @@ enum class detail : uint8_t
insert_priority_success,
erase_oldest,
// handshake
invalid_node_id,
missing_cookie,
invalid_genesis,
_last // Must be the last enum
};

View file

@ -203,11 +203,11 @@ void nano::message_header::count_set (uint8_t count_a)
extensions |= std::bitset<16> (static_cast<unsigned long long> (count_a) << 12);
}
void nano::message_header::flag_set (uint8_t flag_a)
void nano::message_header::flag_set (uint8_t flag_a, bool enable)
{
// Flags from 8 are block_type & count
debug_assert (flag_a < 8);
extensions.set (flag_a, true);
extensions.set (flag_a, enable);
}
bool nano::message_header::bulk_pull_is_count_present () const
@ -1327,10 +1327,12 @@ nano::node_id_handshake::node_id_handshake (nano::network_constants const & cons
if (query)
{
header.flag_set (query_flag);
header.flag_set (v2_flag); // Always indicate support for V2 handshake when querying, old peers will just ignore it
}
if (response)
{
header.flag_set (response_flag);
header.flag_set (v2_flag, response->v2.has_value ()); // We only use V2 handshake when replying to peers that indicated support for it
}
}
@ -1363,7 +1365,7 @@ bool nano::node_id_handshake::deserialize (nano::stream & stream)
if (is_response (header))
{
response_payload pld{};
pld.deserialize (stream);
pld.deserialize (stream, header);
response = pld;
}
}
@ -1388,6 +1390,18 @@ bool nano::node_id_handshake::is_response (nano::message_header const & header)
return result;
}
bool nano::node_id_handshake::is_v2 (nano::message_header const & header)
{
debug_assert (header.type == nano::message_type::node_id_handshake);
bool result = header.extensions.test (v2_flag);
return result;
}
bool nano::node_id_handshake::is_v2 () const
{
return is_v2 (header);
}
void nano::node_id_handshake::visit (nano::message_visitor & visitor_a) const
{
visitor_a.node_id_handshake (*this);
@ -1407,7 +1421,7 @@ std::size_t nano::node_id_handshake::size (nano::message_header const & header)
}
if (is_response (header))
{
result += response_payload::size;
result += response_payload::size (header);
}
return result;
}
@ -1447,14 +1461,81 @@ void nano::node_id_handshake::query_payload::deserialize (nano::stream & stream)
void nano::node_id_handshake::response_payload::serialize (nano::stream & stream) const
{
nano::write (stream, node_id);
nano::write (stream, signature);
if (v2)
{
nano::write (stream, node_id);
nano::write (stream, v2->salt);
nano::write (stream, v2->genesis);
nano::write (stream, signature);
}
// TODO: Remove legacy handshake
else
{
nano::write (stream, node_id);
nano::write (stream, signature);
}
}
void nano::node_id_handshake::response_payload::deserialize (nano::stream & stream)
void nano::node_id_handshake::response_payload::deserialize (nano::stream & stream, nano::message_header const & header)
{
nano::read (stream, node_id);
nano::read (stream, signature);
if (is_v2 (header))
{
nano::read (stream, node_id);
v2_payload pld{};
nano::read (stream, pld.salt);
nano::read (stream, pld.genesis);
v2 = pld;
nano::read (stream, signature);
}
else
{
nano::read (stream, node_id);
nano::read (stream, signature);
}
}
std::size_t nano::node_id_handshake::response_payload::size (const nano::message_header & header)
{
return is_v2 (header) ? size_v2 : size_v1;
}
std::vector<uint8_t> nano::node_id_handshake::response_payload::data_to_sign (const nano::uint256_union & cookie) const
{
std::vector<uint8_t> bytes;
{
nano::vectorstream stream{ bytes };
if (v2)
{
nano::write (stream, cookie);
nano::write (stream, v2->salt);
nano::write (stream, v2->genesis);
}
// TODO: Remove legacy handshake
else
{
nano::write (stream, cookie);
}
}
return bytes;
}
void nano::node_id_handshake::response_payload::sign (const nano::uint256_union & cookie, nano::keypair const & key)
{
debug_assert (key.pub == node_id);
auto data = data_to_sign (cookie);
signature = nano::sign_message (key.prv, key.pub, data.data (), data.size ());
debug_assert (validate (cookie));
}
bool nano::node_id_handshake::response_payload::validate (const nano::uint256_union & cookie) const
{
auto data = data_to_sign (cookie);
if (nano::validate_message (node_id, data.data (), data.size (), signature)) // true => error
{
return false; // Fail
}
return true; // OK
}
/*
@ -1776,4 +1857,4 @@ void nano::asc_pull_ack::account_info_payload::deserialize (nano::stream & strea
nano::read_big_endian (stream, account_block_count);
nano::read (stream, account_conf_frontier);
nano::read_big_endian (stream, account_conf_height);
}
}

View file

@ -79,7 +79,7 @@ public:
std::bitset<16> extensions;
static std::size_t constexpr size = sizeof (nano::networks) + sizeof (version_max) + sizeof (version_using) + sizeof (version_min) + sizeof (type) + sizeof (/* extensions */ uint16_t);
void flag_set (uint8_t);
void flag_set (uint8_t, bool enable = true);
static uint8_t constexpr bulk_pull_count_present_flag = 0;
static uint8_t constexpr bulk_pull_ascending_flag = 1;
bool bulk_pull_is_count_present () const;
@ -326,13 +326,30 @@ public: // Payload definitions
{
public:
void serialize (nano::stream &) const;
void deserialize (nano::stream &);
void deserialize (nano::stream &, nano::message_header const &);
static std::size_t constexpr size = sizeof (nano::account) + sizeof (nano::signature);
void sign (nano::uint256_union const & cookie, nano::keypair const &);
bool validate (nano::uint256_union const & cookie) const;
private:
std::vector<uint8_t> data_to_sign (nano::uint256_union const & cookie) const;
public:
struct v2_payload
{
nano::uint256_union salt;
nano::block_hash genesis;
};
public:
nano::account node_id;
nano::signature signature;
std::optional<v2_payload> v2;
public:
static std::size_t constexpr size_v1 = sizeof (nano::account) + sizeof (nano::signature);
static std::size_t constexpr size_v2 = sizeof (nano::account) + sizeof (nano::signature) + sizeof (v2_payload);
static std::size_t size (nano::message_header const &);
};
public:
@ -350,9 +367,12 @@ public:
public: // Header
static uint8_t constexpr query_flag = 0;
static uint8_t constexpr response_flag = 1;
static uint8_t constexpr v2_flag = 2;
static bool is_query (nano::message_header const &);
static bool is_response (nano::message_header const &);
static bool is_v2 (nano::message_header const &);
bool is_v2 () const;
public: // Payload
std::optional<query_payload> query;

View file

@ -759,6 +759,68 @@ void nano::network::exclude (std::shared_ptr<nano::transport::channel> const & c
erase (*channel);
}
bool nano::network::verify_handshake_response (const nano::node_id_handshake::response_payload & response, const nano::endpoint & remote_endpoint)
{
// Prevent connection with ourselves
if (response.node_id == node.node_id.pub)
{
node.stats.inc (nano::stat::type::handshake, nano::stat::detail::invalid_node_id);
return false; // Fail
}
// Prevent mismatched genesis
if (response.v2 && response.v2->genesis != node.network_params.ledger.genesis->hash ())
{
node.stats.inc (nano::stat::type::handshake, nano::stat::detail::invalid_genesis);
return false; // Fail
}
auto cookie = syn_cookies.cookie (remote_endpoint);
if (!cookie)
{
node.stats.inc (nano::stat::type::handshake, nano::stat::detail::missing_cookie);
return false; // Fail
}
if (!response.validate (*cookie))
{
node.stats.inc (nano::stat::type::handshake, nano::stat::detail::invalid_signature);
return false; // Fail
}
node.stats.inc (nano::stat::type::handshake, nano::stat::detail::ok);
return true; // OK
}
std::optional<nano::node_id_handshake::query_payload> nano::network::prepare_handshake_query (const nano::endpoint & remote_endpoint)
{
if (auto cookie = syn_cookies.assign (remote_endpoint); cookie)
{
nano::node_id_handshake::query_payload query{ *cookie };
return query;
}
return std::nullopt;
}
nano::node_id_handshake::response_payload nano::network::prepare_handshake_response (const nano::node_id_handshake::query_payload & query, bool v2) const
{
nano::node_id_handshake::response_payload response{};
response.node_id = node.node_id.pub;
if (v2)
{
nano::node_id_handshake::response_payload::v2_payload response_v2{};
response_v2.salt = nano::random_pool::generate<uint256_union> ();
response_v2.genesis = node.network_params.ledger.genesis->hash ();
response.v2 = response_v2;
}
response.sign (query.cookie, node.node_id);
return response;
}
/*
* tcp_message_manager
*/
nano::tcp_message_manager::tcp_message_manager (unsigned incoming_connections_max_a) :
max_entries (incoming_connections_max_a * nano::tcp_message_manager::max_entries_per_connection + 1)
{
@ -810,6 +872,10 @@ void nano::tcp_message_manager::stop ()
producer_condition.notify_all ();
}
/*
* syn_cookies
*/
nano::syn_cookies::syn_cookies (std::size_t max_cookies_per_ip_a) :
max_cookies_per_ip (max_cookies_per_ip_a)
{
@ -888,6 +954,30 @@ void nano::syn_cookies::purge (std::chrono::steady_clock::time_point const & cut
}
}
std::optional<nano::uint256_union> nano::syn_cookies::cookie (const nano::endpoint & endpoint_a)
{
auto ip_addr (endpoint_a.address ());
debug_assert (ip_addr.is_v6 ());
nano::lock_guard<nano::mutex> lock{ syn_cookie_mutex };
auto cookie_it (cookies.find (endpoint_a));
if (cookie_it != cookies.end ())
{
auto cookie = cookie_it->second.cookie;
cookies.erase (cookie_it);
unsigned & ip_cookies = cookies_per_ip[ip_addr];
if (ip_cookies > 0)
{
--ip_cookies;
}
else
{
debug_assert (false && "More SYN cookies deleted than created for IP");
}
return cookie;
}
return std::nullopt;
}
std::size_t nano::syn_cookies::cookies_size ()
{
nano::lock_guard<nano::mutex> lock{ syn_cookie_mutex };

View file

@ -14,6 +14,7 @@
namespace nano
{
class node;
class tcp_message_manager final
{
public:
@ -34,13 +35,15 @@ private:
friend class network_tcp_message_manager_Test;
};
/**
* Node ID cookies for node ID handshakes
*/
class syn_cookies final
{
public:
syn_cookies (std::size_t);
explicit syn_cookies (std::size_t);
void purge (std::chrono::steady_clock::time_point const &);
// Returns boost::none if the IP is rate capped on syn cookie requests,
// or if the endpoint already has a syn cookie query
@ -48,6 +51,9 @@ public:
// Returns false if valid, true if invalid (true on error convention)
// Also removes the syn cookie from the store if valid
bool validate (nano::endpoint const &, nano::account const &, nano::signature const &);
/** Get cookie associated with endpoint and erases that cookie from this container */
std::optional<nano::uint256_union> cookie (nano::endpoint const &);
std::unique_ptr<container_info_component> collect_container_info (std::string const &);
std::size_t cookies_size ();
@ -122,6 +128,11 @@ public:
/** Disconnects and adds peer to exclusion list */
void exclude (std::shared_ptr<nano::transport::channel> const & channel);
/** Verifies that handshake response matches our query. @returns true if OK */
bool verify_handshake_response (nano::node_id_handshake::response_payload const & response, nano::endpoint const & remote_endpoint);
std::optional<nano::node_id_handshake::query_payload> prepare_handshake_query (nano::endpoint const & remote_endpoint);
nano::node_id_handshake::response_payload prepare_handshake_response (nano::node_id_handshake::query_payload const & query, bool v2) const;
static std::string to_string (nano::networks);
private:

View file

@ -40,6 +40,7 @@ class socket : public std::enable_shared_from_this<nano::transport::socket>
{
friend class server_socket;
friend class tcp_server;
friend class tcp_channels;
public:
enum class type_t

View file

@ -1,5 +1,6 @@
#include <nano/lib/stats.hpp>
#include <nano/node/node.hpp>
#include <nano/node/transport/message_deserializer.hpp>
#include <nano/node/transport/tcp.hpp>
#include <boost/format.hpp>
@ -544,14 +545,7 @@ void nano::transport::tcp_channels::start_tcp (nano::endpoint const & endpoint_a
if (!ec && channel)
{
// TCP node ID handshake
std::optional<nano::node_id_handshake::query_payload> query;
if (auto cookie = node_l->network.syn_cookies.assign (endpoint_a); cookie)
{
nano::node_id_handshake::query_payload pld{ *cookie };
query = pld;
}
auto query = node_l->network.prepare_handshake_query (endpoint_a);
nano::node_id_handshake message{ node_l->network_params.network, query };
if (node_l->config.logging.network_node_id_handshake_logging ())
@ -619,7 +613,12 @@ void nano::transport::tcp_channels::start_tcp_receive_node_id (std::shared_ptr<n
}
};
socket_l->async_read (receive_buffer_a, 8 + sizeof (nano::account) + sizeof (nano::account) + sizeof (nano::signature), [node_w, channel_a, endpoint_a, receive_buffer_a, cleanup_node_id_handshake_socket] (boost::system::error_code const & ec, std::size_t size_a) {
auto message_deserializer = std::make_shared<nano::transport::message_deserializer> (node.network_params.network, node.network.publish_filter, node.block_uniquer, node.vote_uniquer,
[socket_l] (std::shared_ptr<std::vector<uint8_t>> const & data_a, size_t size_a, std::function<void (boost::system::error_code const &, std::size_t)> callback_a) {
debug_assert (socket_l != nullptr);
socket_l->read_impl (data_a, size_a, callback_a);
});
message_deserializer->read ([node_w, socket_l, channel_a, endpoint_a, cleanup_node_id_handshake_socket] (boost::system::error_code ec, std::unique_ptr<nano::message> message) {
auto node_l = node_w.lock ();
if (!node_l)
{
@ -636,10 +635,9 @@ void nano::transport::tcp_channels::start_tcp_receive_node_id (std::shared_ptr<n
}
node_l->stats.inc (nano::stat::type::message, nano::stat::detail::node_id_handshake, nano::stat::dir::in);
auto error (false);
nano::bufferstream stream (receive_buffer_a->data (), size_a);
nano::message_header header (error, stream);
// the header type should in principle be checked after checking the network bytes and the version numbers, I will not change it here since the benefits do not outweight the difficulties
if (error || header.type != nano::message_type::node_id_handshake)
if (error || message->type () != nano::message_type::node_id_handshake)
{
if (node_l->config.logging.network_node_id_handshake_logging ())
{
@ -648,10 +646,12 @@ void nano::transport::tcp_channels::start_tcp_receive_node_id (std::shared_ptr<n
cleanup_node_id_handshake_socket (endpoint_a);
return;
}
if (header.network != node_l->network_params.network.current_network || header.version_using < node_l->network_params.network.protocol_version_min)
auto & handshake = static_cast<nano::node_id_handshake &> (*message);
if (message->header.network != node_l->network_params.network.current_network || message->header.version_using < node_l->network_params.network.protocol_version_min)
{
// error handling, either the networks bytes or the version is wrong
if (header.network == node_l->network_params.network.current_network)
if (message->header.network == node_l->network_params.network.current_network)
{
node_l->stats.inc (nano::stat::type::message, nano::stat::detail::invalid_network);
}
@ -668,8 +668,8 @@ void nano::transport::tcp_channels::start_tcp_receive_node_id (std::shared_ptr<n
}
return;
}
nano::node_id_handshake message (error, stream, header);
if (error || !message.response || !message.query)
if (error || !handshake.response || !handshake.query)
{
if (node_l->config.logging.network_node_id_handshake_logging ())
{
@ -678,15 +678,16 @@ void nano::transport::tcp_channels::start_tcp_receive_node_id (std::shared_ptr<n
cleanup_node_id_handshake_socket (endpoint_a);
return;
}
channel_a->set_network_version (header.version_using);
channel_a->set_network_version (handshake.header.version_using);
debug_assert (message.query);
debug_assert (message.response);
debug_assert (handshake.query);
debug_assert (handshake.response);
auto node_id = message.response->node_id;
bool process = (!node_l->network.syn_cookies.validate (endpoint_a, node_id, message.response->signature) && node_id != node_l->node_id.pub);
if (!process)
auto const node_id = handshake.response->node_id;
if (!node_l->network.verify_handshake_response (*handshake.response, endpoint_a))
{
cleanup_node_id_handshake_socket (endpoint_a);
return;
}
@ -695,18 +696,20 @@ void nano::transport::tcp_channels::start_tcp_receive_node_id (std::shared_ptr<n
auto existing_channel (node_l->network.tcp_channels.find_node_id (node_id));
if (existing_channel && !existing_channel->temporary)
{
cleanup_node_id_handshake_socket (endpoint_a);
return;
}
channel_a->set_node_id (node_id);
channel_a->set_last_packet_received (std::chrono::steady_clock::now ());
nano::node_id_handshake::response_payload response{ node_l->node_id.pub, nano::sign_message (node_l->node_id.prv, node_l->node_id.pub, message.query->cookie) };
debug_assert (handshake.query);
auto response = node_l->network.prepare_handshake_response (*handshake.query, handshake.is_v2 ());
nano::node_id_handshake handshake_response (node_l->network_params.network, std::nullopt, response);
if (node_l->config.logging.network_node_id_handshake_logging ())
{
node_l->logger.try_log (boost::str (boost::format ("Node ID handshake response sent with node ID %1% to %2%: query %3%") % node_l->node_id.pub.to_node_id () % endpoint_a % message.query->cookie.to_string ()));
node_l->logger.try_log (boost::str (boost::format ("Node ID handshake response sent with node ID %1% to %2%: query %3%") % node_l->node_id.pub.to_node_id () % endpoint_a % handshake.query->cookie.to_string ()));
}
channel_a->send (handshake_response, [node_w, channel_a, endpoint_a, cleanup_node_id_handshake_socket] (boost::system::error_code const & ec, std::size_t size_a) {

View file

@ -10,6 +10,10 @@
#include <memory>
/*
* tcp_listener
*/
nano::transport::tcp_listener::tcp_listener (uint16_t port_a, nano::node & node_a) :
node (node_a),
port (port_a)
@ -123,6 +127,10 @@ std::unique_ptr<nano::container_info_component> nano::transport::collect_contain
return composite;
}
/*
* tcp_server
*/
nano::transport::tcp_server::tcp_server (std::shared_ptr<nano::transport::socket> socket_a, std::shared_ptr<nano::node> node_a, bool allow_bootstrap_a) :
socket{ std::move (socket_a) },
node{ std::move (node_a) },
@ -315,6 +323,7 @@ void nano::transport::tcp_server::handshake_message_visitor::node_id_handshake (
{
server->node->logger.try_log (boost::str (boost::format ("Disabled realtime TCP for handshake %1%") % server->remote_endpoint));
}
// Stop invalid handshake
server->stop ();
return;
}
@ -325,6 +334,7 @@ void nano::transport::tcp_server::handshake_message_visitor::node_id_handshake (
{
server->node->logger.try_log (boost::str (boost::format ("Detected multiple node_id_handshake query from %1%") % server->remote_endpoint));
}
// Stop invalid handshake
server->stop ();
return;
}
@ -338,37 +348,29 @@ void nano::transport::tcp_server::handshake_message_visitor::node_id_handshake (
if (message.query)
{
server->send_handshake_response (*message.query);
server->send_handshake_response (*message.query, message.is_v2 ());
}
if (message.response)
{
nano::account const & node_id = message.response->node_id;
if (!server->node->network.syn_cookies.validate (nano::transport::map_tcp_to_endpoint (server->remote_endpoint), node_id, message.response->signature) && node_id != server->node->node_id.pub)
if (server->node->network.verify_handshake_response (*message.response, nano::transport::map_tcp_to_endpoint (server->remote_endpoint)))
{
server->to_realtime_connection (node_id);
server->to_realtime_connection (message.response->node_id);
}
else
{
// Stop invalid handshake
server->stop ();
return;
}
}
process = true;
}
void nano::transport::tcp_server::send_handshake_response (nano::node_id_handshake::query_payload const & query)
void nano::transport::tcp_server::send_handshake_response (nano::node_id_handshake::query_payload const & query, bool v2)
{
nano::node_id_handshake::response_payload response{ node->node_id.pub, nano::sign_message (node->node_id.prv, node->node_id.pub, query.cookie) };
debug_assert (!nano::validate_message (response.node_id, query.cookie, response.signature));
std::optional<nano::node_id_handshake::query_payload> own_query;
if (auto own_cookie = node->network.syn_cookies.assign (nano::transport::map_tcp_to_endpoint (remote_endpoint)); own_cookie)
{
nano::node_id_handshake::query_payload pld{ *own_cookie };
own_query = pld;
}
auto response = node->network.prepare_handshake_response (query, v2);
auto own_query = node->network.prepare_handshake_query (nano::transport::map_tcp_to_endpoint (remote_endpoint));
nano::node_id_handshake handshake_response{ node->network_params.network, own_query, response };
// TODO: Use channel

View file

@ -63,7 +63,7 @@ public:
std::chrono::steady_clock::time_point last_telemetry_req{};
private:
void send_handshake_response (nano::node_id_handshake::query_payload const & query);
void send_handshake_response (nano::node_id_handshake::query_payload const & query, bool v2);
void receive_message ();
void received_message (std::unique_ptr<nano::message> message);