Add SSL support to P2P

This commit is contained in:
Lee *!* Clagett 2023-09-19 19:05:02 -04:00
parent cc73fe7116
commit 60925f1846
29 changed files with 756 additions and 363 deletions

View file

@ -131,6 +131,7 @@ namespace net_utils
void terminate(); void terminate();
void on_terminating(); void on_terminating();
bool send(epee::byte_slice message); bool send(epee::byte_slice message);
bool start_internal( bool start_internal(
bool is_income, bool is_income,
@ -296,9 +297,20 @@ namespace net_utils
bool speed_limit_is_enabled() const; ///< tells us should we be sleeping here (e.g. do not sleep on RPC connections) bool speed_limit_is_enabled() const; ///< tells us should we be sleeping here (e.g. do not sleep on RPC connections)
void set_ssl_enabled()
{
m_state.ssl.enabled = true;
m_state.ssl.handshaked = true;
}
bool cancel(); bool cancel();
//! Used by boosted_tcp_server class in async_connect_internal
template<typename F>
auto wrap(F&& f)
{
return m_strand.wrap(std::forward<F>(f));
}
private: private:
//----------------- i_service_endpoint --------------------- //----------------- i_service_endpoint ---------------------
virtual bool do_send(byte_slice message); ///< (see do_send from i_service_endpoint) virtual bool do_send(byte_slice message); ///< (see do_send from i_service_endpoint)
@ -376,10 +388,11 @@ namespace net_utils
} }
bool add_connection(t_connection_context& out, boost::asio::ip::tcp::socket&& sock, network_address real_remote, epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect); bool add_connection(t_connection_context& out, boost::asio::ip::tcp::socket&& sock, network_address real_remote, epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect);
try_connect_result_t try_connect(connection_ptr new_connection_l, const std::string& adr, const std::string& port, boost::asio::ip::tcp::socket &sock_, const boost::asio::ip::tcp::endpoint &remote_endpoint, const std::string &bind_ip, uint32_t conn_timeout, epee::net_utils::ssl_support_t ssl_support); try_connect_result_t try_connect(connection_ptr new_connection_l, const std::string& adr, const std::string& port, boost::asio::ip::tcp::socket &sock_, const boost::asio::ip::tcp::endpoint &remote_endpoint, const std::string &bind_ip, uint32_t conn_timeout, epee::net_utils::ssl_options_t& ssl_support);
bool connect(const std::string& adr, const std::string& port, uint32_t conn_timeot, t_connection_context& cn, const std::string& bind_ip = "0.0.0.0", epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect); bool connect(const std::string& adr, const std::string& port, uint32_t conn_timeot, t_connection_context& cn, const std::string& bind_ip = "0.0.0.0", epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_autodetect);
template<class t_callback> template<class t_callback>
bool connect_async(const std::string& adr, const std::string& port, uint32_t conn_timeot, const t_callback &cb, const std::string& bind_ip = "0.0.0.0", epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect); bool connect_async(const std::string& adr, const std::string& port, uint32_t conn_timeot, const t_callback &cb, const std::string& bind_ip = "0.0.0.0", epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_autodetect);
boost::asio::ssl::context& get_ssl_context() noexcept boost::asio::ssl::context& get_ssl_context() noexcept
{ {
@ -477,6 +490,11 @@ namespace net_utils
bool is_thread_worker(); bool is_thread_worker();
template<typename t_callback>
bool connect_async_internal(const connection_ptr& new_connection_l, const boost::asio::ip::tcp::endpoint& remote_endpoint, uint32_t conn_timeout, const t_callback &cb);
bool remove_connection(const connection_ptr& ptr);
const std::shared_ptr<typename connection<t_protocol_handler>::shared_state> m_state; const std::shared_ptr<typename connection<t_protocol_handler>::shared_state> m_state;
/// The io_service used to perform asynchronous operations. /// The io_service used to perform asynchronous operations.

View file

@ -895,7 +895,7 @@ namespace net_utils
boost::uuids::random_generator()(), boost::uuids::random_generator()(),
*real_remote, *real_remote,
is_income, is_income,
connection_basic::m_ssl_support == ssl_support_t::e_ssl_support_enabled connection_basic::m_ssl_support
); );
m_host = real_remote->host_str(); m_host = real_remote->host_str();
try { host_count(1); } catch(...) { /* ignore */ } try { host_count(1); } catch(...) { /* ignore */ }
@ -1559,7 +1559,7 @@ namespace net_utils
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
template<class t_protocol_handler> template<class t_protocol_handler>
typename boosted_tcp_server<t_protocol_handler>::try_connect_result_t boosted_tcp_server<t_protocol_handler>::try_connect(connection_ptr new_connection_l, const std::string& adr, const std::string& port, boost::asio::ip::tcp::socket &sock_, const boost::asio::ip::tcp::endpoint &remote_endpoint, const std::string &bind_ip, uint32_t conn_timeout, epee::net_utils::ssl_support_t ssl_support) typename boosted_tcp_server<t_protocol_handler>::try_connect_result_t boosted_tcp_server<t_protocol_handler>::try_connect(connection_ptr new_connection_l, const std::string& adr, const std::string& port, boost::asio::ip::tcp::socket &sock_, const boost::asio::ip::tcp::endpoint &remote_endpoint, const std::string &bind_ip, uint32_t conn_timeout, epee::net_utils::ssl_options_t& ssl_options)
{ {
TRY_ENTRY(); TRY_ENTRY();
@ -1635,7 +1635,7 @@ namespace net_utils
{ {
// Handshake // Handshake
MDEBUG("Handshaking SSL..."); MDEBUG("Handshaking SSL...");
if (!new_connection_l->handshake(boost::asio::ssl::stream_base::client)) if (!new_connection_l->client_handshake(ssl_options))
{ {
if (ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect) if (ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect)
{ {
@ -1649,6 +1649,7 @@ namespace net_utils
sock_.close(); sock_.close();
return CONNECT_FAILURE; return CONNECT_FAILURE;
} }
new_connection_l->set_ssl_enabled();
} }
return CONNECT_SUCCESS; return CONNECT_SUCCESS;
@ -1657,11 +1658,11 @@ namespace net_utils
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
template<class t_protocol_handler> template<class t_protocol_handler>
bool boosted_tcp_server<t_protocol_handler>::connect(const std::string& adr, const std::string& port, uint32_t conn_timeout, t_connection_context& conn_context, const std::string& bind_ip, epee::net_utils::ssl_support_t ssl_support) bool boosted_tcp_server<t_protocol_handler>::connect(const std::string& adr, const std::string& port, uint32_t conn_timeout, t_connection_context& conn_context, const std::string& bind_ip, epee::net_utils::ssl_options_t ssl_options)
{ {
TRY_ENTRY(); TRY_ENTRY();
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, ssl_support) ); connection_ptr new_connection_l(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, ssl_options.support) );
connections_mutex.lock(); connections_mutex.lock();
connections_.insert(new_connection_l); connections_.insert(new_connection_l);
MDEBUG("connections_ size now " << connections_.size()); MDEBUG("connections_ size now " << connections_.size());
@ -1746,24 +1747,22 @@ namespace net_utils
//boost::asio::ip::tcp::endpoint remote_endpoint(boost::asio::ip::address::from_string(addr.c_str()), port); //boost::asio::ip::tcp::endpoint remote_endpoint(boost::asio::ip::address::from_string(addr.c_str()), port);
boost::asio::ip::tcp::endpoint remote_endpoint(*iterator); boost::asio::ip::tcp::endpoint remote_endpoint(*iterator);
auto try_connect_result = try_connect(new_connection_l, adr, port, sock_, remote_endpoint, bind_ip_to_use, conn_timeout, ssl_support); auto try_connect_result = try_connect(new_connection_l, adr, port, sock_, remote_endpoint, bind_ip_to_use, conn_timeout, ssl_options);
if (try_connect_result == CONNECT_FAILURE) if (try_connect_result == CONNECT_FAILURE)
return false; return false;
if (ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect && try_connect_result == CONNECT_NO_SSL) if (ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect && try_connect_result == CONNECT_NO_SSL)
{ {
// we connected, but could not connect with SSL, try without // we connected, but could not connect with SSL, try without
MERROR("SSL handshake failed on an autodetect connection, reconnecting without SSL"); MERROR("SSL handshake failed on an autodetect connection, reconnecting without SSL");
new_connection_l->disable_ssl(); new_connection_l->disable_ssl();
try_connect_result = try_connect(new_connection_l, adr, port, sock_, remote_endpoint, bind_ip_to_use, conn_timeout, epee::net_utils::ssl_support_t::e_ssl_support_disabled); ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_disabled;
try_connect_result = try_connect(new_connection_l, adr, port, sock_, remote_endpoint, bind_ip_to_use, conn_timeout, ssl_options);
if (try_connect_result != CONNECT_SUCCESS) if (try_connect_result != CONNECT_SUCCESS)
return false; return false;
} }
// start adds the connection to the config object's list, so we don't need to have it locally anymore // start adds the connection to the config object's list, so we don't need to have it locally anymore
connections_mutex.lock(); bool r = remove_connection(new_connection_l) && new_connection_l->start(false, 1 < m_threads_count);
connections_.erase(new_connection_l);
connections_mutex.unlock();
bool r = new_connection_l->start(false, 1 < m_threads_count);
if (r) if (r)
{ {
new_connection_l->get_context(conn_context); new_connection_l->get_context(conn_context);
@ -1783,10 +1782,10 @@ namespace net_utils
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
template<class t_protocol_handler> template<class t_callback> template<class t_protocol_handler> template<class t_callback>
bool boosted_tcp_server<t_protocol_handler>::connect_async(const std::string& adr, const std::string& port, uint32_t conn_timeout, const t_callback &cb, const std::string& bind_ip, epee::net_utils::ssl_support_t ssl_support) bool boosted_tcp_server<t_protocol_handler>::connect_async(const std::string& adr, const std::string& port, uint32_t conn_timeout, const t_callback &cb, const std::string& bind_ip, epee::net_utils::ssl_options_t ssl_options)
{ {
TRY_ENTRY(); TRY_ENTRY();
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, ssl_support) ); connection_ptr new_connection_l(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, ssl_options.support) );
connections_mutex.lock(); connections_mutex.lock();
connections_.insert(new_connection_l); connections_.insert(new_connection_l);
MDEBUG("connections_ size now " << connections_.size()); MDEBUG("connections_ size now " << connections_.size());
@ -1864,59 +1863,112 @@ namespace net_utils
} }
} }
boost::shared_ptr<boost::asio::deadline_timer> sh_deadline(new boost::asio::deadline_timer(io_service_)); ssl_options.configure(new_connection_l->socket_, boost::asio::ssl::stream_base::client);
//start deadline return connect_async_internal(new_connection_l, remote_endpoint, conn_timeout, cb);
sh_deadline->expires_from_now(boost::posix_time::milliseconds(conn_timeout));
sh_deadline->async_wait([=](const boost::system::error_code& error)
{
if(error != boost::asio::error::operation_aborted)
{
_dbg3("Failed to connect to " << adr << ':' << port << ", because of timeout (" << conn_timeout << ")");
new_connection_l->socket().close();
}
});
//start async connect
sock_.async_connect(remote_endpoint, [=](const boost::system::error_code& ec_)
{
t_connection_context conn_context = AUTO_VAL_INIT(conn_context);
boost::system::error_code ignored_ec;
boost::asio::ip::tcp::socket::endpoint_type lep = new_connection_l->socket().local_endpoint(ignored_ec);
if(!ec_)
{//success
if(!sh_deadline->cancel())
{
cb(conn_context, boost::asio::error::operation_aborted);//this mean that deadline timer already queued callback with cancel operation, rare situation
}else
{
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] Connected success to " << adr << ':' << port <<
" from " << lep.address().to_string() << ':' << lep.port());
// start adds the connection to the config object's list, so we don't need to have it locally anymore
connections_mutex.lock();
connections_.erase(new_connection_l);
connections_mutex.unlock();
bool r = new_connection_l->start(false, 1 < m_threads_count);
if (r)
{
new_connection_l->get_context(conn_context);
cb(conn_context, ec_);
}
else
{
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] Failed to start connection to " << adr << ':' << port);
cb(conn_context, boost::asio::error::fault);
}
}
}else
{
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] Failed to connect to " << adr << ':' << port <<
" from " << lep.address().to_string() << ':' << lep.port() << ": " << ec_.message() << ':' << ec_.value());
cb(conn_context, ec_);
}
});
return true;
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::connect_async", false); CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::connect_async", false);
} }
template<class t_protocol_handler> template<class t_callback>
bool boosted_tcp_server<t_protocol_handler>::connect_async_internal(const connection_ptr& new_connection_l, const boost::asio::ip::tcp::endpoint& remote_endpoint, uint32_t conn_timeout, const t_callback &cb)
{
if (!new_connection_l)
return false;
TRY_ENTRY();
const auto on_timer = [=](boost::system::error_code error)
{
if (error != boost::asio::error::operation_aborted)
{
_dbg3("Failed to connect to " << remote_endpoint << ", because of timeout (" << conn_timeout << ')');
new_connection_l->socket().close(error); // ignore errors
}
};
auto sh_deadline = std::make_shared<boost::asio::deadline_timer>(io_service_);
sh_deadline->expires_from_now(boost::posix_time::milliseconds(conn_timeout));
sh_deadline->async_wait(new_connection_l->wrap(on_timer));
new_connection_l->socket().async_connect(remote_endpoint, new_connection_l->wrap([=](const boost::system::error_code& ec_)
{
const auto on_cancel = [=](const boost::system::error_code& error)
{
boost::system::error_code ignored_ec{};
const auto lep = new_connection_l->socket().local_endpoint(ignored_ec);
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] to " << remote_endpoint << " from " << lep << " failed: " << error.message());
if (remove_connection(new_connection_l))
cb(t_connection_context{}, error);
};
if(!ec_)
{//success
const auto on_ready = [=] ()
{
if (sh_deadline->cancel())
{
boost::system::error_code ignored_ec{};
const auto lep = new_connection_l->socket().local_endpoint(ignored_ec);
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] Connected successfully to " << remote_endpoint <<
" from " << lep.address().to_string() << ':' << lep.port());
if (remove_connection(new_connection_l) && new_connection_l->start(false, 1 < m_threads_count))
{
t_connection_context conn_context{};
new_connection_l->get_context(conn_context);
cb(conn_context, ec_);
}
else
on_cancel(boost::asio::error::fault);
}
else // if timer already expired
on_cancel(boost::asio::error::operation_aborted);
};
if (new_connection_l->get_ssl_support() != ssl_support_t::e_ssl_support_disabled)
{
// set new timer for handshake
if (sh_deadline->expires_from_now(boost::posix_time::milliseconds(conn_timeout)))
{
sh_deadline->async_wait(new_connection_l->wrap(on_timer));
new_connection_l->socket_.async_handshake(boost::asio::ssl::stream_base::client, new_connection_l->wrap([=] (const boost::system::error_code& ec)
{
if (ec)
{
if (new_connection_l->get_ssl_support() == ssl_support_t::e_ssl_support_autodetect)
{
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] SSL connection to " <<
remote_endpoint << " failed: " << ec.message() << ". Trying without SSL");
new_connection_l->disable_ssl();
connect_async_internal(new_connection_l, remote_endpoint, conn_timeout, cb);
}
else // ssl mandatory and failed
on_cancel(ec);
}
else // ssl handshake complete
on_ready();
}));
}
else // if timer already expired
on_cancel(boost::asio::error::operation_aborted);
}
else // ssl disabled
on_ready();
}
else // ec_ has error
on_cancel(ec_);
}));
return true;
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::connect_async_internal", false);
}
template<class t_protocol_handler>
bool boosted_tcp_server<t_protocol_handler>::remove_connection(const connection_ptr& new_connection)
{
if (!new_connection)
return false;
const boost::lock_guard<boost::mutex> sync{connections_mutex};
connections_.erase(new_connection);
return true;
}
} // namespace } // namespace
} // namespace } // namespace

View file

@ -132,37 +132,15 @@ class connection_basic { // not-templated base class for rapid developmet of som
ssl_support_t get_ssl_support() const { return m_ssl_support; } ssl_support_t get_ssl_support() const { return m_ssl_support; }
void disable_ssl() { m_ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_disabled; } void disable_ssl() { m_ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_disabled; }
bool handshake(boost::asio::ssl::stream_base::handshake_type type, boost::asio::const_buffer buffer = {}) bool client_handshake(ssl_options_t& ssl)
{
return ssl.handshake(socket_, boost::asio::ssl::stream_base::client);
}
bool server_handshake(boost::asio::const_buffer buffer)
{ {
//m_state != nullptr verified in constructor //m_state != nullptr verified in constructor
return m_state->ssl_options().handshake(socket_, type, buffer); return m_state->ssl_options().handshake(socket_, boost::asio::ssl::stream_base::server, buffer);
}
template<typename MutableBufferSequence, typename ReadHandler>
void async_read_some(const MutableBufferSequence &buffers, ReadHandler &&handler)
{
if (m_ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_enabled)
socket_.async_read_some(buffers, std::forward<ReadHandler>(handler));
else
socket().async_read_some(buffers, std::forward<ReadHandler>(handler));
}
template<typename ConstBufferSequence, typename WriteHandler>
void async_write_some(const ConstBufferSequence &buffers, WriteHandler &&handler)
{
if (m_ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_enabled)
socket_.async_write_some(buffers, std::forward<WriteHandler>(handler));
else
socket().async_write_some(buffers, std::forward<WriteHandler>(handler));
}
template<typename ConstBufferSequence, typename WriteHandler>
void async_write(const ConstBufferSequence &buffers, WriteHandler &&handler)
{
if (m_ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_enabled)
boost::asio::async_write(socket_, buffers, std::forward<WriteHandler>(handler));
else
boost::asio::async_write(socket(), buffers, std::forward<WriteHandler>(handler));
} }
// various handlers to be called from connection class: // various handlers to be called from connection class:

View file

@ -130,7 +130,7 @@ namespace levin
//! Provides space for levin (p2p) header, so that payload can be sent without copy //! Provides space for levin (p2p) header, so that payload can be sent without copy
class message_writer class message_writer
{ {
byte_slice finalize(uint32_t command, uint32_t flags, uint32_t return_code, bool expect_response); byte_slice finalize(uint32_t command, uint32_t flags, uint32_t return_code, bool expect_response, bool pad);
public: public:
using header = bucket_head2; using header = bucket_head2;
@ -148,11 +148,12 @@ namespace levin
return buffer.size() < sizeof(header) ? 0 : buffer.size() - sizeof(header); return buffer.size() < sizeof(header) ? 0 : buffer.size() - sizeof(header);
} }
byte_slice finalize_invoke(uint32_t command) { return finalize(command, LEVIN_PACKET_REQUEST, 0, true); } // `pad == true` will add 0-8192 of zero bytes (actual amount randomized)
byte_slice finalize_notify(uint32_t command) { return finalize(command, LEVIN_PACKET_REQUEST, 0, false); } byte_slice finalize_invoke(uint32_t command, bool pad) { return finalize(command, LEVIN_PACKET_REQUEST, 0, true, pad); }
byte_slice finalize_response(uint32_t command, uint32_t return_code) byte_slice finalize_notify(uint32_t command, bool pad) { return finalize(command, LEVIN_PACKET_REQUEST, 0, false, pad); }
byte_slice finalize_response(uint32_t command, uint32_t return_code, bool pad)
{ {
return finalize(command, LEVIN_PACKET_RESPONSE, return_code, false); return finalize(command, LEVIN_PACKET_RESPONSE, return_code, false, pad);
} }
//! Has space for levin header until a finalize method is used //! Has space for levin header until a finalize method is used

View file

@ -486,7 +486,7 @@ public:
bool is_response = (m_oponent_protocol_ver == LEVIN_PROTOCOL_VER_1 && m_current_head.m_flags&LEVIN_PACKET_RESPONSE); bool is_response = (m_oponent_protocol_ver == LEVIN_PROTOCOL_VER_1 && m_current_head.m_flags&LEVIN_PACKET_RESPONSE);
MDEBUG(m_connection_context << "LEVIN_PACKET_RECEIVED. [len=" << m_current_head.m_cb MDEBUG(m_connection_context << "LEVIN_PACKET_RECEIVED. [len=" << m_current_head.m_cb
<< ", flags" << m_current_head.m_flags << ", flags" << m_current_head.m_flags
<< ", r?=" << m_current_head.m_have_to_return_data << ", r?=" << m_current_head.m_have_to_return_data
<<", cmd = " << m_current_head.m_command <<", cmd = " << m_current_head.m_command
@ -526,7 +526,7 @@ public:
if (m_current_head.m_command == m_connection_context.handshake_command() && m_connection_context.handshake_complete()) if (m_current_head.m_command == m_connection_context.handshake_command() && m_connection_context.handshake_complete())
m_max_packet_size = m_config.m_max_packet_size; m_max_packet_size = m_config.m_max_packet_size;
if(!send_message(return_message.finalize_response(m_current_head.m_command, return_code))) if(!send_message(return_message.finalize_response(m_current_head.m_command, return_code, m_connection_context.should_pad())))
return false; return false;
} }
else else
@ -620,7 +620,7 @@ public:
if (command == m_connection_context.handshake_command()) if (command == m_connection_context.handshake_command())
m_max_packet_size = m_config.m_max_packet_size; m_max_packet_size = m_config.m_max_packet_size;
if(!send_message(in_msg.finalize_invoke(command))) if(!send_message(in_msg.finalize_invoke(command, m_connection_context.should_pad())))
{ {
LOG_ERROR_CC(m_connection_context, "Failed to do_send"); LOG_ERROR_CC(m_connection_context, "Failed to do_send");
err_code = LEVIN_ERROR_CONNECTION; err_code = LEVIN_ERROR_CONNECTION;

View file

@ -46,7 +46,7 @@ namespace epee
namespace net_utils namespace net_utils
{ {
enum class ssl_support_t: uint8_t { enum class ssl_support_t: uint8_t {
e_ssl_support_disabled, e_ssl_support_disabled = 0,
e_ssl_support_enabled, e_ssl_support_enabled,
e_ssl_support_autodetect, e_ssl_support_autodetect,
}; };
@ -107,6 +107,9 @@ namespace net_utils
//! \return True if `host` can be verified using `this` configuration WITHOUT system "root" CAs. //! \return True if `host` can be verified using `this` configuration WITHOUT system "root" CAs.
bool has_strong_verification(boost::string_ref host) const noexcept; bool has_strong_verification(boost::string_ref host) const noexcept;
//! \return All fingerprints
const std::vector<std::vector<std::uint8_t>>& fingerprints() const noexcept { return fingerprints_; }
//! Search against internal fingerprints. Always false if `behavior() != user_certificate_check`. //! Search against internal fingerprints. Always false if `behavior() != user_certificate_check`.
bool has_fingerprint(boost::asio::ssl::verify_context &ctx) const; bool has_fingerprint(boost::asio::ssl::verify_context &ctx) const;
@ -151,6 +154,30 @@ namespace net_utils
bool create_ec_ssl_certificate(EVP_PKEY *&pkey, X509 *&cert); bool create_ec_ssl_certificate(EVP_PKEY *&pkey, X509 *&cert);
bool create_rsa_ssl_certificate(EVP_PKEY *&pkey, X509 *&cert); bool create_rsa_ssl_certificate(EVP_PKEY *&pkey, X509 *&cert);
std::vector<std::uint8_t> convert_fingerprint(const boost::string_ref id);
/**
* @brief Create a binary X509 certificate fingerprint
*
* @param[in] cert The certificate which will be used to create the fingerprint
* @param[in] fdig The digest algorithm to use, defaults to SHA-256 b/c that is what ssl_options_t uses
* @return The binary fingerprint string
*
* @throw boost::system_error if there is an OpenSSL error
*/
std::string get_ssl_fingerprint(const X509 *cert, const EVP_MD *fdig = EVP_sha256());
/**
* @brief Create a binary X509 certificate fingerprint
*
* @param[in] context The context for the current certificate.
* @param[in] fdig The digest algorithm to use, defaults to SHA-256 b/c that is what ssl_options_t uses
* @return The binary fingerprint string
*
* @throw boost::system_error if there is an OpenSSL error
*/
std::string get_ssl_fingerprint(boost::asio::ssl::context& context, const EVP_MD *fdig = EVP_sha256());
/** /**
* @brief Create a human-readable X509 certificate fingerprint * @brief Create a human-readable X509 certificate fingerprint
* *

View file

@ -32,11 +32,13 @@
#include <boost/uuid/uuid.hpp> #include <boost/uuid/uuid.hpp>
#include <boost/asio/io_service.hpp> #include <boost/asio/io_service.hpp>
#include <boost/asio/ip/address_v6.hpp> #include <boost/asio/ip/address_v6.hpp>
#include <boost/optional/optional.hpp>
#include <typeinfo> #include <typeinfo>
#include <type_traits> #include <type_traits>
#include "byte_slice.h" #include "byte_slice.h"
#include "enums.h" #include "enums.h"
#include "misc_log_ex.h" #include "misc_log_ex.h"
#include "net_ssl.h"
#include "serialization/keyvalue_serialization.h" #include "serialization/keyvalue_serialization.h"
#include "int-util.h" #include "int-util.h"
@ -367,7 +369,7 @@ namespace net_utils
const network_address m_remote_address; const network_address m_remote_address;
const bool m_is_income; const bool m_is_income;
const time_t m_started; const time_t m_started;
const bool m_ssl; const ssl_support_t m_ssl;
time_t m_last_recv; time_t m_last_recv;
time_t m_last_send; time_t m_last_send;
uint64_t m_recv_cnt; uint64_t m_recv_cnt;
@ -378,7 +380,7 @@ namespace net_utils
double m_max_speed_up; double m_max_speed_up;
connection_context_base(boost::uuids::uuid connection_id, connection_context_base(boost::uuids::uuid connection_id,
const network_address &remote_address, bool is_income, bool ssl, const network_address &remote_address, bool is_income, ssl_support_t ssl,
time_t last_recv = 0, time_t last_send = 0, time_t last_recv = 0, time_t last_send = 0,
uint64_t recv_cnt = 0, uint64_t send_cnt = 0): uint64_t recv_cnt = 0, uint64_t send_cnt = 0):
m_connection_id(connection_id), m_connection_id(connection_id),
@ -400,7 +402,7 @@ namespace net_utils
m_remote_address(), m_remote_address(),
m_is_income(false), m_is_income(false),
m_started(time(NULL)), m_started(time(NULL)),
m_ssl(false), m_ssl(ssl_support_t::e_ssl_support_disabled),
m_last_recv(0), m_last_recv(0),
m_last_send(0), m_last_send(0),
m_recv_cnt(0), m_recv_cnt(0),
@ -422,10 +424,17 @@ namespace net_utils
return *this; return *this;
} }
bool should_pad() const
{
if (m_remote_address.get_zone() == zone::public_)
return m_ssl != ssl_support_t::e_ssl_support_disabled;
return true; // all other zones use encryption by default
}
private: private:
template<class t_protocol_handler> template<class t_protocol_handler>
friend class connection; friend class connection;
void set_details(boost::uuids::uuid connection_id, const network_address &remote_address, bool is_income, bool ssl) void set_details(boost::uuids::uuid connection_id, const network_address &remote_address, bool is_income, ssl_support_t ssl)
{ {
this->~connection_context_base(); this->~connection_context_base();
new(this) connection_context_base(connection_id, remote_address, is_income, ssl); new(this) connection_context_base(connection_id, remote_address, is_income, ssl);

View file

@ -111,7 +111,8 @@ namespace epee
levin::message_writer to_send; levin::message_writer to_send;
stg.store_to_binary(to_send.buffer); stg.store_to_binary(to_send.buffer);
int res = transport.send(to_send.finalize_notify(command), conn_id); const bool ssl = (context.m_ssl != ssl_support_t::e_ssl_support_disabled);
int res = transport.send(to_send.finalize_notify(command, ssl), conn_id);
if(res <=0 ) if(res <=0 )
{ {
MERROR("Failed to notify command " << command << " return code " << res); MERROR("Failed to notify command " << command << " return code " << res);

View file

@ -70,6 +70,7 @@ target_link_libraries(epee
${Boost_REGEX_LIBRARY} ${Boost_REGEX_LIBRARY}
${Boost_SYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY}
${OPENSSL_LIBRARIES} ${OPENSSL_LIBRARIES}
${sodium_LIBRARIES}
PRIVATE PRIVATE
${EXTRA_LIBRARIES}) ${EXTRA_LIBRARIES})

View file

@ -28,6 +28,8 @@
#include "net/levin_base.h" #include "net/levin_base.h"
#include <sodium/randombytes.h>
#include "cryptonote_config.h"
#include "int-util.h" #include "int-util.h"
namespace epee namespace epee
@ -41,15 +43,25 @@ namespace levin
buffer.put_n(0, sizeof(header)); buffer.put_n(0, sizeof(header));
} }
byte_slice message_writer::finalize(const uint32_t command, const uint32_t flags, const uint32_t return_code, const bool expect_response) byte_slice message_writer::finalize(const uint32_t command, const uint32_t flags, const uint32_t return_code, const bool expect_response, const bool pad)
{ {
if (buffer.size() < sizeof(header)) if (buffer.size() < sizeof(header))
throw std::runtime_error{"levin_writer::finalize already called"}; throw std::runtime_error{"levin_writer::finalize already called"};
header head = make_header(command, payload_size(), flags, expect_response); std::uint32_t pad_bytes = 0;
if (pad)
{
static_assert(P2P_MAX_LEVIN_PAD_BYTES <= std::numeric_limits<std::uint32_t>::max(), "invalid max ssl pad bytes");
pad_bytes = randombytes_uniform(P2P_MAX_LEVIN_PAD_BYTES);
if (std::numeric_limits<std::size_t>::max() - payload_size() < pad_bytes)
throw std::runtime_error{"levin_write::finalize failed to pad bytes due to overflow"};
}
header head = make_header(command, payload_size() + pad_bytes, flags, expect_response);
head.m_return_code = SWAP32LE(return_code); head.m_return_code = SWAP32LE(return_code);
std::memcpy(buffer.tellp() - buffer.size(), std::addressof(head), sizeof(head)); std::memcpy(buffer.tellp() - buffer.size(), std::addressof(head), sizeof(head));
buffer.put_n(0, pad_bytes);
return byte_slice{std::move(buffer)}; return byte_slice{std::move(buffer)};
} }
@ -92,12 +104,13 @@ namespace levin
will ignore extra bytes. So just pad with zeroes and otherwise send will ignore extra bytes. So just pad with zeroes and otherwise send
a "normal", not fragmented message. */ a "normal", not fragmented message. */
// do not use randomized padding, this must be to message boundary
message.buffer.put_n(0, noise_size - message.buffer.size()); message.buffer.put_n(0, noise_size - message.buffer.size());
return message.finalize_notify(command); return message.finalize_notify(command, false);
} }
// fragment message // fragment message
const byte_slice payload_bytes = message.finalize_notify(command); const byte_slice payload_bytes = message.finalize_notify(command, false);
span<const std::uint8_t> payload = to_span(payload_bytes); span<const std::uint8_t> payload = to_span(payload_bytes);
const size_t payload_space = noise_size - sizeof(bucket_head2); const size_t payload_space = noise_size - sizeof(bucket_head2);

View file

@ -27,6 +27,7 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <algorithm>
#include <string.h> #include <string.h>
#include <thread> #include <thread>
#include <boost/asio/ssl.hpp> #include <boost/asio/ssl.hpp>
@ -521,18 +522,19 @@ void ssl_options_t::configure(
{ {
socket.set_verify_mode(boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert); socket.set_verify_mode(boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert);
// callback occurs after `configure` has exited, so keep copies and not references
socket.set_verify_callback([&](const bool preverified, boost::asio::ssl::verify_context &ctx) const ssl_options_t this_copy(*this);
socket.set_verify_callback([host, this_copy = std::move(this_copy)](const bool preverified, boost::asio::ssl::verify_context &ctx)
{ {
// preverified means it passed system or user CA check. System CA is never loaded // preverified means it passed system or user CA check. System CA is never loaded
// when fingerprints are whitelisted. // when fingerprints are whitelisted.
const bool verified = preverified && const bool verified = preverified &&
(verification != ssl_verification_t::system_ca || host.empty() || boost::asio::ssl::rfc2818_verification(host)(preverified, ctx)); (this_copy.verification != ssl_verification_t::system_ca || host.empty() || boost::asio::ssl::rfc2818_verification(host)(preverified, ctx));
if (!verified && !has_fingerprint(ctx)) if (!verified && !this_copy.has_fingerprint(ctx))
{ {
// autodetect will reconnect without SSL - warn and keep connection encrypted // autodetect will reconnect without SSL - warn and keep connection encrypted
if (support != ssl_support_t::e_ssl_support_autodetect) if (this_copy.support != ssl_support_t::e_ssl_support_autodetect)
{ {
MERROR("SSL certificate is not in the allowed list, connection dropped"); MERROR("SSL certificate is not in the allowed list, connection dropped");
return false; return false;
@ -644,6 +646,44 @@ bool ssl_options_t::handshake(
return true; return true;
} }
std::vector<std::uint8_t> convert_fingerprint(const boost::string_ref id)
{
std::vector<std::uint8_t> out;
out.resize(id.size());
std::copy(id.begin(), id.end(), out.begin());
return out;
}
std::string get_ssl_fingerprint(const X509 *cert, const EVP_MD *fdig)
{
CHECK_AND_ASSERT_THROW_MES(cert && fdig, "Pointer args to get_ssl_fingerprint cannot be null");
std::string md;
unsigned n = 0;
md.resize(EVP_MAX_MD_SIZE);
if (!X509_digest(cert, fdig, reinterpret_cast<std::uint8_t*>(&md[0]), &n))
{
const unsigned long ssl_err_val = static_cast<int>(ERR_get_error());
const boost::system::error_code ssl_err_code = boost::asio::error::ssl_errors(static_cast<int>(ssl_err_val));
MERROR("Failed to create SSL fingerprint: " << ERR_reason_error_string(ssl_err_val));
throw boost::system::system_error(ssl_err_code, ERR_reason_error_string(ssl_err_val));
}
md.resize(n);
return md;
}
std::string get_ssl_fingerprint(boost::asio::ssl::context &ssl_context, const EVP_MD *fdig)
{
SSL_CTX *ctx = ssl_context.native_handle();
CHECK_AND_ASSERT_THROW_MES(ctx, "Failed to get SSL context");
X509* ssl_cert = nullptr;
std::unique_ptr<SSL, decltype(&SSL_free)> dflt_SSL(SSL_new(ctx), SSL_free);
CHECK_AND_ASSERT_THROW_MES(dflt_SSL, "Failed to create new SSL object");
CHECK_AND_ASSERT_THROW_MES((ssl_cert = SSL_get_certificate(dflt_SSL.get())), "Failed to get SSL certificate");
return get_ssl_fingerprint(ssl_cert, fdig);
}
std::string get_hr_ssl_fingerprint(const X509 *cert, const EVP_MD *fdig) std::string get_hr_ssl_fingerprint(const X509 *cert, const EVP_MD *fdig)
{ {
unsigned int j; unsigned int j;

View file

@ -135,6 +135,50 @@ the `Expect Response` field zeroed. When a message of this type is received, the
contents can be safely ignored. contents can be safely ignored.
## Encryption
The Levin protocol now has optional SSL encryption. Each peer can have 4 states:
(1) no encryption, (2) autodetect encryption, (3) mandatory encryption with no
certificate verification, or (4) mandatory encryption with certificate
verification.
When a peerlist is shared, the encryption information is ignored (not
trusted), and is put into `autodetect` mode by default. The node data shared
during handshakes OR pings can change the node state to `no encryption` or
`authenticated encryption`, based on direction information from the peer.
> The server/responder must always be in autodetect SSL mode, so that it can
handle any possible state that the client/initiator is in.
> If a node changes certificates, "old" peers will be unable to connect until
the node is completely purged from the white/gray lists OR until the node makes
a direct connection to provide the new encryption state. Incoming connections
from "new" peers (that never saw the old certificate) will still be possible.
### No Encryption
If a peer chooses to have no encryption, it will send `encryption_mode = 1` in
Handshake messages. All clients/initiators can then skip an attempt to connect
via SSL.
### Autodetect Encryption
All peers start in this state, and only move to `no encryption` or
`authenticated encryption` when: (1) the peer is listed on the CLI explicitly
requesting one of the other states, or (2) the peer sends a handshake or ping
message indicating it should be in another state.
When connecting to a peer of this type, the client/initiator should attempt
SSL with no server/responder verification. If the connection fails, it should
immediately attempt a non-SSL connection.
### Authenticated Encryption
If a peer chooses to have a persistent SSL certificate, it will send
`encryption_ver = 2` AND `ssl_finger = <binary sha256 of cert>` in the
Handshake messages.
When connecting to a peer of this type, the client/initiator should attempt
a SSL connection with verification of the SSL fingerprint. The connection
should fail if the fingerprint check fails - it is treated identically to
a invalid/old port number.
## Commands ## Commands
### P2P (Admin) Commands ### P2P (Admin) Commands

View file

@ -147,6 +147,7 @@
#define P2P_DEFAULT_SYNC_SEARCH_CONNECTIONS_COUNT 2 #define P2P_DEFAULT_SYNC_SEARCH_CONNECTIONS_COUNT 2
#define P2P_DEFAULT_LIMIT_RATE_UP 2048 // kB/s #define P2P_DEFAULT_LIMIT_RATE_UP 2048 // kB/s
#define P2P_DEFAULT_LIMIT_RATE_DOWN 8192 // kB/s #define P2P_DEFAULT_LIMIT_RATE_DOWN 8192 // kB/s
#define P2P_MAX_LEVIN_PAD_BYTES 8192
#define P2P_FAILED_ADDR_FORGET_SECONDS (60*60) //1 hour #define P2P_FAILED_ADDR_FORGET_SECONDS (60*60) //1 hour
#define P2P_IP_BLOCKTIME (60*60*24) //24 hour #define P2P_IP_BLOCKTIME (60*60*24) //24 hour

View file

@ -34,6 +34,7 @@
#include "serialization/keyvalue_serialization.h" #include "serialization/keyvalue_serialization.h"
#include "cryptonote_basic/cryptonote_basic.h" #include "cryptonote_basic/cryptonote_basic.h"
#include "cryptonote_basic/blobdatatype.h" #include "cryptonote_basic/blobdatatype.h"
#include "net/net_ssl.h"
namespace cryptonote namespace cryptonote
{ {
@ -49,7 +50,7 @@ namespace cryptonote
bool incoming; bool incoming;
bool localhost; bool localhost;
bool local_ip; bool local_ip;
bool ssl; epee::net_utils::ssl_support_t ssl;
std::string address; std::string address;
std::string host; std::string host;
@ -87,9 +88,11 @@ namespace cryptonote
uint8_t address_type; uint8_t address_type;
BEGIN_KV_SERIALIZE_MAP() BEGIN_KV_SERIALIZE_MAP()
uint8_t ssl_i = uint8_t(ssl);
KV_SERIALIZE(incoming) KV_SERIALIZE(incoming)
KV_SERIALIZE(localhost) KV_SERIALIZE(localhost)
KV_SERIALIZE(local_ip) KV_SERIALIZE(local_ip)
epee::serialization::selector<is_store>::serialize(ssl_i, stg, hparent_section, "ssl");
KV_SERIALIZE(address) KV_SERIALIZE(address)
KV_SERIALIZE(host) KV_SERIALIZE(host)
KV_SERIALIZE(ip) KV_SERIALIZE(ip)
@ -112,6 +115,7 @@ namespace cryptonote
KV_SERIALIZE(height) KV_SERIALIZE(height)
KV_SERIALIZE(pruning_seed) KV_SERIALIZE(pruning_seed)
KV_SERIALIZE(address_type) KV_SERIALIZE(address_type)
ssl = epee::net_utils::ssl_support_t(ssl_i);
END_KV_SERIALIZE_MAP() END_KV_SERIALIZE_MAP()
}; };

View file

@ -159,40 +159,12 @@ namespace levin
return get_out_connections(p2p, get_blockchain_height(p2p, core)); return get_out_connections(p2p, get_blockchain_height(p2p, core));
} }
epee::levin::message_writer make_tx_message(std::vector<blobdata>&& txs, const bool pad, const bool fluff) epee::levin::message_writer make_tx_message(std::vector<blobdata>&& txs, const bool fluff)
{ {
NOTIFY_NEW_TRANSACTIONS::request request{}; NOTIFY_NEW_TRANSACTIONS::request request{};
request.txs = std::move(txs); request.txs = std::move(txs);
request.dandelionpp_fluff = fluff; request.dandelionpp_fluff = fluff;
if (pad)
{
size_t bytes = 9 /* header */ + 4 /* 1 + 'txs' */ + tools::get_varint_data(request.txs.size()).size();
for(auto tx_blob_it = request.txs.begin(); tx_blob_it!=request.txs.end(); ++tx_blob_it)
bytes += tools::get_varint_data(tx_blob_it->size()).size() + tx_blob_it->size();
// stuff some dummy bytes in to stay safe from traffic volume analysis
static constexpr const size_t granularity = 1024;
size_t padding = granularity - bytes % granularity;
const size_t overhead = 2 /* 1 + '_' */ + tools::get_varint_data(padding).size();
if (overhead > padding)
padding = 0;
else
padding -= overhead;
request._ = std::string(padding, ' ');
epee::byte_slice arg_buff;
epee::serialization::store_t_to_binary(request, arg_buff);
// we probably lowballed the payload size a bit, so added a but too much. Fix this now.
size_t remove = arg_buff.size() % granularity;
if (remove > request._.size())
request._.clear();
else
request._.resize(request._.size() - remove);
// if the size of _ moved enough, we might lose byte in size encoding, we don't care
}
epee::levin::message_writer out; epee::levin::message_writer out;
if (!epee::serialization::store_t_to_binary(request, out.buffer)) if (!epee::serialization::store_t_to_binary(request, out.buffer))
throw std::runtime_error{"Failed to serialize to epee binary format"}; throw std::runtime_error{"Failed to serialize to epee binary format"};
@ -202,7 +174,7 @@ namespace levin
bool make_payload_send_txs(connections& p2p, std::vector<blobdata>&& txs, const boost::uuids::uuid& destination, const bool pad, const bool fluff) bool make_payload_send_txs(connections& p2p, std::vector<blobdata>&& txs, const boost::uuids::uuid& destination, const bool pad, const bool fluff)
{ {
epee::byte_slice blob = make_tx_message(std::move(txs), pad, fluff).finalize_notify(NOTIFY_NEW_TRANSACTIONS::ID); epee::byte_slice blob = make_tx_message(std::move(txs), fluff).finalize_notify(NOTIFY_NEW_TRANSACTIONS::ID, pad);
return p2p.send(std::move(blob), destination); return p2p.send(std::move(blob), destination);
} }
@ -847,7 +819,7 @@ namespace levin
// Padding is not useful when using noise mode. Send as stem so receiver // Padding is not useful when using noise mode. Send as stem so receiver
// forwards in Dandelion++ mode. // forwards in Dandelion++ mode.
epee::byte_slice message = epee::levin::make_fragmented_notify( epee::byte_slice message = epee::levin::make_fragmented_notify(
zone_->noise.size(), NOTIFY_NEW_TRANSACTIONS::ID, make_tx_message(std::move(txs), false, false) zone_->noise.size(), NOTIFY_NEW_TRANSACTIONS::ID, make_tx_message(std::move(txs), false)
); );
if (CRYPTONOTE_MAX_FRAGMENTS * zone_->noise.size() < message.size()) if (CRYPTONOTE_MAX_FRAGMENTS * zone_->noise.size() < message.size())
{ {

View file

@ -653,7 +653,7 @@ bool t_rpc_command_executor::print_connections() {
tools::msg_writer() << std::setw(host_field_width) << std::left << "Remote Host" tools::msg_writer() << std::setw(host_field_width) << std::left << "Remote Host"
<< std::setw(8) << "Type" << std::setw(8) << "Type"
<< std::setw(6) << "SSL" << std::setw(13) << "SSL"
<< std::setw(20) << "Peer id" << std::setw(20) << "Peer id"
<< std::setw(20) << "Support Flags" << std::setw(20) << "Support Flags"
<< std::setw(30) << "Recv/Sent (inactive,sec)" << std::setw(30) << "Recv/Sent (inactive,sec)"
@ -664,6 +664,20 @@ bool t_rpc_command_executor::print_connections() {
<< std::setw(10) << "Up (kB/s)" << std::setw(10) << "Up (kB/s)"
<< std::setw(13) << "Up(now)" << std::setw(13) << "Up(now)"
<< std::endl; << std::endl;
const auto ssl_string = [] (const epee::net_utils::ssl_support_t ssl) -> const char*
{
switch (ssl)
{
default:
case epee::net_utils::ssl_support_t::e_ssl_support_disabled:
break;
case epee::net_utils::ssl_support_t::e_ssl_support_autodetect:
return "autodetect";
case epee::net_utils::ssl_support_t::e_ssl_support_enabled:
return "verified";
}
return "no";
};
for (auto & info : res.connections) for (auto & info : res.connections)
{ {
@ -674,7 +688,7 @@ bool t_rpc_command_executor::print_connections() {
//<< std::setw(30) << std::left << in_out //<< std::setw(30) << std::left << in_out
<< std::setw(host_field_width) << std::left << address << std::setw(host_field_width) << std::left << address
<< std::setw(8) << (get_address_type_name((epee::net_utils::address_type)info.address_type)) << std::setw(8) << (get_address_type_name((epee::net_utils::address_type)info.address_type))
<< std::setw(6) << (info.ssl ? "yes" : "no") << std::setw(13) << ssl_string(info.ssl)
<< std::setw(20) << info.peer_id << std::setw(20) << info.peer_id
<< std::setw(20) << info.support_flags << std::setw(20) << info.support_flags
<< std::setw(30) << std::to_string(info.recv_count) + "(" + std::to_string(info.recv_idle_time) + ")/" + std::to_string(info.send_count) + "(" + std::to_string(info.send_idle_time) + ")" << std::setw(30) << std::to_string(info.recv_count) + "(" + std::to_string(info.recv_idle_time) + ")/" + std::to_string(info.send_count) + "(" + std::to_string(info.send_idle_time) + ")"

View file

@ -142,17 +142,23 @@ namespace nodetool
const command_line::arg_descriptor<uint32_t> arg_p2p_external_port = {"p2p-external-port", "External port for p2p network protocol (if port forwarding used with NAT)", 0}; const command_line::arg_descriptor<uint32_t> arg_p2p_external_port = {"p2p-external-port", "External port for p2p network protocol (if port forwarding used with NAT)", 0};
const command_line::arg_descriptor<bool> arg_p2p_allow_local_ip = {"allow-local-ip", "Allow local ip add to peer list, mostly in debug purposes"}; const command_line::arg_descriptor<bool> arg_p2p_allow_local_ip = {"allow-local-ip", "Allow local ip add to peer list, mostly in debug purposes"};
const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_add_peer = {"add-peer", "Manually add peer to local peerlist"}; const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_add_peer = {"add-peer", "Manually add peer to local peerlist. Append \",sha-256 fingerprint\" to make authenticated connection."
const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_add_priority_node = {"add-priority-node", "Specify list of peers to connect to and attempt to keep the connection open"}; "Append \",no_encryption\" to disable SSL autodetect."};
const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_add_exclusive_node = {"add-exclusive-node", "Specify list of peers to connect to only." const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_add_priority_node = {"add-priority-node", "Specify list of peers to connect to and attempt to keep the connection open. "
" If this option is given the options add-priority-node and seed-node are ignored"}; "Append \",sha-256 fingerprint\" to make authenticated connection. Append \",no_encryption\" to disable SSL autodetect."};
const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_seed_node = {"seed-node", "Connect to a node to retrieve peer addresses, and disconnect"}; const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_add_exclusive_node = {"add-exclusive-node", "Specify list of peers to connect to only. "
" If this option is given the options add-priority-node and seed-node are ignored. "
"Append \",sha-256 fingerprint\" to make authenticated connection. Append \",no_encryption\" to disable SSL autodetect."};
const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_seed_node = {"seed-node", "Connect to a node to retrieve peer addresses, and disconnect. "
"Append \",sha-256 fingerprint\" to make authenticated connection. Append \",no_encryption\" to disable SSL autodetect."};
const command_line::arg_descriptor<std::vector<std::string> > arg_tx_proxy = {"tx-proxy", "Send local txes through proxy: <network-type>,<socks-ip:port>[,max_connections][,disable_noise] i.e. \"tor,127.0.0.1:9050,100,disable_noise\""}; const command_line::arg_descriptor<std::vector<std::string> > arg_tx_proxy = {"tx-proxy", "Send local txes through proxy: <network-type>,<socks-ip:port>[,max_connections][,disable_noise] i.e. \"tor,127.0.0.1:9050,100,disable_noise\""};
const command_line::arg_descriptor<std::vector<std::string> > arg_anonymous_inbound = {"anonymous-inbound", "<hidden-service-address>,<[bind-ip:]port>[,max_connections] i.e. \"x.onion,127.0.0.1:18083,100\""}; const command_line::arg_descriptor<std::vector<std::string> > arg_anonymous_inbound = {"anonymous-inbound", "<hidden-service-address>,<[bind-ip:]port>[,max_connections] i.e. \"x.onion,127.0.0.1:18083,100\""};
const command_line::arg_descriptor<std::string> arg_ban_list = {"ban-list", "Specify ban list file, one IP address per line"}; const command_line::arg_descriptor<std::string> arg_ban_list = {"ban-list", "Specify ban list file, one IP address per line"};
const command_line::arg_descriptor<bool> arg_p2p_hide_my_port = {"hide-my-port", "Do not announce yourself as peerlist candidate", false, true}; const command_line::arg_descriptor<bool> arg_p2p_hide_my_port = {"hide-my-port", "Do not announce yourself as peerlist candidate", false, true};
const command_line::arg_descriptor<bool> arg_no_sync = {"no-sync", "Don't synchronize the blockchain with other peers", false}; const command_line::arg_descriptor<bool> arg_no_sync = {"no-sync", "Don't synchronize the blockchain with other peers", false};
const command_line::arg_descriptor<bool> arg_enable_dns_blocklist = {"enable-dns-blocklist", "Apply realtime blocklist from DNS", false}; const command_line::arg_descriptor<bool> arg_enable_dns_blocklist = {"enable-dns-blocklist", "Apply realtime blocklist from DNS", false};
const command_line::arg_descriptor<bool> arg_p2p_disable_encryption = {"p2p-disable-encryption", "Disable all P2P encryption", false};
const command_line::arg_descriptor<bool> arg_p2p_persistent_cert = {"p2p-persistent-cert", "Persist p2p SSL certificate across monerod runs to authenticate/identify server", false};
const command_line::arg_descriptor<bool> arg_no_igd = {"no-igd", "Disable UPnP port mapping"}; const command_line::arg_descriptor<bool> arg_no_igd = {"no-igd", "Disable UPnP port mapping"};
const command_line::arg_descriptor<std::string> arg_igd = {"igd", "UPnP port mapping (disabled, enabled, delayed)", "delayed"}; const command_line::arg_descriptor<std::string> arg_igd = {"igd", "UPnP port mapping (disabled, enabled, delayed)", "delayed"};
@ -167,8 +173,10 @@ namespace nodetool
const command_line::arg_descriptor<int64_t> arg_limit_rate = {"limit-rate", "set limit-rate [kB/s]", -1}; const command_line::arg_descriptor<int64_t> arg_limit_rate = {"limit-rate", "set limit-rate [kB/s]", -1};
const command_line::arg_descriptor<bool> arg_pad_transactions = { const command_line::arg_descriptor<bool> arg_pad_transactions = {
"pad-transactions", "Pad relayed transactions to help defend against traffic volume analysis", false "pad-transactions", "Pad relayed transactions to help defend against traffic volume analysis. Always enabled on SSL, Tor, and I2P connections.", false
}; };
const command_line::arg_descriptor<uint32_t> arg_max_connections_per_ip = {"max-connections-per-ip", "Maximum number of connections allowed from the same IP address", 1}; const command_line::arg_descriptor<uint32_t> arg_max_connections_per_ip = {"max-connections-per-ip", "Maximum number of connections allowed from the same IP address", 1};
boost::optional<std::vector<proxy>> get_proxies(boost::program_options::variables_map const& vm) boost::optional<std::vector<proxy>> get_proxies(boost::program_options::variables_map const& vm)

View file

@ -110,17 +110,58 @@ namespace nodetool
struct p2p_connection_context_t: base_type //t_payload_net_handler::connection_context //public net_utils::connection_context_base struct p2p_connection_context_t: base_type //t_payload_net_handler::connection_context //public net_utils::connection_context_base
{ {
p2p_connection_context_t() p2p_connection_context_t()
: peer_id(0), : cert_finger(),
peer_id(0),
support_flags(0), support_flags(0),
encryption_mode(emode_ssl_autodetect),
m_in_timedsync(false) m_in_timedsync(false)
{} {}
std::string cert_finger;
peerid_type peer_id; peerid_type peer_id;
uint32_t support_flags; uint32_t support_flags;
uint8_t encryption_mode;
bool m_in_timedsync; bool m_in_timedsync;
std::set<epee::net_utils::network_address> sent_addresses; std::set<epee::net_utils::network_address> sent_addresses;
}; };
struct p2p_address
{
epee::net_utils::network_address na;
epee::net_utils::ssl_options_t ssl = epee::net_utils::ssl_support_t::e_ssl_support_autodetect;
template<typename T>
void update_peer(T& peer) const
{
peer.adr = na;
peer.encryption_mode = emode_ssl_autodetect;
if (!ssl)
peer.encryption_mode = emode_disabled;
if (!ssl.fingerprints().empty())
{
peer.encryption_mode = emode_ssl_enabled;
auto& fingerprint = ssl.fingerprints().front();
peer.cert_finger.resize(fingerprint.size());
std::copy(fingerprint.begin(), fingerprint.end(), peer.cert_finger.begin());
}
}
bool operator==(const p2p_address& rhs) const
{ return na == rhs.na; }
};
struct string_address
{
std::string address;
epee::net_utils::ssl_options_t ssl;
bool operator<(const string_address& rhs) const noexcept
{ return address < rhs.address; }
bool operator==(const string_address& rhs) const noexcept
{ return address == rhs.address; }
};
template<class t_payload_net_handler> template<class t_payload_net_handler>
class node_server: public epee::levin::levin_commands_handler<p2p_connection_context_t<typename t_payload_net_handler::connection_context> >, class node_server: public epee::levin::levin_commands_handler<p2p_connection_context_t<typename t_payload_net_handler::connection_context> >,
public i_p2p_endpoint<typename t_payload_net_handler::connection_context>, public i_p2p_endpoint<typename t_payload_net_handler::connection_context>,
@ -139,7 +180,7 @@ namespace nodetool
typedef epee::net_utils::boosted_tcp_server<epee::levin::async_protocol_handler<p2p_connection_context>> net_server; typedef epee::net_utils::boosted_tcp_server<epee::levin::async_protocol_handler<p2p_connection_context>> net_server;
struct network_zone; struct network_zone;
using connect_func = boost::optional<p2p_connection_context>(network_zone&, epee::net_utils::network_address const&, epee::net_utils::ssl_support_t); using connect_func = boost::optional<p2p_connection_context>(network_zone&, p2p_address const&);
struct config_t struct config_t
{ {
@ -165,6 +206,7 @@ namespace nodetool
m_bind_ipv6_address(), m_bind_ipv6_address(),
m_port(), m_port(),
m_port_ipv6(), m_port_ipv6(),
m_ssl_finger(),
m_notifier(), m_notifier(),
m_our_address(), m_our_address(),
m_peerlist(), m_peerlist(),
@ -172,6 +214,7 @@ namespace nodetool
m_proxy_address(), m_proxy_address(),
m_current_number_of_out_peers(0), m_current_number_of_out_peers(0),
m_current_number_of_in_peers(0), m_current_number_of_in_peers(0),
m_ssl_support(epee::net_utils::ssl_support_t::e_ssl_support_disabled),
m_seed_nodes_lock(), m_seed_nodes_lock(),
m_can_pingback(false), m_can_pingback(false),
m_seed_nodes_initialized(false) m_seed_nodes_initialized(false)
@ -187,6 +230,7 @@ namespace nodetool
m_bind_ipv6_address(), m_bind_ipv6_address(),
m_port(), m_port(),
m_port_ipv6(), m_port_ipv6(),
m_ssl_finger(),
m_notifier(), m_notifier(),
m_our_address(), m_our_address(),
m_peerlist(), m_peerlist(),
@ -194,6 +238,7 @@ namespace nodetool
m_proxy_address(), m_proxy_address(),
m_current_number_of_out_peers(0), m_current_number_of_out_peers(0),
m_current_number_of_in_peers(0), m_current_number_of_in_peers(0),
m_ssl_support(epee::net_utils::ssl_support_t::e_ssl_support_disabled),
m_seed_nodes_lock(), m_seed_nodes_lock(),
m_can_pingback(false), m_can_pingback(false),
m_seed_nodes_initialized(false) m_seed_nodes_initialized(false)
@ -203,11 +248,12 @@ namespace nodetool
connect_func* m_connect; connect_func* m_connect;
net_server m_net_server; net_server m_net_server;
std::vector<epee::net_utils::network_address> m_seed_nodes; std::vector<p2p_address> m_seed_nodes;
std::string m_bind_ip; std::string m_bind_ip;
std::string m_bind_ipv6_address; std::string m_bind_ipv6_address;
std::string m_port; std::string m_port;
std::string m_port_ipv6; std::string m_port_ipv6;
std::string m_ssl_finger;
cryptonote::levin::notify m_notifier; cryptonote::levin::notify m_notifier;
epee::net_utils::network_address m_our_address; // in anonymity networks epee::net_utils::network_address m_our_address; // in anonymity networks
peerlist_manager m_peerlist; peerlist_manager m_peerlist;
@ -215,6 +261,7 @@ namespace nodetool
boost::asio::ip::tcp::endpoint m_proxy_address; boost::asio::ip::tcp::endpoint m_proxy_address;
std::atomic<unsigned int> m_current_number_of_out_peers; std::atomic<unsigned int> m_current_number_of_out_peers;
std::atomic<unsigned int> m_current_number_of_in_peers; std::atomic<unsigned int> m_current_number_of_in_peers;
epee::net_utils::ssl_support_t m_ssl_support;
boost::shared_mutex m_seed_nodes_lock; boost::shared_mutex m_seed_nodes_lock;
bool m_can_pingback; bool m_can_pingback;
bool m_seed_nodes_initialized; bool m_seed_nodes_initialized;
@ -370,7 +417,7 @@ namespace nodetool
bool make_new_connection_from_anchor_peerlist(const std::vector<anchor_peerlist_entry>& anchor_peerlist); bool make_new_connection_from_anchor_peerlist(const std::vector<anchor_peerlist_entry>& anchor_peerlist);
bool make_new_connection_from_peerlist(network_zone& zone, bool use_white_list); bool make_new_connection_from_peerlist(network_zone& zone, bool use_white_list);
bool try_to_connect_and_handshake_with_new_peer(const epee::net_utils::network_address& na, bool just_take_peerlist = false, uint64_t last_seen_stamp = 0, PeerType peer_type = white, uint64_t first_seen_stamp = 0); bool try_to_connect_and_handshake_with_new_peer(const p2p_address& peer, bool just_take_peerlist = false, uint64_t last_seen_stamp = 0, PeerType peer_type = white, uint64_t first_seen_stamp = 0);
size_t get_random_index_with_fixed_probability(size_t max_index); size_t get_random_index_with_fixed_probability(size_t max_index);
bool is_peer_used(const peerlist_entry& peer); bool is_peer_used(const peerlist_entry& peer);
bool is_peer_used(const anchor_peerlist_entry& peer); bool is_peer_used(const anchor_peerlist_entry& peer);
@ -390,9 +437,9 @@ namespace nodetool
void record_addr_failed(const epee::net_utils::network_address& addr); void record_addr_failed(const epee::net_utils::network_address& addr);
bool is_addr_recently_failed(const epee::net_utils::network_address& addr); bool is_addr_recently_failed(const epee::net_utils::network_address& addr);
bool is_priority_node(const epee::net_utils::network_address& na); bool is_priority_node(const epee::net_utils::network_address& na);
std::set<std::string> get_ip_seed_nodes() const; std::set<string_address> get_ip_seed_nodes() const;
std::set<std::string> get_dns_seed_nodes(); std::set<string_address> get_dns_seed_nodes();
std::set<std::string> get_seed_nodes(epee::net_utils::zone); std::set<string_address> get_seed_nodes(epee::net_utils::zone);
bool connect_to_seed(epee::net_utils::zone); bool connect_to_seed(epee::net_utils::zone);
template <class Container> template <class Container>
@ -415,7 +462,7 @@ namespace nodetool
size_t get_outgoing_connections_count(); size_t get_outgoing_connections_count();
size_t get_outgoing_connections_count(network_zone&); size_t get_outgoing_connections_count(network_zone&);
bool check_connection_and_handshake_with_peer(const epee::net_utils::network_address& na, uint64_t last_seen_stamp); bool check_connection_and_handshake_with_peer(const p2p_address& peer, uint64_t last_seen_stamp);
bool gray_peerlist_housekeeping(); bool gray_peerlist_housekeeping();
bool check_incoming_connections(); bool check_incoming_connections();
@ -474,16 +521,16 @@ namespace nodetool
epee::math_helper::once_a_time_seconds<3600, false> m_incoming_connections_interval; epee::math_helper::once_a_time_seconds<3600, false> m_incoming_connections_interval;
epee::math_helper::once_a_time_seconds<7000> m_dns_blocklist_interval; epee::math_helper::once_a_time_seconds<7000> m_dns_blocklist_interval;
std::list<epee::net_utils::network_address> m_priority_peers; std::list<p2p_address> m_priority_peers;
std::vector<epee::net_utils::network_address> m_exclusive_peers; std::vector<p2p_address> m_exclusive_peers;
std::atomic_flag m_fallback_seed_nodes_added; std::atomic_flag m_fallback_seed_nodes_added;
std::vector<nodetool::peerlist_entry> m_command_line_peers; std::vector<nodetool::peerlist_entry> m_command_line_peers;
uint64_t m_peer_livetime; uint64_t m_peer_livetime;
//keep connections to initiate some interactions //keep connections to initiate some interactions
static boost::optional<p2p_connection_context> public_connect(network_zone&, epee::net_utils::network_address const&, epee::net_utils::ssl_support_t); static boost::optional<p2p_connection_context> public_connect(network_zone&, p2p_address const&);
static boost::optional<p2p_connection_context> socks_connect(network_zone&, epee::net_utils::network_address const&, epee::net_utils::ssl_support_t); static boost::optional<p2p_connection_context> socks_connect(network_zone&, p2p_address const&);
/* A `std::map` provides constant iterators and key/value pointers even with /* A `std::map` provides constant iterators and key/value pointers even with
@ -510,8 +557,6 @@ namespace nodetool
boost::uuids::uuid m_network_id; boost::uuids::uuid m_network_id;
cryptonote::network_type m_nettype; cryptonote::network_type m_nettype;
epee::net_utils::ssl_support_t m_ssl_support;
bool m_enable_dns_seed_nodes; bool m_enable_dns_seed_nodes;
bool m_enable_dns_blocklist; bool m_enable_dns_blocklist;
@ -538,6 +583,8 @@ namespace nodetool
extern const command_line::arg_descriptor<bool> arg_p2p_hide_my_port; extern const command_line::arg_descriptor<bool> arg_p2p_hide_my_port;
extern const command_line::arg_descriptor<bool> arg_no_sync; extern const command_line::arg_descriptor<bool> arg_no_sync;
extern const command_line::arg_descriptor<bool> arg_enable_dns_blocklist; extern const command_line::arg_descriptor<bool> arg_enable_dns_blocklist;
extern const command_line::arg_descriptor<bool> arg_p2p_disable_encryption;
extern const command_line::arg_descriptor<bool> arg_p2p_persistent_cert;
extern const command_line::arg_descriptor<bool> arg_no_igd; extern const command_line::arg_descriptor<bool> arg_no_igd;
extern const command_line::arg_descriptor<std::string> arg_igd; extern const command_line::arg_descriptor<std::string> arg_igd;

View file

@ -50,6 +50,7 @@
#include "common/util.h" #include "common/util.h"
#include "common/dns_utils.h" #include "common/dns_utils.h"
#include "common/pruning.h" #include "common/pruning.h"
#include "hex.h"
#include "net/error.h" #include "net/error.h"
#include "misc_log_ex.h" #include "misc_log_ex.h"
#include "p2p_protocol_defs.h" #include "p2p_protocol_defs.h"
@ -80,6 +81,19 @@ static inline boost::asio::ip::address_v4 make_address_v4_from_v6(const boost::a
namespace nodetool namespace nodetool
{ {
static string_address parse_address(std::string adr)
{
namespace net = epee::net_utils;
const auto pos = adr.find(',');
net::ssl_options_t e2e_mode{net::ssl_support_t::e_ssl_support_autodetect};
if (boost::string_ref{adr}.ends_with("no_encryption"))
e2e_mode = net::ssl_options_t{net::ssl_support_t::e_ssl_support_disabled};
else if (pos != std::string::npos)
e2e_mode = net::ssl_options_t{{epee::from_hex_locale::to_vector(boost::string_ref{adr}.substr(pos + 1))}, {}};
adr.erase(std::min(adr.size(), adr.find(',')));
return {std::move(adr) , std::move(e2e_mode)};
}
template<class t_payload_net_handler> template<class t_payload_net_handler>
node_server<t_payload_net_handler>::~node_server() node_server<t_payload_net_handler>::~node_server()
{ {
@ -94,7 +108,7 @@ namespace nodetool
} }
} }
//----------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------
inline bool append_net_address(std::vector<epee::net_utils::network_address> & seed_nodes, std::string const & addr, uint16_t default_port); inline bool append_net_address(std::vector<p2p_address> & seed_nodes, string_address addr, uint16_t default_port);
//----------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------
template<class t_payload_net_handler> template<class t_payload_net_handler>
void node_server<t_payload_net_handler>::init_options(boost::program_options::options_description& desc) void node_server<t_payload_net_handler>::init_options(boost::program_options::options_description& desc)
@ -117,6 +131,8 @@ namespace nodetool
command_line::add_arg(desc, arg_p2p_hide_my_port); command_line::add_arg(desc, arg_p2p_hide_my_port);
command_line::add_arg(desc, arg_no_sync); command_line::add_arg(desc, arg_no_sync);
command_line::add_arg(desc, arg_enable_dns_blocklist); command_line::add_arg(desc, arg_enable_dns_blocklist);
command_line::add_arg(desc, arg_p2p_disable_encryption);
command_line::add_arg(desc, arg_p2p_persistent_cert);
command_line::add_arg(desc, arg_no_igd); command_line::add_arg(desc, arg_no_igd);
command_line::add_arg(desc, arg_igd); command_line::add_arg(desc, arg_igd);
command_line::add_arg(desc, arg_out_peers); command_line::add_arg(desc, arg_out_peers);
@ -407,7 +423,6 @@ namespace nodetool
{ {
bool testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on); bool testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
bool stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on); bool stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on);
const bool pad_txs = command_line::get_arg(vm, arg_pad_transactions);
m_nettype = testnet ? cryptonote::TESTNET : stagenet ? cryptonote::STAGENET : cryptonote::MAINNET; m_nettype = testnet ? cryptonote::TESTNET : stagenet ? cryptonote::STAGENET : cryptonote::MAINNET;
network_zone& public_zone = m_network_zones[epee::net_utils::zone::public_]; network_zone& public_zone = m_network_zones[epee::net_utils::zone::public_];
@ -451,6 +466,8 @@ namespace nodetool
m_offline = command_line::get_arg(vm, cryptonote::arg_offline); m_offline = command_line::get_arg(vm, cryptonote::arg_offline);
m_use_ipv6 = command_line::get_arg(vm, arg_p2p_use_ipv6); m_use_ipv6 = command_line::get_arg(vm, arg_p2p_use_ipv6);
m_require_ipv4 = !command_line::get_arg(vm, arg_p2p_ignore_ipv4); m_require_ipv4 = !command_line::get_arg(vm, arg_p2p_ignore_ipv4);
const bool pad_txs =
!command_line::get_arg(vm, arg_p2p_disable_encryption) || command_line::get_arg(vm, arg_pad_transactions);
public_zone.m_notifier = cryptonote::levin::notify{ public_zone.m_notifier = cryptonote::levin::notify{
public_zone.m_net_server.get_io_service(), public_zone.m_net_server.get_config_shared(), nullptr, epee::net_utils::zone::public_, pad_txs, m_payload_handler.get_core() public_zone.m_net_server.get_io_service(), public_zone.m_net_server.get_config_shared(), nullptr, epee::net_utils::zone::public_, pad_txs, m_payload_handler.get_core()
}; };
@ -463,11 +480,13 @@ namespace nodetool
nodetool::peerlist_entry pe = AUTO_VAL_INIT(pe); nodetool::peerlist_entry pe = AUTO_VAL_INIT(pe);
pe.id = crypto::rand<uint64_t>(); pe.id = crypto::rand<uint64_t>();
const uint16_t default_port = cryptonote::get_config(m_nettype).P2P_DEFAULT_PORT; const uint16_t default_port = cryptonote::get_config(m_nettype).P2P_DEFAULT_PORT;
expect<epee::net_utils::network_address> adr = net::get_network_address(pr_str, default_port); string_address str_addr = parse_address(pr_str);
expect<epee::net_utils::network_address> adr = net::get_network_address(str_addr.address, default_port);
if (adr) if (adr)
{ {
add_zone(adr->get_zone()); add_zone(adr->get_zone());
pe.adr = std::move(*adr); p2p_address p2p_peer{std::move(*adr), std::move(str_addr.ssl)};
p2p_peer.update_peer(pe);
m_command_line_peers.push_back(std::move(pe)); m_command_line_peers.push_back(std::move(pe));
continue; continue;
} }
@ -475,13 +494,13 @@ namespace nodetool
adr == net::error::unsupported_address, false, "Bad address (\"" << pr_str << "\"): " << adr.error().message() adr == net::error::unsupported_address, false, "Bad address (\"" << pr_str << "\"): " << adr.error().message()
); );
std::vector<epee::net_utils::network_address> resolved_addrs; std::vector<p2p_address> resolved_addrs;
bool r = append_net_address(resolved_addrs, pr_str, default_port); bool r = append_net_address(resolved_addrs, str_addr, default_port);
CHECK_AND_ASSERT_MES(r, false, "Failed to parse or resolve address from string: " << pr_str); CHECK_AND_ASSERT_MES(r, false, "Failed to parse or resolve address from string: " << pr_str);
for (const epee::net_utils::network_address& addr : resolved_addrs) for (const p2p_address& peer : resolved_addrs)
{ {
pe.id = crypto::rand<uint64_t>(); pe.id = crypto::rand<uint64_t>();
pe.adr = addr; peer.update_peer(pe);
m_command_line_peers.push_back(pe); m_command_line_peers.push_back(pe);
} }
} }
@ -605,7 +624,7 @@ namespace nodetool
} }
zone.m_notifier = cryptonote::levin::notify{ zone.m_notifier = cryptonote::levin::notify{
zone.m_net_server.get_io_service(), zone.m_net_server.get_config_shared(), std::move(this_noise), proxy.zone, pad_txs, m_payload_handler.get_core() zone.m_net_server.get_io_service(), zone.m_net_server.get_config_shared(), std::move(this_noise), proxy.zone, true /* pad txs */, m_payload_handler.get_core()
}; };
} }
@ -654,8 +673,8 @@ namespace nodetool
} }
//----------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------
inline bool append_net_address( inline bool append_net_address(
std::vector<epee::net_utils::network_address> & seed_nodes std::vector<p2p_address> & seed_nodes
, std::string const & addr , string_address peer
, uint16_t default_port , uint16_t default_port
) )
{ {
@ -664,7 +683,7 @@ namespace nodetool
// Split addr string into host string and port string // Split addr string into host string and port string
std::string host; std::string host;
std::string port = std::to_string(default_port); std::string port = std::to_string(default_port);
net::get_network_address_host_and_port(addr, host, port); net::get_network_address_host_and_port(peer.address, host, port);
MINFO("Resolving node address: host=" << host << ", port=" << port); MINFO("Resolving node address: host=" << host << ", port=" << port);
io_service io_srv; io_service io_srv;
@ -681,13 +700,13 @@ namespace nodetool
if (endpoint.address().is_v4()) if (endpoint.address().is_v4())
{ {
epee::net_utils::network_address na{epee::net_utils::ipv4_network_address{boost::asio::detail::socket_ops::host_to_network_long(endpoint.address().to_v4().to_ulong()), endpoint.port()}}; epee::net_utils::network_address na{epee::net_utils::ipv4_network_address{boost::asio::detail::socket_ops::host_to_network_long(endpoint.address().to_v4().to_ulong()), endpoint.port()}};
seed_nodes.push_back(na); seed_nodes.push_back(p2p_address{na, peer.ssl});
MINFO("Added node: " << na.str()); MINFO("Added node: " << na.str());
} }
else else
{ {
epee::net_utils::network_address na{epee::net_utils::ipv6_network_address{endpoint.address().to_v6(), endpoint.port()}}; epee::net_utils::network_address na{epee::net_utils::ipv6_network_address{endpoint.address().to_v6(), endpoint.port()}};
seed_nodes.push_back(na); seed_nodes.push_back(p2p_address{na, peer.ssl});
MINFO("Added node: " << na.str()); MINFO("Added node: " << na.str());
} }
} }
@ -696,43 +715,44 @@ namespace nodetool
//----------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------
template<class t_payload_net_handler> template<class t_payload_net_handler>
std::set<std::string> node_server<t_payload_net_handler>::get_ip_seed_nodes() const std::set<string_address> node_server<t_payload_net_handler>::get_ip_seed_nodes() const
{ {
std::set<std::string> full_addrs; const auto autodetect = epee::net_utils::ssl_support_t::e_ssl_support_autodetect;
std::set<string_address> full_addrs;
if (m_nettype == cryptonote::TESTNET) if (m_nettype == cryptonote::TESTNET)
{ {
full_addrs.insert("176.9.0.187:28080"); full_addrs.insert(string_address{"176.9.0.187:28080", autodetect});
full_addrs.insert("51.79.173.165:28080"); full_addrs.insert(string_address{"51.79.173.165:28080", autodetect});
full_addrs.insert("192.99.8.110:28080"); full_addrs.insert(string_address{"192.99.8.110:28080", autodetect});
full_addrs.insert("37.187.74.171:28080"); full_addrs.insert(string_address{"37.187.74.171:28080", autodetect});
full_addrs.insert("77.172.183.193:28080"); full_addrs.insert(string_address{"77.172.183.193:28080", autodetect});
} }
else if (m_nettype == cryptonote::STAGENET) else if (m_nettype == cryptonote::STAGENET)
{ {
full_addrs.insert("176.9.0.187:38080"); full_addrs.insert(string_address{"176.9.0.187:38080", autodetect});
full_addrs.insert("51.79.173.165:38080"); full_addrs.insert(string_address{"51.79.173.165:38080", autodetect});
full_addrs.insert("192.99.8.110:38080"); full_addrs.insert(string_address{"192.99.8.110:38080", autodetect});
full_addrs.insert("37.187.74.171:38080"); full_addrs.insert(string_address{"37.187.74.171:38080", autodetect});
full_addrs.insert("77.172.183.193:38080"); full_addrs.insert(string_address{"77.172.183.193:38080", autodetect});
} }
else if (m_nettype == cryptonote::FAKECHAIN) else if (m_nettype == cryptonote::FAKECHAIN)
{ {
} }
else else
{ {
full_addrs.insert("176.9.0.187:18080"); full_addrs.insert(string_address{"176.9.0.187:18080", autodetect});
full_addrs.insert("88.198.163.90:18080"); full_addrs.insert(string_address{"88.198.163.90:18080", autodetect});
full_addrs.insert("66.85.74.134:18080"); full_addrs.insert(string_address{"66.85.74.134:18080", autodetect});
full_addrs.insert("51.79.173.165:18080"); full_addrs.insert(string_address{"51.79.173.165:18080", autodetect});
full_addrs.insert("192.99.8.110:18080"); full_addrs.insert(string_address{"192.99.8.110:18080", autodetect});
full_addrs.insert("37.187.74.171:18080"); full_addrs.insert(string_address{"37.187.74.171:18080", autodetect});
full_addrs.insert("77.172.183.193:18080"); full_addrs.insert(string_address{"77.172.183.193:18080", autodetect});
} }
return full_addrs; return full_addrs;
} }
//----------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------
template<class t_payload_net_handler> template<class t_payload_net_handler>
std::set<std::string> node_server<t_payload_net_handler>::get_dns_seed_nodes() std::set<string_address> node_server<t_payload_net_handler>::get_dns_seed_nodes()
{ {
if (!m_exclusive_peers.empty() || m_offline) if (!m_exclusive_peers.empty() || m_offline)
{ {
@ -753,7 +773,7 @@ namespace nodetool
return get_ip_seed_nodes(); return get_ip_seed_nodes();
} }
std::set<std::string> full_addrs; std::set<string_address> full_addrs;
// for each hostname in the seed nodes list, attempt to DNS resolve and // for each hostname in the seed nodes list, attempt to DNS resolve and
// add the result addresses as seed nodes // add the result addresses as seed nodes
@ -825,7 +845,7 @@ namespace nodetool
if (result.size()) if (result.size())
{ {
for (const auto& addr_string : result) for (const auto& addr_string : result)
full_addrs.insert(addr_string + ":" + std::to_string(cryptonote::get_config(m_nettype).P2P_DEFAULT_PORT)); full_addrs.insert(string_address{addr_string + ":" + std::to_string(cryptonote::get_config(m_nettype).P2P_DEFAULT_PORT), epee::net_utils::ssl_support_t::e_ssl_support_disabled});
} }
++i; ++i;
} }
@ -847,8 +867,10 @@ namespace nodetool
} }
//----------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------
template<class t_payload_net_handler> template<class t_payload_net_handler>
std::set<std::string> node_server<t_payload_net_handler>::get_seed_nodes(epee::net_utils::zone zone) std::set<string_address> node_server<t_payload_net_handler>::get_seed_nodes(epee::net_utils::zone zone)
{ {
// `.onion` and `.i2p` are already end-to-end encrypted
const auto no_encryption = epee::net_utils::ssl_support_t::e_ssl_support_disabled;
switch (zone) switch (zone)
{ {
case epee::net_utils::zone::public_: case epee::net_utils::zone::public_:
@ -856,23 +878,25 @@ namespace nodetool
case epee::net_utils::zone::tor: case epee::net_utils::zone::tor:
if (m_nettype == cryptonote::MAINNET) if (m_nettype == cryptonote::MAINNET)
{ {
// tor hidden services are already e2e ssl encrypted
return { return {
"zbjkbsxc5munw3qusl7j2hpcmikhqocdf4pqhnhtpzw5nt5jrmofptid.onion:18083", string_address{"zbjkbsxc5munw3qusl7j2hpcmikhqocdf4pqhnhtpzw5nt5jrmofptid.onion:18083", no_encryption},
"qz43zul2x56jexzoqgkx2trzwcfnr6l3hbtfcfx54g4r3eahy3bssjyd.onion:18083", string_address{"qz43zul2x56jexzoqgkx2trzwcfnr6l3hbtfcfx54g4r3eahy3bssjyd.onion:18083", no_encryption},
"plowsof3t5hogddwabaeiyrno25efmzfxyro2vligremt7sxpsclfaid.onion:18083", string_address{"plowsof3t5hogddwabaeiyrno25efmzfxyro2vligremt7sxpsclfaid.onion:18083", no_encryption},
"plowsoffjexmxalw73tkjmf422gq6575fc7vicuu4javzn2ynnte6tyd.onion:18083", string_address{"plowsoffjexmxalw73tkjmf422gq6575fc7vicuu4javzn2ynnte6tyd.onion:18083", no_encryption},
"plowsofe6cleftfmk2raiw5h2x66atrik3nja4bfd3zrfa2hdlgworad.onion:18083", string_address{"plowsofe6cleftfmk2raiw5h2x66atrik3nja4bfd3zrfa2hdlgworad.onion:18083", no_encryption},
"aclc4e2jhhtr44guufbnwk5bzwhaecinax4yip4wr4tjn27sjsfg6zqd.onion:18083", string_address{"aclc4e2jhhtr44guufbnwk5bzwhaecinax4yip4wr4tjn27sjsfg6zqd.onion:18083", no_encryption},
}; };
} }
return {}; return {};
case epee::net_utils::zone::i2p: case epee::net_utils::zone::i2p:
if (m_nettype == cryptonote::MAINNET) if (m_nettype == cryptonote::MAINNET)
{ {
// i2p services are already e2e aes noise encrypted
return { return {
"uqj3aphckqtjsitz7kxx5flqpwjlq5ppr3chazfued7xucv3nheq.b32.i2p", string_address{"uqj3aphckqtjsitz7kxx5flqpwjlq5ppr3chazfued7xucv3nheq.b32.i2p", no_encryption},
"vdmnehdjkpkg57nthgnjfuaqgku673r5bpbqg56ix6fyqoywgqrq.b32.i2p", string_address{"vdmnehdjkpkg57nthgnjfuaqgku673r5bpbqg56ix6fyqoywgqrq.b32.i2p", no_encryption},
"ugnlcdciyhghh2zert7c3kl4biwkirc43ke33jiy5slnd3mv2trq.b32.i2p", string_address{"ugnlcdciyhghh2zert7c3kl4biwkirc43ke33jiy5slnd3mv2trq.b32.i2p", no_encryption}
}; };
} }
return {}; return {};
@ -956,7 +980,6 @@ namespace nodetool
return res; return res;
//try to bind //try to bind
m_ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_disabled;
for (auto& zone : m_network_zones) for (auto& zone : m_network_zones)
{ {
zone.second.m_net_server.get_config_object().set_handler(this); zone.second.m_net_server.get_config_object().set_handler(this);
@ -974,8 +997,39 @@ namespace nodetool
ipv6_port = zone.second.m_port_ipv6; ipv6_port = zone.second.m_port_ipv6;
MINFO("Binding (IPv6) on " << zone.second.m_bind_ipv6_address << ":" << zone.second.m_port_ipv6); MINFO("Binding (IPv6) on " << zone.second.m_bind_ipv6_address << ":" << zone.second.m_port_ipv6);
} }
res = zone.second.m_net_server.init_server(zone.second.m_port, zone.second.m_bind_ip, ipv6_port, ipv6_addr, m_use_ipv6, m_require_ipv4, epee::net_utils::ssl_support_t::e_ssl_support_disabled);
namespace net = epee::net_utils;
bool store_cert = false;
bool share_fingerprint = false;
net::ssl_options_t p2p_ssl{net::ssl_support_t::e_ssl_support_disabled};
const auto key_files = m_config_folder + "/p2p_public";
if (zone.first == epee::net_utils::zone::public_ && !command_line::get_arg(vm, arg_p2p_disable_encryption))
{
p2p_ssl = net::ssl_options_t{net::ssl_support_t::e_ssl_support_autodetect};
if (command_line::get_arg(vm, arg_p2p_persistent_cert))
{
share_fingerprint = true;
p2p_ssl = net::ssl_options_t{net::ssl_support_t::e_ssl_support_enabled};
MINFO("Using persistent SSL certificate for p2p");
if (boost::filesystem::exists(key_files + ".key") && boost::filesystem::exists(key_files + ".crt"))
{
p2p_ssl.auth.private_key_path = key_files + ".key";
p2p_ssl.auth.certificate_path = key_files + ".crt";
}
else
store_cert = true;
}
}
else
MINFO("Generating new temporary SSL certificate for p2p");
zone.second.m_ssl_support = p2p_ssl.support;
res = zone.second.m_net_server.init_server(zone.second.m_port, zone.second.m_bind_ip, ipv6_port, ipv6_addr, m_use_ipv6, m_require_ipv4, std::move(p2p_ssl));
CHECK_AND_ASSERT_MES(res, false, "Failed to bind server"); CHECK_AND_ASSERT_MES(res, false, "Failed to bind server");
if (store_cert)
net::store_ssl_keys(zone.second.m_net_server.get_ssl_context(), key_files);
if (share_fingerprint)
zone.second.m_ssl_finger = net::get_ssl_fingerprint(zone.second.m_net_server.get_ssl_context());
} }
} }
@ -1138,7 +1192,6 @@ namespace nodetool
bool node_server<t_payload_net_handler>::do_handshake_with_peer(peerid_type& pi, p2p_connection_context& context_, bool just_take_peerlist) bool node_server<t_payload_net_handler>::do_handshake_with_peer(peerid_type& pi, p2p_connection_context& context_, bool just_take_peerlist)
{ {
network_zone& zone = m_network_zones.at(context_.m_remote_address.get_zone()); network_zone& zone = m_network_zones.at(context_.m_remote_address.get_zone());
typename COMMAND_HANDSHAKE::request arg; typename COMMAND_HANDSHAKE::request arg;
typename COMMAND_HANDSHAKE::response rsp; typename COMMAND_HANDSHAKE::response rsp;
get_local_node_data(arg.node_data, zone); get_local_node_data(arg.node_data, zone);
@ -1187,6 +1240,8 @@ namespace nodetool
context.m_rpc_port = rsp.node_data.rpc_port; context.m_rpc_port = rsp.node_data.rpc_port;
context.m_rpc_credits_per_hash = rsp.node_data.rpc_credits_per_hash; context.m_rpc_credits_per_hash = rsp.node_data.rpc_credits_per_hash;
context.support_flags = rsp.node_data.support_flags; context.support_flags = rsp.node_data.support_flags;
context.encryption_mode = rsp.node_data.encryption_mode;
context.cert_finger = std::move(rsp.node_data.cert_finger);
const auto azone = context.m_remote_address.get_zone(); const auto azone = context.m_remote_address.get_zone();
network_zone& zone = m_network_zones.at(azone); network_zone& zone = m_network_zones.at(azone);
zone.m_peerlist.set_peer_just_seen(rsp.node_data.peer_id, context.m_remote_address, context.m_pruning_seed, context.m_rpc_port, context.m_rpc_credits_per_hash); zone.m_peerlist.set_peer_just_seen(rsp.node_data.peer_id, context.m_remote_address, context.m_pruning_seed, context.m_rpc_port, context.m_rpc_credits_per_hash);
@ -1363,13 +1418,13 @@ namespace nodetool
} while(0) } while(0)
template<class t_payload_net_handler> template<class t_payload_net_handler>
bool node_server<t_payload_net_handler>::try_to_connect_and_handshake_with_new_peer(const epee::net_utils::network_address& na, bool just_take_peerlist, uint64_t last_seen_stamp, PeerType peer_type, uint64_t first_seen_stamp) bool node_server<t_payload_net_handler>::try_to_connect_and_handshake_with_new_peer(const p2p_address& peer, bool just_take_peerlist, uint64_t last_seen_stamp, PeerType peer_type, uint64_t first_seen_stamp)
{ {
network_zone& zone = m_network_zones.at(na.get_zone()); network_zone& zone = m_network_zones.at(peer.na.get_zone());
if (zone.m_connect == nullptr) // outgoing connections in zone not possible if (zone.m_connect == nullptr) // outgoing connections in zone not possible
return false; return false;
if (zone.m_our_address == na) if (zone.m_our_address == peer.na)
return false; return false;
if (zone.m_current_number_of_out_peers == zone.m_config.m_net_config.max_out_connection_count) // out peers limit if (zone.m_current_number_of_out_peers == zone.m_config.m_net_config.max_out_connection_count) // out peers limit
@ -1384,17 +1439,17 @@ namespace nodetool
} }
MDEBUG("Connecting to " << na.str() << "(peer_type=" << peer_type << ", last_seen: " MDEBUG("Connecting to " << peer.na.str() << "(peer_type=" << peer_type << ", last_seen: "
<< (last_seen_stamp ? epee::misc_utils::get_time_interval_string(time(NULL) - last_seen_stamp):"never") << (last_seen_stamp ? epee::misc_utils::get_time_interval_string(time(NULL) - last_seen_stamp):"never")
<< ")..."); << ")...");
auto con = zone.m_connect(zone, na, m_ssl_support); auto con = zone.m_connect(zone, peer);
if(!con) if(!con)
{ {
bool is_priority = is_priority_node(na); bool is_priority = is_priority_node(peer.na);
LOG_PRINT_CC_PRIORITY_NODE(is_priority, bool(con), "Connect failed to " << na.str() LOG_PRINT_CC_PRIORITY_NODE(is_priority, bool(con), "Connect failed to " << peer.na.str()
/*<< ", try " << try_count*/); /*<< ", try " << try_count*/);
record_addr_failed(na); record_addr_failed(peer.na);
return false; return false;
} }
@ -1404,11 +1459,11 @@ namespace nodetool
if(!res) if(!res)
{ {
bool is_priority = is_priority_node(na); bool is_priority = is_priority_node(peer.na);
LOG_PRINT_CC_PRIORITY_NODE(is_priority, *con, "Failed to HANDSHAKE with peer " LOG_PRINT_CC_PRIORITY_NODE(is_priority, *con, "Failed to HANDSHAKE with peer "
<< na.str() << peer.na.str()
/*<< ", try " << try_count*/); /*<< ", try " << try_count*/);
record_addr_failed(na); record_addr_failed(peer.na);
return false; return false;
} }
@ -1420,7 +1475,7 @@ namespace nodetool
} }
peerlist_entry pe_local = AUTO_VAL_INIT(pe_local); peerlist_entry pe_local = AUTO_VAL_INIT(pe_local);
pe_local.adr = na; pe_local.adr = peer.na;
pe_local.id = pi; pe_local.id = pi;
time_t last_seen; time_t last_seen;
time(&last_seen); time(&last_seen);
@ -1428,13 +1483,17 @@ namespace nodetool
pe_local.pruning_seed = con->m_pruning_seed; pe_local.pruning_seed = con->m_pruning_seed;
pe_local.rpc_port = con->m_rpc_port; pe_local.rpc_port = con->m_rpc_port;
pe_local.rpc_credits_per_hash = con->m_rpc_credits_per_hash; pe_local.rpc_credits_per_hash = con->m_rpc_credits_per_hash;
pe_local.encryption_mode = con->encryption_mode;
pe_local.cert_finger = con->cert_finger;
zone.m_peerlist.append_with_peer_white(pe_local); zone.m_peerlist.append_with_peer_white(pe_local);
//update last seen and push it to peerlist manager //update last seen and push it to peerlist manager
anchor_peerlist_entry ape = AUTO_VAL_INIT(ape); anchor_peerlist_entry ape = AUTO_VAL_INIT(ape);
ape.adr = na; ape.adr = peer.na;
ape.id = pi; ape.id = pi;
ape.first_seen = first_seen_stamp ? first_seen_stamp : time(nullptr); ape.first_seen = first_seen_stamp ? first_seen_stamp : time(nullptr);
ape.encryption_mode = con->encryption_mode;
ape.cert_finger = con->cert_finger;
zone.m_peerlist.append_with_peer_anchor(ape); zone.m_peerlist.append_with_peer_anchor(ape);
zone.m_notifier.on_handshake_complete(con->m_connection_id, con->m_is_income); zone.m_notifier.on_handshake_complete(con->m_connection_id, con->m_is_income);
@ -1445,22 +1504,22 @@ namespace nodetool
} }
template<class t_payload_net_handler> template<class t_payload_net_handler>
bool node_server<t_payload_net_handler>::check_connection_and_handshake_with_peer(const epee::net_utils::network_address& na, uint64_t last_seen_stamp) bool node_server<t_payload_net_handler>::check_connection_and_handshake_with_peer(const p2p_address& peer, uint64_t last_seen_stamp)
{ {
network_zone& zone = m_network_zones.at(na.get_zone()); network_zone& zone = m_network_zones.at(peer.na.get_zone());
if (zone.m_connect == nullptr) if (zone.m_connect == nullptr)
return false; return false;
LOG_PRINT_L1("Connecting to " << na.str() << "(last_seen: " LOG_PRINT_L1("Connecting to " << peer.na.str() << "(last_seen: "
<< (last_seen_stamp ? epee::misc_utils::get_time_interval_string(time(NULL) - last_seen_stamp):"never") << (last_seen_stamp ? epee::misc_utils::get_time_interval_string(time(NULL) - last_seen_stamp):"never")
<< ")..."); << ")...");
auto con = zone.m_connect(zone, na, m_ssl_support); auto con = zone.m_connect(zone, peer);
if (!con) { if (!con) {
bool is_priority = is_priority_node(na); bool is_priority = is_priority_node(peer.na);
LOG_PRINT_CC_PRIORITY_NODE(is_priority, p2p_connection_context{}, "Connect failed to " << na.str()); LOG_PRINT_CC_PRIORITY_NODE(is_priority, p2p_connection_context{}, "Connect failed to " << peer.na.str());
record_addr_failed(na); record_addr_failed(peer.na);
return false; return false;
} }
@ -1469,10 +1528,10 @@ namespace nodetool
peerid_type pi = AUTO_VAL_INIT(pi); peerid_type pi = AUTO_VAL_INIT(pi);
const bool res = do_handshake_with_peer(pi, *con, true); const bool res = do_handshake_with_peer(pi, *con, true);
if (!res) { if (!res) {
bool is_priority = is_priority_node(na); bool is_priority = is_priority_node(peer.na);
LOG_PRINT_CC_PRIORITY_NODE(is_priority, *con, "Failed to HANDSHAKE with peer " << na.str()); LOG_PRINT_CC_PRIORITY_NODE(is_priority, *con, "Failed to HANDSHAKE with peer " << peer.na.str());
record_addr_failed(na); record_addr_failed(peer.na);
return false; return false;
} }
@ -1530,7 +1589,7 @@ namespace nodetool
<< "[peer_type=" << anchor << "[peer_type=" << anchor
<< "] first_seen: " << epee::misc_utils::get_time_interval_string(time(NULL) - pe.first_seen)); << "] first_seen: " << epee::misc_utils::get_time_interval_string(time(NULL) - pe.first_seen));
if(!try_to_connect_and_handshake_with_new_peer(pe.adr, false, 0, anchor, pe.first_seen)) { if(!try_to_connect_and_handshake_with_new_peer({pe.adr, get_p2p_encryption(pe)}, false, 0, anchor, pe.first_seen)) {
_note("Handshake failed"); _note("Handshake failed");
continue; continue;
} }
@ -1719,7 +1778,7 @@ namespace nodetool
<< "[peer_list=" << (use_white_list ? white : gray) << "[peer_list=" << (use_white_list ? white : gray)
<< "] last_seen: " << (pe.last_seen ? epee::misc_utils::get_time_interval_string(time(NULL) - pe.last_seen) : "never")); << "] last_seen: " << (pe.last_seen ? epee::misc_utils::get_time_interval_string(time(NULL) - pe.last_seen) : "never"));
if(!try_to_connect_and_handshake_with_new_peer(pe.adr, false, pe.last_seen, use_white_list ? white : gray)) { if(!try_to_connect_and_handshake_with_new_peer({pe.adr, get_p2p_encryption(pe)}, false, pe.last_seen, use_white_list ? white : gray)) {
_note("Handshake failed"); _note("Handshake failed");
continue; continue;
} }
@ -1743,8 +1802,8 @@ namespace nodetool
for (const auto& full_addr : get_seed_nodes(zone)) for (const auto& full_addr : get_seed_nodes(zone))
{ {
// seeds should have hostname converted to IP already // seeds should have hostname converted to IP already
MDEBUG("Seed node: " << full_addr); MDEBUG("Seed node: " << full_addr.address);
server.m_seed_nodes.push_back(MONERO_UNWRAP(net::get_network_address(full_addr, default_port))); server.m_seed_nodes.push_back(p2p_address{MONERO_UNWRAP(net::get_network_address(full_addr.address, default_port)), full_addr.ssl});
} }
MDEBUG("Number of seed nodes: " << server.m_seed_nodes.size()); MDEBUG("Number of seed nodes: " << server.m_seed_nodes.size());
} }
@ -1761,7 +1820,7 @@ namespace nodetool
return false; return false;
peerlist_entry pe_seed{}; peerlist_entry pe_seed{};
pe_seed.adr = server.m_seed_nodes[current_index]; pe_seed.adr = server.m_seed_nodes[current_index].na;
if (is_peer_used(pe_seed)) if (is_peer_used(pe_seed))
is_connected_to_at_least_one_seed_node = true; is_connected_to_at_least_one_seed_node = true;
else if (try_to_connect_and_handshake_with_new_peer(server.m_seed_nodes[current_index], true)) else if (try_to_connect_and_handshake_with_new_peer(server.m_seed_nodes[current_index], true))
@ -1778,7 +1837,7 @@ namespace nodetool
for (const auto &peer: get_ip_seed_nodes()) for (const auto &peer: get_ip_seed_nodes())
{ {
MDEBUG("Fallback seed node: " << peer); MDEBUG("Fallback seed node: " << peer.address);
append_net_address(server.m_seed_nodes, peer, cryptonote::get_config(m_nettype).P2P_DEFAULT_PORT); append_net_address(server.m_seed_nodes, peer, cryptonote::get_config(m_nettype).P2P_DEFAULT_PORT);
} }
} }
@ -2206,6 +2265,8 @@ namespace nodetool
node_data.rpc_credits_per_hash = zone.m_can_pingback ? m_rpc_credits_per_hash : 0; node_data.rpc_credits_per_hash = zone.m_can_pingback ? m_rpc_credits_per_hash : 0;
node_data.network_id = m_network_id; node_data.network_id = m_network_id;
node_data.support_flags = zone.m_config.m_support_flags; node_data.support_flags = zone.m_config.m_support_flags;
node_data.encryption_mode = get_encryption_mode(zone.m_ssl_support);
node_data.cert_finger = zone.m_ssl_finger;
return true; return true;
} }
//----------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------
@ -2225,7 +2286,14 @@ namespace nodetool
template<class t_payload_net_handler> template<class t_payload_net_handler>
bool node_server<t_payload_net_handler>::relay_notify_to_list(int command, epee::levin::message_writer data_buff, std::vector<std::pair<epee::net_utils::zone, boost::uuids::uuid>> connections) bool node_server<t_payload_net_handler>::relay_notify_to_list(int command, epee::levin::message_writer data_buff, std::vector<std::pair<epee::net_utils::zone, boost::uuids::uuid>> connections)
{ {
epee::byte_slice message = data_buff.finalize_notify(command); /* This is typically used for relaying blocks, and conveniently uses the
same ref-counted buffer for every connection. Unfortunately, this means
every connection is sent a message of identical size, which should be
identifiable by a ISP/level spy. The alternatives are less efficient (a
uniquely padded message foreach link). Defending against this type of
analysis is likely always complicated, as each message type will have
unique */
epee::byte_slice message = data_buff.finalize_notify(command, false);
std::sort(connections.begin(), connections.end()); std::sort(connections.begin(), connections.end());
auto zone = m_network_zones.begin(); auto zone = m_network_zones.begin();
for(const auto& c_id: connections) for(const auto& c_id: connections)
@ -2317,7 +2385,7 @@ namespace nodetool
return false; return false;
network_zone& zone = m_network_zones.at(context.m_remote_address.get_zone()); network_zone& zone = m_network_zones.at(context.m_remote_address.get_zone());
int res = zone.m_net_server.get_config_object().send(message.finalize_notify(command), context.m_connection_id); int res = zone.m_net_server.get_config_object().send(message.finalize_notify(command, context.should_pad()), context.m_connection_id);
return res > 0; return res > 0;
} }
//----------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------
@ -2371,6 +2439,7 @@ namespace nodetool
{ {
address = epee::net_utils::network_address{epee::net_utils::ipv6_network_address(ipv6_addr, node_data.my_port)}; address = epee::net_utils::network_address{epee::net_utils::ipv6_network_address(ipv6_addr, node_data.my_port)};
} }
peerid_type pr = node_data.peer_id; peerid_type pr = node_data.peer_id;
bool r = zone.m_net_server.connect_async(ip, port, zone.m_config.m_net_config.ping_connection_timeout, [cb, /*context,*/ address, pr, this]( bool r = zone.m_net_server.connect_async(ip, port, zone.m_config.m_net_config.ping_connection_timeout, [cb, /*context,*/ address, pr, this](
const typename net_server::t_connection_context& ping_context, const typename net_server::t_connection_context& ping_context,
@ -2421,7 +2490,7 @@ namespace nodetool
return false; return false;
} }
return true; return true;
}, "0.0.0.0", m_ssl_support); }, "0.0.0.0", get_p2p_encryption(node_data));
if(!r) if(!r)
{ {
LOG_WARNING_CC(context, "Failed to call connect_async, network error."); LOG_WARNING_CC(context, "Failed to call connect_async, network error.");
@ -2498,7 +2567,7 @@ namespace nodetool
real peer with that identity banned/blacklisted. */ real peer with that identity banned/blacklisted. */
if(outgoing_to_same_zone) if(outgoing_to_same_zone)
rsp.local_peerlist_new.push_back(peerlist_entry{zone.m_our_address, zone.m_config.m_peer_id, std::time(nullptr)}); rsp.local_peerlist_new.push_back(peerlist_entry{zone.m_our_address, {}, zone.m_config.m_peer_id, std::time(nullptr)});
LOG_DEBUG_CC(context, "COMMAND_TIMED_SYNC"); LOG_DEBUG_CC(context, "COMMAND_TIMED_SYNC");
return 1; return 1;
@ -2578,7 +2647,7 @@ namespace nodetool
peerid_type peer_id_l = arg.node_data.peer_id; peerid_type peer_id_l = arg.node_data.peer_id;
uint32_t port_l = arg.node_data.my_port; uint32_t port_l = arg.node_data.my_port;
//try ping to be sure that we can add this peer to peer_list //try ping to be sure that we can add this peer to peer_list
try_ping(arg.node_data, context, [peer_id_l, port_l, context, this]() try_ping(arg.node_data, context, [arg, peer_id_l, port_l, context, this]()
{ {
CHECK_AND_ASSERT_MES((context.m_remote_address.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id() || context.m_remote_address.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id()), void(), CHECK_AND_ASSERT_MES((context.m_remote_address.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id() || context.m_remote_address.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id()), void(),
"Only IPv4 or IPv6 addresses are supported here"); "Only IPv4 or IPv6 addresses are supported here");
@ -2600,6 +2669,8 @@ namespace nodetool
pe.pruning_seed = context.m_pruning_seed; pe.pruning_seed = context.m_pruning_seed;
pe.rpc_port = context.m_rpc_port; pe.rpc_port = context.m_rpc_port;
pe.rpc_credits_per_hash = context.m_rpc_credits_per_hash; pe.rpc_credits_per_hash = context.m_rpc_credits_per_hash;
pe.encryption_mode = arg.node_data.encryption_mode;
pe.cert_finger = arg.node_data.cert_finger;
this->m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.append_with_peer_white(pe); this->m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.append_with_peer_white(pe);
LOG_DEBUG_CC(context, "PING SUCCESS " << context.m_remote_address.host_str() << ":" << port_l); LOG_DEBUG_CC(context, "PING SUCCESS " << context.m_remote_address.host_str() << ":" << port_l);
}); });
@ -2696,22 +2767,22 @@ namespace nodetool
template<class t_payload_net_handler> template<class t_payload_net_handler>
bool node_server<t_payload_net_handler>::is_priority_node(const epee::net_utils::network_address& na) bool node_server<t_payload_net_handler>::is_priority_node(const epee::net_utils::network_address& na)
{ {
return (std::find(m_priority_peers.begin(), m_priority_peers.end(), na) != m_priority_peers.end()) || (std::find(m_exclusive_peers.begin(), m_exclusive_peers.end(), na) != m_exclusive_peers.end()); return (std::find(m_priority_peers.begin(), m_priority_peers.end(), p2p_address{na}) != m_priority_peers.end()) || (std::find(m_exclusive_peers.begin(), m_exclusive_peers.end(), p2p_address{na}) != m_exclusive_peers.end());
} }
template<class t_payload_net_handler> template <class Container> template<class t_payload_net_handler> template <class Container>
bool node_server<t_payload_net_handler>::connect_to_peerlist(const Container& peers) bool node_server<t_payload_net_handler>::connect_to_peerlist(const Container& peers)
{ {
const network_zone& public_zone = m_network_zones.at(epee::net_utils::zone::public_); const network_zone& public_zone = m_network_zones.at(epee::net_utils::zone::public_);
for(const epee::net_utils::network_address& na: peers) for(const p2p_address& peer: peers)
{ {
if(public_zone.m_net_server.is_stop_signal_sent()) if(public_zone.m_net_server.is_stop_signal_sent())
return false; return false;
if(is_addr_connected(na)) if(is_addr_connected(peer.na))
continue; continue;
try_to_connect_and_handshake_with_new_peer(na); try_to_connect_and_handshake_with_new_peer(peer);
} }
return true; return true;
@ -2725,19 +2796,20 @@ namespace nodetool
for(const std::string& pr_str: perrs) for(const std::string& pr_str: perrs)
{ {
const uint16_t default_port = cryptonote::get_config(m_nettype).P2P_DEFAULT_PORT; const uint16_t default_port = cryptonote::get_config(m_nettype).P2P_DEFAULT_PORT;
expect<epee::net_utils::network_address> adr = net::get_network_address(pr_str, default_port); auto peer = parse_address(pr_str);
expect<epee::net_utils::network_address> adr = net::get_network_address(peer.address, default_port);
if (adr) if (adr)
{ {
add_zone(adr->get_zone()); add_zone(adr->get_zone());
container.push_back(std::move(*adr)); container.push_back(p2p_address{std::move(*adr), std::move(peer.ssl)});
continue; continue;
} }
std::vector<epee::net_utils::network_address> resolved_addrs; std::vector<p2p_address> resolved_addrs;
bool r = append_net_address(resolved_addrs, pr_str, default_port); bool r = append_net_address(resolved_addrs, peer, default_port);
CHECK_AND_ASSERT_MES(r, false, "Failed to parse or resolve address from string: " << pr_str); CHECK_AND_ASSERT_MES(r, false, "Failed to parse or resolve address from string: " << pr_str);
for (const epee::net_utils::network_address& addr : resolved_addrs) for (p2p_address& addr : resolved_addrs)
{ {
container.push_back(addr); container.push_back(std::move(addr));
} }
} }
@ -2917,7 +2989,7 @@ namespace nodetool
if (!zone.second.m_peerlist.get_random_gray_peer(pe)) if (!zone.second.m_peerlist.get_random_gray_peer(pe))
continue; continue;
if (!check_connection_and_handshake_with_peer(pe.adr, pe.last_seen)) if (!check_connection_and_handshake_with_peer({pe.adr, get_p2p_encryption(pe)}, pe.last_seen))
{ {
zone.second.m_peerlist.remove_from_peer_gray(pe); zone.second.m_peerlist.remove_from_peer_gray(pe);
LOG_PRINT_L2("PEER EVICTED FROM GRAY PEER LIST: address: " << pe.adr.host_str() << " Peer ID: " << peerid_to_string(pe.id)); LOG_PRINT_L2("PEER EVICTED FROM GRAY PEER LIST: address: " << pe.adr.host_str() << " Peer ID: " << peerid_to_string(pe.id));
@ -3102,13 +3174,13 @@ namespace nodetool
template<typename t_payload_net_handler> template<typename t_payload_net_handler>
boost::optional<p2p_connection_context_t<typename t_payload_net_handler::connection_context>> boost::optional<p2p_connection_context_t<typename t_payload_net_handler::connection_context>>
node_server<t_payload_net_handler>::socks_connect(network_zone& zone, const epee::net_utils::network_address& remote, epee::net_utils::ssl_support_t ssl_support) node_server<t_payload_net_handler>::socks_connect(network_zone& zone, const p2p_address& peer)
{ {
auto result = socks_connect_internal(zone.m_net_server.get_stop_signal(), zone.m_net_server.get_io_service(), zone.m_proxy_address, remote); auto result = socks_connect_internal(zone.m_net_server.get_stop_signal(), zone.m_net_server.get_io_service(), zone.m_proxy_address, peer.na);
if (result) // if no error if (result) // if no error
{ {
p2p_connection_context context{}; p2p_connection_context context{};
if (zone.m_net_server.add_connection(context, std::move(*result), remote, ssl_support)) if (zone.m_net_server.add_connection(context, std::move(*result), peer.na, epee::net_utils::ssl_support_t::e_ssl_support_disabled))
return {std::move(context)}; return {std::move(context)};
} }
return boost::none; return boost::none;
@ -3116,10 +3188,10 @@ namespace nodetool
template<typename t_payload_net_handler> template<typename t_payload_net_handler>
boost::optional<p2p_connection_context_t<typename t_payload_net_handler::connection_context>> boost::optional<p2p_connection_context_t<typename t_payload_net_handler::connection_context>>
node_server<t_payload_net_handler>::public_connect(network_zone& zone, epee::net_utils::network_address const& na, epee::net_utils::ssl_support_t ssl_support) node_server<t_payload_net_handler>::public_connect(network_zone& zone, const p2p_address& peer)
{ {
bool is_ipv4 = na.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id(); bool is_ipv4 = peer.na.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id();
bool is_ipv6 = na.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id(); bool is_ipv6 = peer.na.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id();
CHECK_AND_ASSERT_MES(is_ipv4 || is_ipv6, boost::none, CHECK_AND_ASSERT_MES(is_ipv4 || is_ipv6, boost::none,
"Only IPv4 or IPv6 addresses are supported here"); "Only IPv4 or IPv6 addresses are supported here");
@ -3128,13 +3200,13 @@ namespace nodetool
if (is_ipv4) if (is_ipv4)
{ {
const epee::net_utils::ipv4_network_address &ipv4 = na.as<const epee::net_utils::ipv4_network_address>(); const epee::net_utils::ipv4_network_address &ipv4 = peer.na.as<const epee::net_utils::ipv4_network_address>();
address = epee::string_tools::get_ip_string_from_int32(ipv4.ip()); address = epee::string_tools::get_ip_string_from_int32(ipv4.ip());
port = epee::string_tools::num_to_string_fast(ipv4.port()); port = epee::string_tools::num_to_string_fast(ipv4.port());
} }
else if (is_ipv6) else if (is_ipv6)
{ {
const epee::net_utils::ipv6_network_address &ipv6 = na.as<const epee::net_utils::ipv6_network_address>(); const epee::net_utils::ipv6_network_address &ipv6 = peer.na.as<const epee::net_utils::ipv6_network_address>();
address = ipv6.ip().to_string(); address = ipv6.ip().to_string();
port = epee::string_tools::num_to_string_fast(ipv6.port()); port = epee::string_tools::num_to_string_fast(ipv6.port());
} }
@ -3144,10 +3216,14 @@ namespace nodetool
return boost::none; return boost::none;
} }
const auto ssl =
zone.m_ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_disabled ?
zone.m_ssl_support : peer.ssl;
typename net_server::t_connection_context con{}; typename net_server::t_connection_context con{};
const bool res = zone.m_net_server.connect(address, port, const bool res = zone.m_net_server.connect(address, port,
zone.m_config.m_net_config.connection_timeout, zone.m_config.m_net_config.connection_timeout,
con, "0.0.0.0", ssl_support); con, "0.0.0.0", ssl);
if (res) if (res)
return {std::move(con)}; return {std::move(con)};

View file

@ -396,20 +396,30 @@ namespace nodetool
//update gray list //update gray list
auto by_addr_it_gr = m_peers_gray.get<by_addr>().find(ple.adr); auto by_addr_it_gr = m_peers_gray.get<by_addr>().find(ple.adr);
peerlist_entry new_ple = ple;
if(by_addr_it_gr == m_peers_gray.get<by_addr>().end()) if(by_addr_it_gr == m_peers_gray.get<by_addr>().end())
{ {
//put new record into white list // Do not trust encryption information from 3rd parties
m_peers_gray.insert(ple); new_ple.encryption_mode = emode_ssl_autodetect;
new_ple.cert_finger.clear();
m_peers_gray.insert(new_ple);
trim_gray_peerlist(); trim_gray_peerlist();
}else }else
{ {
//update record in gray list //update record in gray list
peerlist_entry new_ple = ple;
if (by_addr_it_gr->pruning_seed && ple.pruning_seed == 0) // guard against older nodes not passing pruning info around if (by_addr_it_gr->pruning_seed && ple.pruning_seed == 0) // guard against older nodes not passing pruning info around
new_ple.pruning_seed = by_addr_it_gr->pruning_seed; new_ple.pruning_seed = by_addr_it_gr->pruning_seed;
if (by_addr_it_gr->rpc_port && ple.rpc_port == 0) // guard against older nodes not passing RPC port around if (by_addr_it_gr->rpc_port && ple.rpc_port == 0) // guard against older nodes not passing RPC port around
new_ple.rpc_port = by_addr_it_gr->rpc_port; new_ple.rpc_port = by_addr_it_gr->rpc_port;
new_ple.last_seen = by_addr_it_gr->last_seen; // do not overwrite the last seen timestamp, incoming peer list are untrusted new_ple.last_seen = by_addr_it_gr->last_seen; // do not overwrite the last seen timestamp, incoming peer list are untrusted
// use existing values for encryption on replace
new_ple.encryption_mode = by_addr_it_gr->encryption_mode;
new_ple.cert_finger = by_addr_it_gr->cert_finger;
m_peers_gray.replace(by_addr_it_gr, new_ple); m_peers_gray.replace(by_addr_it_gr, new_ple);
} }
return true; return true;
@ -420,7 +430,6 @@ namespace nodetool
bool peerlist_manager::append_with_peer_anchor(const anchor_peerlist_entry& ple) bool peerlist_manager::append_with_peer_anchor(const anchor_peerlist_entry& ple)
{ {
TRY_ENTRY(); TRY_ENTRY();
CRITICAL_REGION_LOCAL(m_peerlist_lock); CRITICAL_REGION_LOCAL(m_peerlist_lock);
auto by_addr_it_anchor = m_peers_anchor.get<by_addr>().find(ple.adr); auto by_addr_it_anchor = m_peers_anchor.get<by_addr>().find(ple.adr);

View file

@ -38,7 +38,8 @@
#include "net/i2p_address.h" #include "net/i2p_address.h"
#include "p2p/p2p_protocol_defs.h" #include "p2p/p2p_protocol_defs.h"
BOOST_CLASS_VERSION(nodetool::peerlist_entry, 3) BOOST_CLASS_VERSION(nodetool::peerlist_entry, 4)
BOOST_CLASS_VERSION(nodetool::anchor_peerlist_entry, 1)
namespace boost namespace boost
{ {
@ -238,6 +239,18 @@ namespace boost
return; return;
} }
a & pl.rpc_credits_per_hash; a & pl.rpc_credits_per_hash;
if (ver < 4)
{
if (!typename Archive::is_saving())
{
pl.encryption_mode = nodetool::emode_ssl_autodetect;
pl.cert_finger.clear();
}
return;
}
a & pl.encryption_mode;
a & pl.cert_finger;
} }
template <class Archive, class ver_type> template <class Archive, class ver_type>
@ -246,6 +259,17 @@ namespace boost
a & pl.adr; a & pl.adr;
a & pl.id; a & pl.id;
a & pl.first_seen; a & pl.first_seen;
if (ver < 1)
{
if (!typename Archive::is_saving())
{
pl.encryption_mode = nodetool::emode_ssl_autodetect;
pl.cert_finger.clear();
}
return;
}
a & pl.encryption_mode;
a & pl.cert_finger;
} }
} }
} }

View file

@ -33,10 +33,12 @@
#include <iomanip> #include <iomanip>
#include <boost/uuid/uuid.hpp> #include <boost/uuid/uuid.hpp>
#include <boost/serialization/version.hpp> #include <boost/serialization/version.hpp>
#include <boost/variant/variant.hpp>
#include "serialization/keyvalue_serialization.h" #include "serialization/keyvalue_serialization.h"
#include "net/net_utils_base.h" #include "net/net_utils_base.h"
#include "net/tor_address.h" // needed for serialization #include "net/tor_address.h" // needed for serialization
#include "net/i2p_address.h" // needed for serialization #include "net/i2p_address.h" // needed for serialization
#include "net/net_ssl.h"
#include "misc_language.h" #include "misc_language.h"
#include "string_tools.h" #include "string_tools.h"
#include "time_helper.h" #include "time_helper.h"
@ -48,6 +50,11 @@ namespace nodetool
typedef boost::uuids::uuid uuid; typedef boost::uuids::uuid uuid;
typedef uint64_t peerid_type; typedef uint64_t peerid_type;
// The current modes/versions for p2p encryption
constexpr uint8_t emode_ssl_autodetect = 0;
constexpr uint8_t emode_disabled = 1;
constexpr uint8_t emode_ssl_enabled = 2;
static inline std::string peerid_to_string(peerid_type peer_id) static inline std::string peerid_to_string(peerid_type peer_id)
{ {
std::ostringstream s; std::ostringstream s;
@ -55,6 +62,48 @@ namespace nodetool
return epee::string_tools::pad_string(s.str(), 16, '0', true); return epee::string_tools::pad_string(s.str(), 16, '0', true);
} }
inline uint8_t get_encryption_mode(const epee::net_utils::ssl_support_t support)
{
switch (support)
{
case epee::net_utils::ssl_support_t::e_ssl_support_enabled:
return emode_ssl_enabled;
case epee::net_utils::ssl_support_t::e_ssl_support_disabled:
return emode_disabled;
default:
case epee::net_utils::ssl_support_t::e_ssl_support_autodetect:
break;
}
return emode_ssl_autodetect;
}
template<typename T>
inline epee::net_utils::ssl_options_t get_p2p_encryption(const T& peer)
{
namespace net = epee::net_utils;
switch (peer.encryption_mode)
{
default:
case emode_ssl_autodetect:
return net::ssl_support_t::e_ssl_support_autodetect;
case emode_disabled:
return net::ssl_support_t::e_ssl_support_disabled;
case emode_ssl_enabled:
break;
}
if (peer.cert_finger.empty())
{
// SSL is required, but no verification
net::ssl_options_t out = net::ssl_support_t::e_ssl_support_enabled;
out.verification = net::ssl_verification_t::none;
return out;
}
// Fingerprint specified, require SSL
std::vector<std::vector<std::uint8_t>> fingers{net::convert_fingerprint(peer.cert_finger)};
return {std::move(fingers), {}};
}
#pragma pack (push, 1) #pragma pack (push, 1)
struct network_address_old struct network_address_old
@ -72,11 +121,13 @@ namespace nodetool
struct peerlist_entry_base struct peerlist_entry_base
{ {
AddressType adr; AddressType adr;
std::string cert_finger;
peerid_type id; peerid_type id;
int64_t last_seen; int64_t last_seen;
uint32_t pruning_seed; uint32_t pruning_seed;
uint16_t rpc_port; uint16_t rpc_port;
uint32_t rpc_credits_per_hash; uint32_t rpc_credits_per_hash;
uint8_t encryption_mode;
BEGIN_KV_SERIALIZE_MAP() BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(adr) KV_SERIALIZE(adr)
@ -85,16 +136,9 @@ namespace nodetool
KV_SERIALIZE_OPT(pruning_seed, (uint32_t)0) KV_SERIALIZE_OPT(pruning_seed, (uint32_t)0)
KV_SERIALIZE_OPT(rpc_port, (uint16_t)0) KV_SERIALIZE_OPT(rpc_port, (uint16_t)0)
KV_SERIALIZE_OPT(rpc_credits_per_hash, (uint32_t)0) KV_SERIALIZE_OPT(rpc_credits_per_hash, (uint32_t)0)
KV_SERIALIZE_OPT(encryption_mode, emode_ssl_autodetect)
KV_SERIALIZE_OPT(cert_finger, std::string{})
END_KV_SERIALIZE_MAP() END_KV_SERIALIZE_MAP()
BEGIN_SERIALIZE()
FIELD(adr)
FIELD(id)
VARINT_FIELD(last_seen)
VARINT_FIELD(pruning_seed)
VARINT_FIELD(rpc_port)
VARINT_FIELD(rpc_credits_per_hash)
END_SERIALIZE()
}; };
typedef peerlist_entry_base<epee::net_utils::network_address> peerlist_entry; typedef peerlist_entry_base<epee::net_utils::network_address> peerlist_entry;
@ -102,44 +146,21 @@ namespace nodetool
struct anchor_peerlist_entry_base struct anchor_peerlist_entry_base
{ {
AddressType adr; AddressType adr;
std::string cert_finger;
peerid_type id; peerid_type id;
int64_t first_seen; int64_t first_seen;
uint8_t encryption_mode;
BEGIN_KV_SERIALIZE_MAP() BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(adr) KV_SERIALIZE(adr)
KV_SERIALIZE(id) KV_SERIALIZE(id)
KV_SERIALIZE(first_seen) KV_SERIALIZE(first_seen)
KV_SERIALIZE_OPT(encryption_mode, emode_ssl_autodetect)
KV_SERIALIZE_OPT(cert_finger, std::string{})
END_KV_SERIALIZE_MAP() END_KV_SERIALIZE_MAP()
BEGIN_SERIALIZE()
FIELD(adr)
FIELD(id)
VARINT_FIELD(first_seen)
END_SERIALIZE()
}; };
typedef anchor_peerlist_entry_base<epee::net_utils::network_address> anchor_peerlist_entry; typedef anchor_peerlist_entry_base<epee::net_utils::network_address> anchor_peerlist_entry;
template<typename AddressType>
struct connection_entry_base
{
AddressType adr;
peerid_type id;
bool is_income;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(adr)
KV_SERIALIZE(id)
KV_SERIALIZE(is_income)
END_KV_SERIALIZE_MAP()
BEGIN_SERIALIZE()
FIELD(adr)
FIELD(id)
FIELD(is_income)
END_SERIALIZE()
};
typedef connection_entry_base<epee::net_utils::network_address> connection_entry;
#pragma pack(pop) #pragma pack(pop)
inline inline
@ -184,12 +205,14 @@ namespace nodetool
struct basic_node_data struct basic_node_data
{ {
std::string cert_finger;
uuid network_id; uuid network_id;
uint32_t my_port; uint32_t my_port;
uint16_t rpc_port; uint16_t rpc_port;
uint32_t rpc_credits_per_hash; uint32_t rpc_credits_per_hash;
peerid_type peer_id; peerid_type peer_id;
uint32_t support_flags; uint32_t support_flags;
uint8_t encryption_mode;
BEGIN_KV_SERIALIZE_MAP() BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_VAL_POD_AS_BLOB(network_id) KV_SERIALIZE_VAL_POD_AS_BLOB(network_id)
@ -198,6 +221,8 @@ namespace nodetool
KV_SERIALIZE_OPT(rpc_port, (uint16_t)(0)) KV_SERIALIZE_OPT(rpc_port, (uint16_t)(0))
KV_SERIALIZE_OPT(rpc_credits_per_hash, (uint32_t)0) KV_SERIALIZE_OPT(rpc_credits_per_hash, (uint32_t)0)
KV_SERIALIZE_OPT(support_flags, (uint32_t)0) KV_SERIALIZE_OPT(support_flags, (uint32_t)0)
KV_SERIALIZE_OPT(encryption_mode, emode_ssl_autodetect)
KV_SERIALIZE_OPT(cert_finger, std::string{})
END_KV_SERIALIZE_MAP() END_KV_SERIALIZE_MAP()
}; };

View file

@ -56,9 +56,9 @@ monerod_base = [builddir + "/bin/monerod", "--regtest", "--fixed-difficulty", st
monerod_extra = [ monerod_extra = [
["--offline"], ["--offline"],
["--rpc-payment-address", "44SKxxLQw929wRF6BA9paQ1EWFshNnKhXM3qz6Mo3JGDE2YG3xyzVutMStEicxbQGRfrYvAAYxH6Fe8rnD56EaNwUiqhcwR", "--rpc-payment-difficulty", str(DIFFICULTY), "--rpc-payment-credits", "5000", "--offline"], ["--rpc-payment-address", "44SKxxLQw929wRF6BA9paQ1EWFshNnKhXM3qz6Mo3JGDE2YG3xyzVutMStEicxbQGRfrYvAAYxH6Fe8rnD56EaNwUiqhcwR", "--rpc-payment-difficulty", str(DIFFICULTY), "--rpc-payment-credits", "5000", "--offline"],
["--add-exclusive-node", "127.0.0.1:18283"], ["--add-exclusive-node", "127.0.0.1:18283", "--add-exclusive-node", "127.0.0.1:18284", "--p2p-disable-encryption"],
["--add-exclusive-node", "127.0.0.1:18282"], ["--add-exclusive-node", "127.0.0.1:18282", "--add-exclusive-node", "127.0.0.1:18284"],
["--rpc-login", "md5_lover:Z1ON0101", "--offline"], ["--rpc-login", "md5_lover:Z1ON0101", "--add-exclusive-node", "127.0,0,1:18282,no_encryption", "--add-exclusive-node", "127.0.0.1:18283"],
] ]
wallet_base = [builddir + "/bin/monero-wallet-rpc", "--wallet-dir", WALLET_DIRECTORY, "--rpc-bind-port", "wallet_port", "--rpc-ssl", "disabled", "--daemon-ssl", "disabled", "--log-level", "1", "--allow-mismatched-daemon-version"] wallet_base = [builddir + "/bin/monero-wallet-rpc", "--wallet-dir", WALLET_DIRECTORY, "--rpc-bind-port", "wallet_port", "--rpc-ssl", "disabled", "--daemon-ssl", "disabled", "--log-level", "1", "--allow-mismatched-daemon-version"]
wallet_extra = [ wallet_extra = [

View file

@ -45,6 +45,7 @@ class P2PTest():
self.mine(80) self.mine(80)
self.test_p2p_reorg() self.test_p2p_reorg()
self.test_p2p_tx_propagation() self.test_p2p_tx_propagation()
self.test_p2p_ssl()
def reset(self): def reset(self):
print('Resetting blockchain') print('Resetting blockchain')
@ -78,6 +79,8 @@ class P2PTest():
daemon3 = Daemon(idx = 3) daemon3 = Daemon(idx = 3)
# give sync some time # give sync some time
daemon2.stop_mining()
daemon3.stop_mining()
time.sleep(1) time.sleep(1)
res = daemon2.get_info() res = daemon2.get_info()
@ -168,6 +171,8 @@ class P2PTest():
res = daemon.get_transaction_pool_hashes() res = daemon.get_transaction_pool_hashes()
assert not 'tx_hashes' in res or len(res.tx_hashes) == 0 assert not 'tx_hashes' in res or len(res.tx_hashes) == 0
daemon2.stop_mining()
daemon3.stop_mining()
self.wallet.refresh() self.wallet.refresh()
res = self.wallet.get_balance() res = self.wallet.get_balance()
@ -176,13 +181,36 @@ class P2PTest():
assert len(res.tx_hash) == 32*2 assert len(res.tx_hash) == 32*2
txid = res.tx_hash txid = res.tx_hash
time.sleep(5) time.sleep(45)
for daemon in [daemon2, daemon3]: for daemon in [daemon2, daemon3]:
res = daemon.get_transaction_pool_hashes() res = daemon.get_transaction_pool_hashes()
assert len(res.tx_hashes) == 1 assert len(res.tx_hashes) == 1
assert res.tx_hashes[0] == txid assert res.tx_hashes[0] == txid
def test_p2p_ssl(self):
print('Testing P2P SSL')
daemon2 = Daemon(idx = 2)
daemon3 = Daemon(idx = 3)
daemon4 = Daemon(idx = 4, username="md5_lover", password="Z1ON0101")
connections = daemon2.get_connections().connections
for connection in connections:
assert connection.ssl == 0
connections = daemon3.get_connections().connections
for connection in connections:
if connection.port == "18282":
assert connection.ssl == 0
elif connection.port == "18284":
assert connection.ssl == 2
connections = daemon4.get_connections().connections
for connection in connections:
if connection.port == "18282":
assert connection.ssl == 0
elif connection.port == "18283":
assert connection.ssl == 2
if __name__ == '__main__': if __name__ == '__main__':
P2PTest().run_test() P2PTest().run_test()

View file

@ -47,7 +47,7 @@ namespace net_load_tests
{ {
struct test_connection_context : epee::net_utils::connection_context_base struct test_connection_context : epee::net_utils::connection_context_base
{ {
test_connection_context(): epee::net_utils::connection_context_base(boost::uuids::nil_uuid(), {}, false, false), m_closed(false) {} test_connection_context(): epee::net_utils::connection_context_base(boost::uuids::nil_uuid(), {}, false, epee::net_utils::ssl_support_t::e_ssl_support_disabled), m_closed(false) {}
static constexpr int handshake_command() noexcept { return 1001; } static constexpr int handshake_command() noexcept { return 1001; }
static constexpr bool handshake_complete() noexcept { return true; } static constexpr bool handshake_complete() noexcept { return true; }
size_t get_max_bytes(int command) const { return LEVIN_DEFAULT_MAX_PACKET_SIZE; } size_t get_max_bytes(int command) const { return LEVIN_DEFAULT_MAX_PACKET_SIZE; }

View file

@ -372,14 +372,14 @@ TEST_F(positive_test_connection_to_levin_protocol_handler_calls, handler_process
epee::levin::bucket_head2 resp_head; epee::levin::bucket_head2 resp_head;
ASSERT_LT(sizeof(resp_head), send_data.size()); ASSERT_LT(sizeof(resp_head), send_data.size());
std::memcpy(std::addressof(resp_head), send_data.data(), sizeof(resp_head)); std::memcpy(std::addressof(resp_head), send_data.data(), sizeof(resp_head));
std::string out_data = send_data.substr(sizeof(resp_head)); std::string out_data = send_data.substr(sizeof(resp_head), expected_out_data.size());
// Check sent response // Check sent response
ASSERT_EQ(expected_out_data, out_data); ASSERT_EQ(expected_out_data, out_data);
ASSERT_EQ(LEVIN_SIGNATURE, SWAP64LE(resp_head.m_signature)); ASSERT_EQ(LEVIN_SIGNATURE, SWAP64LE(resp_head.m_signature));
ASSERT_EQ(expected_command, SWAP32LE(resp_head.m_command)); ASSERT_EQ(expected_command, SWAP32LE(resp_head.m_command));
ASSERT_EQ(expected_return_code, SWAP32LE(resp_head.m_return_code)); ASSERT_EQ(expected_return_code, SWAP32LE(resp_head.m_return_code));
ASSERT_EQ(expected_out_data.size(), SWAP64LE(resp_head.m_cb)); ASSERT_LE(expected_out_data.size(), SWAP64LE(resp_head.m_cb));
ASSERT_FALSE(resp_head.m_have_to_return_data); ASSERT_FALSE(resp_head.m_have_to_return_data);
ASSERT_EQ(SWAP32LE(LEVIN_PROTOCOL_VER_1), resp_head.m_protocol_version); ASSERT_EQ(SWAP32LE(LEVIN_PROTOCOL_VER_1), resp_head.m_protocol_version);
ASSERT_TRUE(0 != (SWAP32LE(resp_head.m_flags) & LEVIN_PACKET_RESPONSE)); ASSERT_TRUE(0 != (SWAP32LE(resp_head.m_flags) & LEVIN_PACKET_RESPONSE));
@ -438,7 +438,7 @@ TEST_F(positive_test_connection_to_levin_protocol_handler_calls, handler_process
message.buffer.write(epee::to_span(in_data)); message.buffer.write(epee::to_span(in_data));
const epee::byte_slice noise = epee::levin::make_noise_notify(1024); const epee::byte_slice noise = epee::levin::make_noise_notify(1024);
const epee::byte_slice notify = message.finalize_notify(expected_command); const epee::byte_slice notify = message.finalize_notify(expected_command, false);
test_connection_ptr conn = create_connection(); test_connection_ptr conn = create_connection();
@ -479,7 +479,7 @@ TEST_F(positive_test_connection_to_levin_protocol_handler_calls, handler_process
in_fragmented_data.buffer.put_n('c', 1024 * 4); in_fragmented_data.buffer.put_n('c', 1024 * 4);
const epee::byte_slice noise = epee::levin::make_noise_notify(1024); const epee::byte_slice noise = epee::levin::make_noise_notify(1024);
const epee::byte_slice notify = message.finalize_notify(expected_command); const epee::byte_slice notify = message.finalize_notify(expected_command, false);
epee::byte_slice fragmented = epee::levin::make_fragmented_notify(noise.size(), expected_fragmented_command, std::move(in_fragmented_data)); epee::byte_slice fragmented = epee::levin::make_fragmented_notify(noise.size(), expected_fragmented_command, std::move(in_fragmented_data));
EXPECT_EQ(5u, fragmented.size() / 1024); EXPECT_EQ(5u, fragmented.size() / 1024);

View file

@ -177,7 +177,7 @@ namespace
handler_(std::addressof(endpoint_), connections, context_) handler_(std::addressof(endpoint_), connections, context_)
{ {
using base_type = epee::net_utils::connection_context_base; using base_type = epee::net_utils::connection_context_base;
static_cast<base_type&>(context_) = base_type{random_generator(), {}, is_incoming, false}; static_cast<base_type&>(context_) = base_type{random_generator(), {}, is_incoming, epee::net_utils::ssl_support_t::e_ssl_support_disabled};
context_.m_state = cryptonote::cryptonote_connection_context::state_normal; context_.m_state = cryptonote::cryptonote_connection_context::state_normal;
handler_.after_init_connection(); handler_.after_init_connection();
} }
@ -397,7 +397,7 @@ TEST(make_header, expect_return)
TEST(message_writer, invoke_with_empty_payload) TEST(message_writer, invoke_with_empty_payload)
{ {
const epee::byte_slice message = epee::levin::message_writer{}.finalize_invoke(443); const epee::byte_slice message = epee::levin::message_writer{}.finalize_invoke(443, false);
const epee::levin::bucket_head2 header = const epee::levin::bucket_head2 header =
epee::levin::make_header(443, 0, LEVIN_PACKET_REQUEST, true); epee::levin::make_header(443, 0, LEVIN_PACKET_REQUEST, true);
ASSERT_EQ(sizeof(header), message.size()); ASSERT_EQ(sizeof(header), message.size());
@ -412,7 +412,7 @@ TEST(message_writer, invoke_with_payload)
epee::levin::message_writer writer{}; epee::levin::message_writer writer{};
writer.buffer.write(epee::to_span(bytes)); writer.buffer.write(epee::to_span(bytes));
const epee::byte_slice message = writer.finalize_invoke(443); const epee::byte_slice message = writer.finalize_invoke(443, false);
const epee::levin::bucket_head2 header = const epee::levin::bucket_head2 header =
epee::levin::make_header(443, bytes.size(), LEVIN_PACKET_REQUEST, true); epee::levin::make_header(443, bytes.size(), LEVIN_PACKET_REQUEST, true);
@ -423,7 +423,7 @@ TEST(message_writer, invoke_with_payload)
TEST(message_writer, notify_with_empty_payload) TEST(message_writer, notify_with_empty_payload)
{ {
const epee::byte_slice message = epee::levin::message_writer{}.finalize_notify(443); const epee::byte_slice message = epee::levin::message_writer{}.finalize_notify(443, false);
const epee::levin::bucket_head2 header = const epee::levin::bucket_head2 header =
epee::levin::make_header(443, 0, LEVIN_PACKET_REQUEST, false); epee::levin::make_header(443, 0, LEVIN_PACKET_REQUEST, false);
ASSERT_EQ(sizeof(header), message.size()); ASSERT_EQ(sizeof(header), message.size());
@ -438,7 +438,7 @@ TEST(message_writer, notify_with_payload)
epee::levin::message_writer writer{}; epee::levin::message_writer writer{};
writer.buffer.write(epee::to_span(bytes)); writer.buffer.write(epee::to_span(bytes));
const epee::byte_slice message = writer.finalize_notify(443); const epee::byte_slice message = writer.finalize_notify(443, false);
const epee::levin::bucket_head2 header = const epee::levin::bucket_head2 header =
epee::levin::make_header(443, bytes.size(), LEVIN_PACKET_REQUEST, false); epee::levin::make_header(443, bytes.size(), LEVIN_PACKET_REQUEST, false);
@ -449,7 +449,7 @@ TEST(message_writer, notify_with_payload)
TEST(message_writer, response_with_empty_payload) TEST(message_writer, response_with_empty_payload)
{ {
const epee::byte_slice message = epee::levin::message_writer{}.finalize_response(443, 1); const epee::byte_slice message = epee::levin::message_writer{}.finalize_response(443, 1, false);
epee::levin::bucket_head2 header = epee::levin::bucket_head2 header =
epee::levin::make_header(443, 0, LEVIN_PACKET_RESPONSE, false); epee::levin::make_header(443, 0, LEVIN_PACKET_RESPONSE, false);
header.m_return_code = SWAP32LE(1); header.m_return_code = SWAP32LE(1);
@ -465,7 +465,7 @@ TEST(message_writer, response_with_payload)
epee::levin::message_writer writer{}; epee::levin::message_writer writer{};
writer.buffer.write(epee::to_span(bytes)); writer.buffer.write(epee::to_span(bytes));
const epee::byte_slice message = writer.finalize_response(443, 6450); const epee::byte_slice message = writer.finalize_response(443, 6450, false);
epee::levin::bucket_head2 header = epee::levin::bucket_head2 header =
epee::levin::make_header(443, bytes.size(), LEVIN_PACKET_RESPONSE, false); epee::levin::make_header(443, bytes.size(), LEVIN_PACKET_RESPONSE, false);
header.m_return_code = SWAP32LE(6450); header.m_return_code = SWAP32LE(6450);
@ -480,9 +480,9 @@ TEST(message_writer, error)
epee::levin::message_writer writer{}; epee::levin::message_writer writer{};
writer.buffer.clear(); writer.buffer.clear();
EXPECT_THROW(writer.finalize_invoke(0), std::runtime_error); EXPECT_THROW(writer.finalize_invoke(0, false), std::runtime_error);
EXPECT_THROW(writer.finalize_notify(0), std::runtime_error); EXPECT_THROW(writer.finalize_notify(0, false), std::runtime_error);
EXPECT_THROW(writer.finalize_response(0, 0), std::runtime_error); EXPECT_THROW(writer.finalize_response(0, 0, false), std::runtime_error);
} }
TEST(make_noise, invalid) TEST(make_noise, invalid)
@ -1068,7 +1068,7 @@ TEST_F(levin_notify, fluff_with_padding)
{ {
auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
EXPECT_EQ(txs, notification.txs); EXPECT_EQ(txs, notification.txs);
EXPECT_FALSE(notification._.empty()); EXPECT_TRUE(notification._.empty());
EXPECT_TRUE(notification.dandelionpp_fluff); EXPECT_TRUE(notification.dandelionpp_fluff);
} }
} }
@ -1133,7 +1133,7 @@ TEST_F(levin_notify, stem_with_padding)
{ {
auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
EXPECT_EQ(txs, notification.txs); EXPECT_EQ(txs, notification.txs);
EXPECT_FALSE(notification._.empty()); EXPECT_TRUE(notification._.empty());
EXPECT_EQ(!is_stem, notification.dandelionpp_fluff); EXPECT_EQ(!is_stem, notification.dandelionpp_fluff);
} }
@ -1196,7 +1196,7 @@ TEST_F(levin_notify, stem_no_outs_with_padding)
{ {
auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
EXPECT_EQ(sorted_txs, notification.txs); EXPECT_EQ(sorted_txs, notification.txs);
EXPECT_FALSE(notification._.empty()); EXPECT_TRUE(notification._.empty());
EXPECT_TRUE(notification.dandelionpp_fluff); EXPECT_TRUE(notification.dandelionpp_fluff);
} }
} }
@ -1265,7 +1265,7 @@ TEST_F(levin_notify, local_with_padding)
{ {
auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
EXPECT_EQ(their_txs, notification.txs); EXPECT_EQ(their_txs, notification.txs);
EXPECT_FALSE(notification._.empty()); EXPECT_TRUE(notification._.empty());
EXPECT_EQ(!is_stem, notification.dandelionpp_fluff); EXPECT_EQ(!is_stem, notification.dandelionpp_fluff);
} }
@ -1294,7 +1294,7 @@ TEST_F(levin_notify, local_with_padding)
EXPECT_EQ(1u, receiver_.notified_size()); EXPECT_EQ(1u, receiver_.notified_size());
auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
EXPECT_EQ(my_txs, notification.txs); EXPECT_EQ(my_txs, notification.txs);
EXPECT_FALSE(notification._.empty()); EXPECT_TRUE(notification._.empty());
EXPECT_TRUE(!notification.dandelionpp_fluff); EXPECT_TRUE(!notification.dandelionpp_fluff);
has_stemmed |= is_stem; has_stemmed |= is_stem;
@ -1362,7 +1362,7 @@ TEST_F(levin_notify, forward_with_padding)
{ {
auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
EXPECT_EQ(txs, notification.txs); EXPECT_EQ(txs, notification.txs);
EXPECT_FALSE(notification._.empty()); EXPECT_TRUE(notification._.empty());
EXPECT_EQ(!is_stem, notification.dandelionpp_fluff); EXPECT_EQ(!is_stem, notification.dandelionpp_fluff);
} }
@ -1755,7 +1755,7 @@ TEST_F(levin_notify, private_fluff_with_padding)
{ {
auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
EXPECT_EQ(txs, notification.txs); EXPECT_EQ(txs, notification.txs);
EXPECT_FALSE(notification._.empty()); EXPECT_TRUE(notification._.empty());
EXPECT_TRUE(notification.dandelionpp_fluff); EXPECT_TRUE(notification.dandelionpp_fluff);
} }
} }
@ -1807,7 +1807,7 @@ TEST_F(levin_notify, private_stem_with_padding)
{ {
auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
EXPECT_EQ(txs, notification.txs); EXPECT_EQ(txs, notification.txs);
EXPECT_FALSE(notification._.empty()); EXPECT_TRUE(notification._.empty());
EXPECT_TRUE(notification.dandelionpp_fluff); EXPECT_TRUE(notification.dandelionpp_fluff);
} }
} }
@ -1859,7 +1859,7 @@ TEST_F(levin_notify, private_local_with_padding)
{ {
auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
EXPECT_EQ(txs, notification.txs); EXPECT_EQ(txs, notification.txs);
EXPECT_FALSE(notification._.empty()); EXPECT_TRUE(notification._.empty());
EXPECT_TRUE(notification.dandelionpp_fluff); EXPECT_TRUE(notification.dandelionpp_fluff);
} }
} }
@ -1911,7 +1911,7 @@ TEST_F(levin_notify, private_forward_with_padding)
{ {
auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
EXPECT_EQ(txs, notification.txs); EXPECT_EQ(txs, notification.txs);
EXPECT_FALSE(notification._.empty()); EXPECT_TRUE(notification._.empty());
EXPECT_TRUE(notification.dandelionpp_fluff); EXPECT_TRUE(notification.dandelionpp_fluff);
} }
} }
@ -2385,7 +2385,7 @@ TEST_F(levin_notify, command_max_bytes)
{ {
epee::levin::message_writer dest{}; epee::levin::message_writer dest{};
dest.buffer.write(epee::to_span(payload)); dest.buffer.write(epee::to_span(payload));
bytes = dest.finalize_notify(ping_command); bytes = dest.finalize_notify(ping_command, false);
} }
EXPECT_EQ(1, get_connections().send(bytes.clone(), contexts_.front().get_id())); EXPECT_EQ(1, get_connections().send(bytes.clone(), contexts_.front().get_id()));
@ -2401,7 +2401,7 @@ TEST_F(levin_notify, command_max_bytes)
payload.push_back('h'); payload.push_back('h');
epee::levin::message_writer dest{}; epee::levin::message_writer dest{};
dest.buffer.write(epee::to_span(payload)); dest.buffer.write(epee::to_span(payload));
bytes = dest.finalize_notify(ping_command); bytes = dest.finalize_notify(ping_command, true);
} }
EXPECT_EQ(1, get_connections().send(std::move(bytes), contexts_.front().get_id())); EXPECT_EQ(1, get_connections().send(std::move(bytes), contexts_.front().get_id()));

View file

@ -530,14 +530,14 @@ TEST(cryptonote_protocol_handler, race_condition)
} }
virtual bool invoke_notify_to_peer(int command, epee::levin::message_writer in, const contexts::basic& context) override { virtual bool invoke_notify_to_peer(int command, epee::levin::message_writer in, const contexts::basic& context) override {
if (shared_state) if (shared_state)
return shared_state->send(in.finalize_notify(command), context.m_connection_id); return shared_state->send(in.finalize_notify(command, true), context.m_connection_id);
else else
return {}; return {};
} }
virtual bool relay_notify_to_list(int command, epee::levin::message_writer in, connections_t connections) override { virtual bool relay_notify_to_list(int command, epee::levin::message_writer in, connections_t connections) override {
if (shared_state) { if (shared_state) {
for (auto &e: connections) for (auto &e: connections)
shared_state->send(in.finalize_notify(command), e.second); shared_state->send(in.finalize_notify(command, true), e.second);
} }
return {}; return {};
} }
@ -1090,6 +1090,7 @@ TEST(node_server, race_condition)
conn->get_context(context); conn->get_context(context);
event_t handshaked; event_t handshaked;
typename messages::handshake::request_t msg{{ typename messages::handshake::request_t msg{{
std::string{},
::config::NETWORK_ID, ::config::NETWORK_ID,
58080, 58080,
}}; }};

View file

@ -111,13 +111,13 @@ TEST(peerlist_storage, store)
std::string buffer{}; std::string buffer{};
{ {
nodetool::peerlist_types types{}; nodetool::peerlist_types types{};
types.white.push_back({epee::net_utils::ipv4_network_address{1000, 10}, 44, 55}); types.white.push_back({epee::net_utils::ipv4_network_address{1000, 10}, {}, 44, 55});
types.white.push_back({net::tor_address::unknown(), 64, 75}); types.white.push_back({net::tor_address::unknown(), {}, 64, 75});
types.gray.push_back({net::tor_address::unknown(), 99, 88}); types.gray.push_back({net::tor_address::unknown(), {}, 99, 88});
types.gray.push_back({epee::net_utils::ipv4_network_address{2000, 20}, 84, 45}); types.gray.push_back({epee::net_utils::ipv4_network_address{2000, 20}, {}, 84, 45});
types.anchor.push_back({epee::net_utils::ipv4_network_address{999, 654}, 444, 555}); types.anchor.push_back({epee::net_utils::ipv4_network_address{999, 654}, {}, 444, 555});
types.anchor.push_back({net::tor_address::unknown(), 14, 33}); types.anchor.push_back({net::tor_address::unknown(), {}, 14, 33});
types.anchor.push_back({net::tor_address::unknown(), 24, 22}); types.anchor.push_back({net::tor_address::unknown(), {}, 24, 22});
std::ostringstream stream{}; std::ostringstream stream{};
EXPECT_TRUE(peers.store(stream, types)); EXPECT_TRUE(peers.store(stream, types));