Add SSL support to P2P
This commit is contained in:
parent
cc73fe7116
commit
60925f1846
|
@ -131,6 +131,7 @@ namespace net_utils
|
|||
void terminate();
|
||||
void on_terminating();
|
||||
|
||||
|
||||
bool send(epee::byte_slice message);
|
||||
bool start_internal(
|
||||
bool is_income,
|
||||
|
@ -296,8 +297,19 @@ namespace net_utils
|
|||
|
||||
|
||||
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();
|
||||
|
||||
//! 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:
|
||||
//----------------- 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);
|
||||
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);
|
||||
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);
|
||||
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_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_autodetect);
|
||||
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
|
||||
{
|
||||
|
@ -477,6 +490,11 @@ namespace net_utils
|
|||
|
||||
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;
|
||||
|
||||
/// The io_service used to perform asynchronous operations.
|
||||
|
|
|
@ -895,7 +895,7 @@ namespace net_utils
|
|||
boost::uuids::random_generator()(),
|
||||
*real_remote,
|
||||
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();
|
||||
try { host_count(1); } catch(...) { /* ignore */ }
|
||||
|
@ -1559,7 +1559,7 @@ namespace net_utils
|
|||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
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();
|
||||
|
||||
|
@ -1635,7 +1635,7 @@ namespace net_utils
|
|||
{
|
||||
// Handshake
|
||||
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)
|
||||
{
|
||||
|
@ -1649,6 +1649,7 @@ namespace net_utils
|
|||
sock_.close();
|
||||
return CONNECT_FAILURE;
|
||||
}
|
||||
new_connection_l->set_ssl_enabled();
|
||||
}
|
||||
|
||||
return CONNECT_SUCCESS;
|
||||
|
@ -1657,11 +1658,11 @@ namespace net_utils
|
|||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
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();
|
||||
|
||||
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_.insert(new_connection_l);
|
||||
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(*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)
|
||||
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
|
||||
MERROR("SSL handshake failed on an autodetect connection, reconnecting without 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)
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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);
|
||||
bool r = remove_connection(new_connection_l) && new_connection_l->start(false, 1 < m_threads_count);
|
||||
if (r)
|
||||
{
|
||||
new_connection_l->get_context(conn_context);
|
||||
|
@ -1783,10 +1782,10 @@ namespace net_utils
|
|||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
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();
|
||||
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_.insert(new_connection_l);
|
||||
MDEBUG("connections_ size now " << connections_.size());
|
||||
|
@ -1863,60 +1862,113 @@ namespace net_utils
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
boost::shared_ptr<boost::asio::deadline_timer> sh_deadline(new boost::asio::deadline_timer(io_service_));
|
||||
//start deadline
|
||||
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;
|
||||
ssl_options.configure(new_connection_l->socket_, boost::asio::ssl::stream_base::client);
|
||||
return connect_async_internal(new_connection_l, remote_endpoint, conn_timeout, cb);
|
||||
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
|
||||
|
|
|
@ -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; }
|
||||
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
|
||||
return m_state->ssl_options().handshake(socket_, type, 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));
|
||||
return m_state->ssl_options().handshake(socket_, boost::asio::ssl::stream_base::server, buffer);
|
||||
}
|
||||
|
||||
// various handlers to be called from connection class:
|
||||
|
|
|
@ -130,7 +130,7 @@ namespace levin
|
|||
//! Provides space for levin (p2p) header, so that payload can be sent without copy
|
||||
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:
|
||||
using header = bucket_head2;
|
||||
|
||||
|
@ -147,12 +147,13 @@ namespace levin
|
|||
{
|
||||
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); }
|
||||
byte_slice finalize_notify(uint32_t command) { return finalize(command, LEVIN_PACKET_REQUEST, 0, false); }
|
||||
byte_slice finalize_response(uint32_t command, uint32_t return_code)
|
||||
|
||||
// `pad == true` will add 0-8192 of zero bytes (actual amount randomized)
|
||||
byte_slice finalize_invoke(uint32_t command, bool pad) { return finalize(command, LEVIN_PACKET_REQUEST, 0, true, pad); }
|
||||
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
|
||||
|
|
|
@ -486,7 +486,7 @@ public:
|
|||
|
||||
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
|
||||
<< ", r?=" << m_current_head.m_have_to_return_data
|
||||
<<", 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())
|
||||
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;
|
||||
}
|
||||
else
|
||||
|
@ -620,7 +620,7 @@ public:
|
|||
if (command == m_connection_context.handshake_command())
|
||||
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");
|
||||
err_code = LEVIN_ERROR_CONNECTION;
|
||||
|
|
|
@ -46,7 +46,7 @@ namespace epee
|
|||
namespace net_utils
|
||||
{
|
||||
enum class ssl_support_t: uint8_t {
|
||||
e_ssl_support_disabled,
|
||||
e_ssl_support_disabled = 0,
|
||||
e_ssl_support_enabled,
|
||||
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.
|
||||
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`.
|
||||
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_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
|
||||
*
|
||||
|
|
|
@ -32,11 +32,13 @@
|
|||
#include <boost/uuid/uuid.hpp>
|
||||
#include <boost/asio/io_service.hpp>
|
||||
#include <boost/asio/ip/address_v6.hpp>
|
||||
#include <boost/optional/optional.hpp>
|
||||
#include <typeinfo>
|
||||
#include <type_traits>
|
||||
#include "byte_slice.h"
|
||||
#include "enums.h"
|
||||
#include "misc_log_ex.h"
|
||||
#include "net_ssl.h"
|
||||
#include "serialization/keyvalue_serialization.h"
|
||||
#include "int-util.h"
|
||||
|
||||
|
@ -367,7 +369,7 @@ namespace net_utils
|
|||
const network_address m_remote_address;
|
||||
const bool m_is_income;
|
||||
const time_t m_started;
|
||||
const bool m_ssl;
|
||||
const ssl_support_t m_ssl;
|
||||
time_t m_last_recv;
|
||||
time_t m_last_send;
|
||||
uint64_t m_recv_cnt;
|
||||
|
@ -378,7 +380,7 @@ namespace net_utils
|
|||
double m_max_speed_up;
|
||||
|
||||
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,
|
||||
uint64_t recv_cnt = 0, uint64_t send_cnt = 0):
|
||||
m_connection_id(connection_id),
|
||||
|
@ -400,7 +402,7 @@ namespace net_utils
|
|||
m_remote_address(),
|
||||
m_is_income(false),
|
||||
m_started(time(NULL)),
|
||||
m_ssl(false),
|
||||
m_ssl(ssl_support_t::e_ssl_support_disabled),
|
||||
m_last_recv(0),
|
||||
m_last_send(0),
|
||||
m_recv_cnt(0),
|
||||
|
@ -421,11 +423,18 @@ namespace net_utils
|
|||
set_details(a.m_connection_id, a.m_remote_address, a.m_is_income, a.m_ssl);
|
||||
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:
|
||||
template<class t_protocol_handler>
|
||||
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();
|
||||
new(this) connection_context_base(connection_id, remote_address, is_income, ssl);
|
||||
|
|
|
@ -111,7 +111,8 @@ namespace epee
|
|||
levin::message_writer to_send;
|
||||
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 )
|
||||
{
|
||||
MERROR("Failed to notify command " << command << " return code " << res);
|
||||
|
|
|
@ -70,6 +70,7 @@ target_link_libraries(epee
|
|||
${Boost_REGEX_LIBRARY}
|
||||
${Boost_SYSTEM_LIBRARY}
|
||||
${OPENSSL_LIBRARIES}
|
||||
${sodium_LIBRARIES}
|
||||
PRIVATE
|
||||
${EXTRA_LIBRARIES})
|
||||
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
|
||||
#include "net/levin_base.h"
|
||||
|
||||
#include <sodium/randombytes.h>
|
||||
#include "cryptonote_config.h"
|
||||
#include "int-util.h"
|
||||
|
||||
namespace epee
|
||||
|
@ -41,15 +43,25 @@ namespace levin
|
|||
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))
|
||||
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);
|
||||
|
||||
std::memcpy(buffer.tellp() - buffer.size(), std::addressof(head), sizeof(head));
|
||||
buffer.put_n(0, pad_bytes);
|
||||
return byte_slice{std::move(buffer)};
|
||||
}
|
||||
|
||||
|
@ -92,12 +104,13 @@ namespace levin
|
|||
will ignore extra bytes. So just pad with zeroes and otherwise send
|
||||
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());
|
||||
return message.finalize_notify(command);
|
||||
return message.finalize_notify(command, false);
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
const size_t payload_space = noise_size - sizeof(bucket_head2);
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
// 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.
|
||||
|
||||
#include <algorithm>
|
||||
#include <string.h>
|
||||
#include <thread>
|
||||
#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_callback([&](const bool preverified, boost::asio::ssl::verify_context &ctx)
|
||||
// callback occurs after `configure` has exited, so keep copies and not references
|
||||
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
|
||||
// when fingerprints are whitelisted.
|
||||
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
|
||||
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");
|
||||
return false;
|
||||
|
@ -644,6 +646,44 @@ bool ssl_options_t::handshake(
|
|||
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)
|
||||
{
|
||||
unsigned int j;
|
||||
|
|
|
@ -135,6 +135,50 @@ the `Expect Response` field zeroed. When a message of this type is received, the
|
|||
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
|
||||
### P2P (Admin) Commands
|
||||
|
||||
|
|
|
@ -147,6 +147,7 @@
|
|||
#define P2P_DEFAULT_SYNC_SEARCH_CONNECTIONS_COUNT 2
|
||||
#define P2P_DEFAULT_LIMIT_RATE_UP 2048 // 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_IP_BLOCKTIME (60*60*24) //24 hour
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "serialization/keyvalue_serialization.h"
|
||||
#include "cryptonote_basic/cryptonote_basic.h"
|
||||
#include "cryptonote_basic/blobdatatype.h"
|
||||
#include "net/net_ssl.h"
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
|
@ -49,7 +50,7 @@ namespace cryptonote
|
|||
bool incoming;
|
||||
bool localhost;
|
||||
bool local_ip;
|
||||
bool ssl;
|
||||
epee::net_utils::ssl_support_t ssl;
|
||||
|
||||
std::string address;
|
||||
std::string host;
|
||||
|
@ -87,9 +88,11 @@ namespace cryptonote
|
|||
uint8_t address_type;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
uint8_t ssl_i = uint8_t(ssl);
|
||||
KV_SERIALIZE(incoming)
|
||||
KV_SERIALIZE(localhost)
|
||||
KV_SERIALIZE(local_ip)
|
||||
epee::serialization::selector<is_store>::serialize(ssl_i, stg, hparent_section, "ssl");
|
||||
KV_SERIALIZE(address)
|
||||
KV_SERIALIZE(host)
|
||||
KV_SERIALIZE(ip)
|
||||
|
@ -112,6 +115,7 @@ namespace cryptonote
|
|||
KV_SERIALIZE(height)
|
||||
KV_SERIALIZE(pruning_seed)
|
||||
KV_SERIALIZE(address_type)
|
||||
ssl = epee::net_utils::ssl_support_t(ssl_i);
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
|
|
|
@ -159,40 +159,12 @@ namespace levin
|
|||
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{};
|
||||
request.txs = std::move(txs);
|
||||
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;
|
||||
if (!epee::serialization::store_t_to_binary(request, out.buffer))
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -847,7 +819,7 @@ namespace levin
|
|||
// Padding is not useful when using noise mode. Send as stem so receiver
|
||||
// forwards in Dandelion++ mode.
|
||||
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())
|
||||
{
|
||||
|
|
|
@ -653,7 +653,7 @@ bool t_rpc_command_executor::print_connections() {
|
|||
|
||||
tools::msg_writer() << std::setw(host_field_width) << std::left << "Remote Host"
|
||||
<< std::setw(8) << "Type"
|
||||
<< std::setw(6) << "SSL"
|
||||
<< std::setw(13) << "SSL"
|
||||
<< std::setw(20) << "Peer id"
|
||||
<< std::setw(20) << "Support Flags"
|
||||
<< 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(13) << "Up(now)"
|
||||
<< 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)
|
||||
{
|
||||
|
@ -674,7 +688,7 @@ bool t_rpc_command_executor::print_connections() {
|
|||
//<< std::setw(30) << std::left << in_out
|
||||
<< 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(6) << (info.ssl ? "yes" : "no")
|
||||
<< std::setw(13) << ssl_string(info.ssl)
|
||||
<< std::setw(20) << info.peer_id
|
||||
<< 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) + ")"
|
||||
|
|
|
@ -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<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_priority_node = {"add-priority-node", "Specify list of peers to connect to and attempt to keep the connection open"};
|
||||
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"};
|
||||
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_peer = {"add-peer", "Manually add peer to local peerlist. 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_add_priority_node = {"add-priority-node", "Specify list of peers to connect to and attempt to keep the connection open. "
|
||||
"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_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_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<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_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<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<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};
|
||||
|
||||
boost::optional<std::vector<proxy>> get_proxies(boost::program_options::variables_map const& vm)
|
||||
|
|
|
@ -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
|
||||
{
|
||||
p2p_connection_context_t()
|
||||
: peer_id(0),
|
||||
: cert_finger(),
|
||||
peer_id(0),
|
||||
support_flags(0),
|
||||
encryption_mode(emode_ssl_autodetect),
|
||||
m_in_timedsync(false)
|
||||
{}
|
||||
|
||||
std::string cert_finger;
|
||||
peerid_type peer_id;
|
||||
uint32_t support_flags;
|
||||
uint8_t encryption_mode;
|
||||
bool m_in_timedsync;
|
||||
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>
|
||||
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>,
|
||||
|
@ -139,7 +180,7 @@ namespace nodetool
|
|||
typedef epee::net_utils::boosted_tcp_server<epee::levin::async_protocol_handler<p2p_connection_context>> net_server;
|
||||
|
||||
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
|
||||
{
|
||||
|
@ -165,6 +206,7 @@ namespace nodetool
|
|||
m_bind_ipv6_address(),
|
||||
m_port(),
|
||||
m_port_ipv6(),
|
||||
m_ssl_finger(),
|
||||
m_notifier(),
|
||||
m_our_address(),
|
||||
m_peerlist(),
|
||||
|
@ -172,6 +214,7 @@ namespace nodetool
|
|||
m_proxy_address(),
|
||||
m_current_number_of_out_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_can_pingback(false),
|
||||
m_seed_nodes_initialized(false)
|
||||
|
@ -187,6 +230,7 @@ namespace nodetool
|
|||
m_bind_ipv6_address(),
|
||||
m_port(),
|
||||
m_port_ipv6(),
|
||||
m_ssl_finger(),
|
||||
m_notifier(),
|
||||
m_our_address(),
|
||||
m_peerlist(),
|
||||
|
@ -194,6 +238,7 @@ namespace nodetool
|
|||
m_proxy_address(),
|
||||
m_current_number_of_out_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_can_pingback(false),
|
||||
m_seed_nodes_initialized(false)
|
||||
|
@ -203,11 +248,12 @@ namespace nodetool
|
|||
|
||||
connect_func* m_connect;
|
||||
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_ipv6_address;
|
||||
std::string m_port;
|
||||
std::string m_port_ipv6;
|
||||
std::string m_ssl_finger;
|
||||
cryptonote::levin::notify m_notifier;
|
||||
epee::net_utils::network_address m_our_address; // in anonymity networks
|
||||
peerlist_manager m_peerlist;
|
||||
|
@ -215,6 +261,7 @@ namespace nodetool
|
|||
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_in_peers;
|
||||
epee::net_utils::ssl_support_t m_ssl_support;
|
||||
boost::shared_mutex m_seed_nodes_lock;
|
||||
bool m_can_pingback;
|
||||
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_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);
|
||||
bool is_peer_used(const 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);
|
||||
bool is_addr_recently_failed(const epee::net_utils::network_address& addr);
|
||||
bool is_priority_node(const epee::net_utils::network_address& na);
|
||||
std::set<std::string> get_ip_seed_nodes() const;
|
||||
std::set<std::string> get_dns_seed_nodes();
|
||||
std::set<std::string> get_seed_nodes(epee::net_utils::zone);
|
||||
std::set<string_address> get_ip_seed_nodes() const;
|
||||
std::set<string_address> get_dns_seed_nodes();
|
||||
std::set<string_address> get_seed_nodes(epee::net_utils::zone);
|
||||
bool connect_to_seed(epee::net_utils::zone);
|
||||
|
||||
template <class Container>
|
||||
|
@ -415,7 +462,7 @@ namespace nodetool
|
|||
size_t get_outgoing_connections_count();
|
||||
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 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<7000> m_dns_blocklist_interval;
|
||||
|
||||
std::list<epee::net_utils::network_address> m_priority_peers;
|
||||
std::vector<epee::net_utils::network_address> m_exclusive_peers;
|
||||
std::list<p2p_address> m_priority_peers;
|
||||
std::vector<p2p_address> m_exclusive_peers;
|
||||
std::atomic_flag m_fallback_seed_nodes_added;
|
||||
std::vector<nodetool::peerlist_entry> m_command_line_peers;
|
||||
uint64_t m_peer_livetime;
|
||||
//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> socks_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&, p2p_address const&);
|
||||
|
||||
|
||||
/* A `std::map` provides constant iterators and key/value pointers even with
|
||||
|
@ -510,8 +557,6 @@ namespace nodetool
|
|||
boost::uuids::uuid m_network_id;
|
||||
cryptonote::network_type m_nettype;
|
||||
|
||||
epee::net_utils::ssl_support_t m_ssl_support;
|
||||
|
||||
bool m_enable_dns_seed_nodes;
|
||||
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_no_sync;
|
||||
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<std::string> arg_igd;
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
#include "common/util.h"
|
||||
#include "common/dns_utils.h"
|
||||
#include "common/pruning.h"
|
||||
#include "hex.h"
|
||||
#include "net/error.h"
|
||||
#include "misc_log_ex.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
|
||||
{
|
||||
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>
|
||||
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>
|
||||
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_no_sync);
|
||||
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_igd);
|
||||
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 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;
|
||||
|
||||
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_use_ipv6 = command_line::get_arg(vm, arg_p2p_use_ipv6);
|
||||
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_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);
|
||||
pe.id = crypto::rand<uint64_t>();
|
||||
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)
|
||||
{
|
||||
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));
|
||||
continue;
|
||||
}
|
||||
|
@ -475,13 +494,13 @@ namespace nodetool
|
|||
adr == net::error::unsupported_address, false, "Bad address (\"" << pr_str << "\"): " << adr.error().message()
|
||||
);
|
||||
|
||||
std::vector<epee::net_utils::network_address> resolved_addrs;
|
||||
bool r = append_net_address(resolved_addrs, pr_str, default_port);
|
||||
std::vector<p2p_address> resolved_addrs;
|
||||
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);
|
||||
for (const epee::net_utils::network_address& addr : resolved_addrs)
|
||||
for (const p2p_address& peer : resolved_addrs)
|
||||
{
|
||||
pe.id = crypto::rand<uint64_t>();
|
||||
pe.adr = addr;
|
||||
peer.update_peer(pe);
|
||||
m_command_line_peers.push_back(pe);
|
||||
}
|
||||
}
|
||||
|
@ -605,7 +624,7 @@ namespace nodetool
|
|||
}
|
||||
|
||||
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(
|
||||
std::vector<epee::net_utils::network_address> & seed_nodes
|
||||
, std::string const & addr
|
||||
std::vector<p2p_address> & seed_nodes
|
||||
, string_address peer
|
||||
, uint16_t default_port
|
||||
)
|
||||
{
|
||||
|
@ -664,7 +683,7 @@ namespace nodetool
|
|||
// Split addr string into host string and port string
|
||||
std::string host;
|
||||
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);
|
||||
|
||||
io_service io_srv;
|
||||
|
@ -681,13 +700,13 @@ namespace nodetool
|
|||
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()}};
|
||||
seed_nodes.push_back(na);
|
||||
seed_nodes.push_back(p2p_address{na, peer.ssl});
|
||||
MINFO("Added node: " << na.str());
|
||||
}
|
||||
else
|
||||
{
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
@ -696,43 +715,44 @@ namespace nodetool
|
|||
|
||||
//-----------------------------------------------------------------------------------
|
||||
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)
|
||||
{
|
||||
full_addrs.insert("176.9.0.187:28080");
|
||||
full_addrs.insert("51.79.173.165:28080");
|
||||
full_addrs.insert("192.99.8.110:28080");
|
||||
full_addrs.insert("37.187.74.171:28080");
|
||||
full_addrs.insert("77.172.183.193:28080");
|
||||
full_addrs.insert(string_address{"176.9.0.187:28080", autodetect});
|
||||
full_addrs.insert(string_address{"51.79.173.165:28080", autodetect});
|
||||
full_addrs.insert(string_address{"192.99.8.110:28080", autodetect});
|
||||
full_addrs.insert(string_address{"37.187.74.171:28080", autodetect});
|
||||
full_addrs.insert(string_address{"77.172.183.193:28080", autodetect});
|
||||
}
|
||||
else if (m_nettype == cryptonote::STAGENET)
|
||||
{
|
||||
full_addrs.insert("176.9.0.187:38080");
|
||||
full_addrs.insert("51.79.173.165:38080");
|
||||
full_addrs.insert("192.99.8.110:38080");
|
||||
full_addrs.insert("37.187.74.171:38080");
|
||||
full_addrs.insert("77.172.183.193:38080");
|
||||
full_addrs.insert(string_address{"176.9.0.187:38080", autodetect});
|
||||
full_addrs.insert(string_address{"51.79.173.165:38080", autodetect});
|
||||
full_addrs.insert(string_address{"192.99.8.110:38080", autodetect});
|
||||
full_addrs.insert(string_address{"37.187.74.171:38080", autodetect});
|
||||
full_addrs.insert(string_address{"77.172.183.193:38080", autodetect});
|
||||
}
|
||||
else if (m_nettype == cryptonote::FAKECHAIN)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
full_addrs.insert("176.9.0.187:18080");
|
||||
full_addrs.insert("88.198.163.90:18080");
|
||||
full_addrs.insert("66.85.74.134:18080");
|
||||
full_addrs.insert("51.79.173.165:18080");
|
||||
full_addrs.insert("192.99.8.110:18080");
|
||||
full_addrs.insert("37.187.74.171:18080");
|
||||
full_addrs.insert("77.172.183.193:18080");
|
||||
full_addrs.insert(string_address{"176.9.0.187:18080", autodetect});
|
||||
full_addrs.insert(string_address{"88.198.163.90:18080", autodetect});
|
||||
full_addrs.insert(string_address{"66.85.74.134:18080", autodetect});
|
||||
full_addrs.insert(string_address{"51.79.173.165:18080", autodetect});
|
||||
full_addrs.insert(string_address{"192.99.8.110:18080", autodetect});
|
||||
full_addrs.insert(string_address{"37.187.74.171:18080", autodetect});
|
||||
full_addrs.insert(string_address{"77.172.183.193:18080", autodetect});
|
||||
}
|
||||
return full_addrs;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------
|
||||
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)
|
||||
{
|
||||
|
@ -753,7 +773,7 @@ namespace nodetool
|
|||
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
|
||||
// add the result addresses as seed nodes
|
||||
|
@ -825,7 +845,7 @@ namespace nodetool
|
|||
if (result.size())
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
@ -847,8 +867,10 @@ namespace nodetool
|
|||
}
|
||||
//-----------------------------------------------------------------------------------
|
||||
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)
|
||||
{
|
||||
case epee::net_utils::zone::public_:
|
||||
|
@ -856,23 +878,25 @@ namespace nodetool
|
|||
case epee::net_utils::zone::tor:
|
||||
if (m_nettype == cryptonote::MAINNET)
|
||||
{
|
||||
// tor hidden services are already e2e ssl encrypted
|
||||
return {
|
||||
"zbjkbsxc5munw3qusl7j2hpcmikhqocdf4pqhnhtpzw5nt5jrmofptid.onion:18083",
|
||||
"qz43zul2x56jexzoqgkx2trzwcfnr6l3hbtfcfx54g4r3eahy3bssjyd.onion:18083",
|
||||
"plowsof3t5hogddwabaeiyrno25efmzfxyro2vligremt7sxpsclfaid.onion:18083",
|
||||
"plowsoffjexmxalw73tkjmf422gq6575fc7vicuu4javzn2ynnte6tyd.onion:18083",
|
||||
"plowsofe6cleftfmk2raiw5h2x66atrik3nja4bfd3zrfa2hdlgworad.onion:18083",
|
||||
"aclc4e2jhhtr44guufbnwk5bzwhaecinax4yip4wr4tjn27sjsfg6zqd.onion:18083",
|
||||
string_address{"zbjkbsxc5munw3qusl7j2hpcmikhqocdf4pqhnhtpzw5nt5jrmofptid.onion:18083", no_encryption},
|
||||
string_address{"qz43zul2x56jexzoqgkx2trzwcfnr6l3hbtfcfx54g4r3eahy3bssjyd.onion:18083", no_encryption},
|
||||
string_address{"plowsof3t5hogddwabaeiyrno25efmzfxyro2vligremt7sxpsclfaid.onion:18083", no_encryption},
|
||||
string_address{"plowsoffjexmxalw73tkjmf422gq6575fc7vicuu4javzn2ynnte6tyd.onion:18083", no_encryption},
|
||||
string_address{"plowsofe6cleftfmk2raiw5h2x66atrik3nja4bfd3zrfa2hdlgworad.onion:18083", no_encryption},
|
||||
string_address{"aclc4e2jhhtr44guufbnwk5bzwhaecinax4yip4wr4tjn27sjsfg6zqd.onion:18083", no_encryption},
|
||||
};
|
||||
}
|
||||
return {};
|
||||
case epee::net_utils::zone::i2p:
|
||||
if (m_nettype == cryptonote::MAINNET)
|
||||
{
|
||||
// i2p services are already e2e aes noise encrypted
|
||||
return {
|
||||
"uqj3aphckqtjsitz7kxx5flqpwjlq5ppr3chazfued7xucv3nheq.b32.i2p",
|
||||
"vdmnehdjkpkg57nthgnjfuaqgku673r5bpbqg56ix6fyqoywgqrq.b32.i2p",
|
||||
"ugnlcdciyhghh2zert7c3kl4biwkirc43ke33jiy5slnd3mv2trq.b32.i2p",
|
||||
string_address{"uqj3aphckqtjsitz7kxx5flqpwjlq5ppr3chazfued7xucv3nheq.b32.i2p", no_encryption},
|
||||
string_address{"vdmnehdjkpkg57nthgnjfuaqgku673r5bpbqg56ix6fyqoywgqrq.b32.i2p", no_encryption},
|
||||
string_address{"ugnlcdciyhghh2zert7c3kl4biwkirc43ke33jiy5slnd3mv2trq.b32.i2p", no_encryption}
|
||||
};
|
||||
}
|
||||
return {};
|
||||
|
@ -956,7 +980,6 @@ namespace nodetool
|
|||
return res;
|
||||
|
||||
//try to bind
|
||||
m_ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_disabled;
|
||||
for (auto& zone : m_network_zones)
|
||||
{
|
||||
zone.second.m_net_server.get_config_object().set_handler(this);
|
||||
|
@ -974,8 +997,39 @@ namespace nodetool
|
|||
ipv6_port = 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");
|
||||
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)
|
||||
{
|
||||
network_zone& zone = m_network_zones.at(context_.m_remote_address.get_zone());
|
||||
|
||||
typename COMMAND_HANDSHAKE::request arg;
|
||||
typename COMMAND_HANDSHAKE::response rsp;
|
||||
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_credits_per_hash = rsp.node_data.rpc_credits_per_hash;
|
||||
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();
|
||||
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);
|
||||
|
@ -1363,13 +1418,13 @@ namespace nodetool
|
|||
} while(0)
|
||||
|
||||
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
|
||||
return false;
|
||||
|
||||
if (zone.m_our_address == na)
|
||||
if (zone.m_our_address == peer.na)
|
||||
return false;
|
||||
|
||||
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")
|
||||
<< ")...");
|
||||
|
||||
auto con = zone.m_connect(zone, na, m_ssl_support);
|
||||
auto con = zone.m_connect(zone, peer);
|
||||
if(!con)
|
||||
{
|
||||
bool is_priority = is_priority_node(na);
|
||||
LOG_PRINT_CC_PRIORITY_NODE(is_priority, bool(con), "Connect failed to " << na.str()
|
||||
bool is_priority = is_priority_node(peer.na);
|
||||
LOG_PRINT_CC_PRIORITY_NODE(is_priority, bool(con), "Connect failed to " << peer.na.str()
|
||||
/*<< ", try " << try_count*/);
|
||||
record_addr_failed(na);
|
||||
record_addr_failed(peer.na);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1404,11 +1459,11 @@ namespace nodetool
|
|||
|
||||
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()
|
||||
<< peer.na.str()
|
||||
/*<< ", try " << try_count*/);
|
||||
record_addr_failed(na);
|
||||
record_addr_failed(peer.na);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1420,7 +1475,7 @@ namespace nodetool
|
|||
}
|
||||
|
||||
peerlist_entry pe_local = AUTO_VAL_INIT(pe_local);
|
||||
pe_local.adr = na;
|
||||
pe_local.adr = peer.na;
|
||||
pe_local.id = pi;
|
||||
time_t last_seen;
|
||||
time(&last_seen);
|
||||
|
@ -1428,13 +1483,17 @@ namespace nodetool
|
|||
pe_local.pruning_seed = con->m_pruning_seed;
|
||||
pe_local.rpc_port = con->m_rpc_port;
|
||||
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);
|
||||
//update last seen and push it to peerlist manager
|
||||
|
||||
anchor_peerlist_entry ape = AUTO_VAL_INIT(ape);
|
||||
ape.adr = na;
|
||||
ape.adr = peer.na;
|
||||
ape.id = pi;
|
||||
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_notifier.on_handshake_complete(con->m_connection_id, con->m_is_income);
|
||||
|
@ -1445,22 +1504,22 @@ namespace nodetool
|
|||
}
|
||||
|
||||
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)
|
||||
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")
|
||||
<< ")...");
|
||||
|
||||
auto con = zone.m_connect(zone, na, m_ssl_support);
|
||||
auto con = zone.m_connect(zone, peer);
|
||||
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());
|
||||
record_addr_failed(na);
|
||||
LOG_PRINT_CC_PRIORITY_NODE(is_priority, p2p_connection_context{}, "Connect failed to " << peer.na.str());
|
||||
record_addr_failed(peer.na);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -1469,10 +1528,10 @@ namespace nodetool
|
|||
peerid_type pi = AUTO_VAL_INIT(pi);
|
||||
const bool res = do_handshake_with_peer(pi, *con, true);
|
||||
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());
|
||||
record_addr_failed(na);
|
||||
LOG_PRINT_CC_PRIORITY_NODE(is_priority, *con, "Failed to HANDSHAKE with peer " << peer.na.str());
|
||||
record_addr_failed(peer.na);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1530,7 +1589,7 @@ namespace nodetool
|
|||
<< "[peer_type=" << anchor
|
||||
<< "] 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");
|
||||
continue;
|
||||
}
|
||||
|
@ -1719,7 +1778,7 @@ namespace nodetool
|
|||
<< "[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"));
|
||||
|
||||
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");
|
||||
continue;
|
||||
}
|
||||
|
@ -1743,8 +1802,8 @@ namespace nodetool
|
|||
for (const auto& full_addr : get_seed_nodes(zone))
|
||||
{
|
||||
// seeds should have hostname converted to IP already
|
||||
MDEBUG("Seed node: " << full_addr);
|
||||
server.m_seed_nodes.push_back(MONERO_UNWRAP(net::get_network_address(full_addr, default_port)));
|
||||
MDEBUG("Seed node: " << full_addr.address);
|
||||
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());
|
||||
}
|
||||
|
@ -1761,7 +1820,7 @@ namespace nodetool
|
|||
return false;
|
||||
|
||||
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))
|
||||
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))
|
||||
|
@ -1778,7 +1837,7 @@ namespace nodetool
|
|||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -2206,6 +2265,8 @@ namespace nodetool
|
|||
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.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;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------
|
||||
|
@ -2225,7 +2286,14 @@ namespace nodetool
|
|||
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)
|
||||
{
|
||||
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());
|
||||
auto zone = m_network_zones.begin();
|
||||
for(const auto& c_id: connections)
|
||||
|
@ -2317,7 +2385,7 @@ namespace nodetool
|
|||
return false;
|
||||
|
||||
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;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------
|
||||
|
@ -2371,6 +2439,7 @@ namespace nodetool
|
|||
{
|
||||
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;
|
||||
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,
|
||||
|
@ -2421,7 +2490,7 @@ namespace nodetool
|
|||
return false;
|
||||
}
|
||||
return true;
|
||||
}, "0.0.0.0", m_ssl_support);
|
||||
}, "0.0.0.0", get_p2p_encryption(node_data));
|
||||
if(!r)
|
||||
{
|
||||
LOG_WARNING_CC(context, "Failed to call connect_async, network error.");
|
||||
|
@ -2498,7 +2567,7 @@ namespace nodetool
|
|||
real peer with that identity banned/blacklisted. */
|
||||
|
||||
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");
|
||||
return 1;
|
||||
|
@ -2578,7 +2647,7 @@ namespace nodetool
|
|||
peerid_type peer_id_l = arg.node_data.peer_id;
|
||||
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(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(),
|
||||
"Only IPv4 or IPv6 addresses are supported here");
|
||||
|
@ -2600,6 +2669,8 @@ namespace nodetool
|
|||
pe.pruning_seed = context.m_pruning_seed;
|
||||
pe.rpc_port = context.m_rpc_port;
|
||||
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);
|
||||
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>
|
||||
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>
|
||||
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_);
|
||||
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())
|
||||
return false;
|
||||
|
||||
if(is_addr_connected(na))
|
||||
if(is_addr_connected(peer.na))
|
||||
continue;
|
||||
|
||||
try_to_connect_and_handshake_with_new_peer(na);
|
||||
try_to_connect_and_handshake_with_new_peer(peer);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -2725,19 +2796,20 @@ namespace nodetool
|
|||
for(const std::string& pr_str: perrs)
|
||||
{
|
||||
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)
|
||||
{
|
||||
add_zone(adr->get_zone());
|
||||
container.push_back(std::move(*adr));
|
||||
container.push_back(p2p_address{std::move(*adr), std::move(peer.ssl)});
|
||||
continue;
|
||||
}
|
||||
std::vector<epee::net_utils::network_address> resolved_addrs;
|
||||
bool r = append_net_address(resolved_addrs, pr_str, default_port);
|
||||
std::vector<p2p_address> resolved_addrs;
|
||||
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);
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2916,8 +2988,8 @@ namespace nodetool
|
|||
peerlist_entry pe{};
|
||||
if (!zone.second.m_peerlist.get_random_gray_peer(pe))
|
||||
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);
|
||||
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>
|
||||
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
|
||||
{
|
||||
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 boost::none;
|
||||
|
@ -3116,10 +3188,10 @@ namespace nodetool
|
|||
|
||||
template<typename t_payload_net_handler>
|
||||
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_ipv6 = na.get_type_id() == epee::net_utils::ipv6_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 = peer.na.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id();
|
||||
CHECK_AND_ASSERT_MES(is_ipv4 || is_ipv6, boost::none,
|
||||
"Only IPv4 or IPv6 addresses are supported here");
|
||||
|
||||
|
@ -3128,13 +3200,13 @@ namespace nodetool
|
|||
|
||||
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());
|
||||
port = epee::string_tools::num_to_string_fast(ipv4.port());
|
||||
}
|
||||
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();
|
||||
port = epee::string_tools::num_to_string_fast(ipv6.port());
|
||||
}
|
||||
|
@ -3144,10 +3216,14 @@ namespace nodetool
|
|||
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{};
|
||||
const bool res = zone.m_net_server.connect(address, port,
|
||||
zone.m_config.m_net_config.connection_timeout,
|
||||
con, "0.0.0.0", ssl_support);
|
||||
con, "0.0.0.0", ssl);
|
||||
|
||||
if (res)
|
||||
return {std::move(con)};
|
||||
|
|
|
@ -396,20 +396,30 @@ namespace nodetool
|
|||
|
||||
//update gray list
|
||||
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())
|
||||
{
|
||||
//put new record into white list
|
||||
m_peers_gray.insert(ple);
|
||||
// Do not trust encryption information from 3rd parties
|
||||
new_ple.encryption_mode = emode_ssl_autodetect;
|
||||
new_ple.cert_finger.clear();
|
||||
|
||||
m_peers_gray.insert(new_ple);
|
||||
trim_gray_peerlist();
|
||||
}else
|
||||
{
|
||||
//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
|
||||
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
|
||||
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
|
||||
|
||||
// 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);
|
||||
}
|
||||
return true;
|
||||
|
@ -420,7 +430,6 @@ namespace nodetool
|
|||
bool peerlist_manager::append_with_peer_anchor(const anchor_peerlist_entry& ple)
|
||||
{
|
||||
TRY_ENTRY();
|
||||
|
||||
CRITICAL_REGION_LOCAL(m_peerlist_lock);
|
||||
|
||||
auto by_addr_it_anchor = m_peers_anchor.get<by_addr>().find(ple.adr);
|
||||
|
|
|
@ -38,7 +38,8 @@
|
|||
#include "net/i2p_address.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
|
||||
{
|
||||
|
@ -238,6 +239,18 @@ namespace boost
|
|||
return;
|
||||
}
|
||||
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>
|
||||
|
@ -246,6 +259,17 @@ namespace boost
|
|||
a & pl.adr;
|
||||
a & pl.id;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,10 +33,12 @@
|
|||
#include <iomanip>
|
||||
#include <boost/uuid/uuid.hpp>
|
||||
#include <boost/serialization/version.hpp>
|
||||
#include <boost/variant/variant.hpp>
|
||||
#include "serialization/keyvalue_serialization.h"
|
||||
#include "net/net_utils_base.h"
|
||||
#include "net/tor_address.h" // needed for serialization
|
||||
#include "net/i2p_address.h" // needed for serialization
|
||||
#include "net/net_ssl.h"
|
||||
#include "misc_language.h"
|
||||
#include "string_tools.h"
|
||||
#include "time_helper.h"
|
||||
|
@ -48,6 +50,11 @@ namespace nodetool
|
|||
typedef boost::uuids::uuid uuid;
|
||||
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)
|
||||
{
|
||||
std::ostringstream s;
|
||||
|
@ -55,6 +62,48 @@ namespace nodetool
|
|||
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)
|
||||
|
||||
struct network_address_old
|
||||
|
@ -72,11 +121,13 @@ namespace nodetool
|
|||
struct peerlist_entry_base
|
||||
{
|
||||
AddressType adr;
|
||||
std::string cert_finger;
|
||||
peerid_type id;
|
||||
int64_t last_seen;
|
||||
uint32_t pruning_seed;
|
||||
uint16_t rpc_port;
|
||||
uint32_t rpc_credits_per_hash;
|
||||
uint8_t encryption_mode;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(adr)
|
||||
|
@ -85,16 +136,9 @@ namespace nodetool
|
|||
KV_SERIALIZE_OPT(pruning_seed, (uint32_t)0)
|
||||
KV_SERIALIZE_OPT(rpc_port, (uint16_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()
|
||||
|
||||
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;
|
||||
|
||||
|
@ -102,44 +146,21 @@ namespace nodetool
|
|||
struct anchor_peerlist_entry_base
|
||||
{
|
||||
AddressType adr;
|
||||
std::string cert_finger;
|
||||
peerid_type id;
|
||||
int64_t first_seen;
|
||||
uint8_t encryption_mode;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(adr)
|
||||
KV_SERIALIZE(id)
|
||||
KV_SERIALIZE(first_seen)
|
||||
KV_SERIALIZE_OPT(encryption_mode, emode_ssl_autodetect)
|
||||
KV_SERIALIZE_OPT(cert_finger, std::string{})
|
||||
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;
|
||||
|
||||
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)
|
||||
|
||||
inline
|
||||
|
@ -184,12 +205,14 @@ namespace nodetool
|
|||
|
||||
struct basic_node_data
|
||||
{
|
||||
uuid network_id;
|
||||
std::string cert_finger;
|
||||
uuid network_id;
|
||||
uint32_t my_port;
|
||||
uint16_t rpc_port;
|
||||
uint32_t rpc_credits_per_hash;
|
||||
peerid_type peer_id;
|
||||
uint32_t support_flags;
|
||||
uint8_t encryption_mode;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
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_credits_per_hash, (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()
|
||||
};
|
||||
|
||||
|
|
|
@ -56,9 +56,9 @@ monerod_base = [builddir + "/bin/monerod", "--regtest", "--fixed-difficulty", st
|
|||
monerod_extra = [
|
||||
["--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:18282"],
|
||||
["--rpc-login", "md5_lover:Z1ON0101", "--offline"],
|
||||
["--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:18284"],
|
||||
["--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_extra = [
|
||||
|
|
|
@ -45,6 +45,7 @@ class P2PTest():
|
|||
self.mine(80)
|
||||
self.test_p2p_reorg()
|
||||
self.test_p2p_tx_propagation()
|
||||
self.test_p2p_ssl()
|
||||
|
||||
def reset(self):
|
||||
print('Resetting blockchain')
|
||||
|
@ -78,6 +79,8 @@ class P2PTest():
|
|||
daemon3 = Daemon(idx = 3)
|
||||
|
||||
# give sync some time
|
||||
daemon2.stop_mining()
|
||||
daemon3.stop_mining()
|
||||
time.sleep(1)
|
||||
|
||||
res = daemon2.get_info()
|
||||
|
@ -168,6 +171,8 @@ class P2PTest():
|
|||
res = daemon.get_transaction_pool_hashes()
|
||||
assert not 'tx_hashes' in res or len(res.tx_hashes) == 0
|
||||
|
||||
daemon2.stop_mining()
|
||||
daemon3.stop_mining()
|
||||
self.wallet.refresh()
|
||||
res = self.wallet.get_balance()
|
||||
|
||||
|
@ -176,13 +181,36 @@ class P2PTest():
|
|||
assert len(res.tx_hash) == 32*2
|
||||
txid = res.tx_hash
|
||||
|
||||
time.sleep(5)
|
||||
time.sleep(45)
|
||||
|
||||
for daemon in [daemon2, daemon3]:
|
||||
res = daemon.get_transaction_pool_hashes()
|
||||
assert len(res.tx_hashes) == 1
|
||||
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__':
|
||||
P2PTest().run_test()
|
||||
|
|
|
@ -47,7 +47,7 @@ namespace net_load_tests
|
|||
{
|
||||
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 bool handshake_complete() noexcept { return true; }
|
||||
size_t get_max_bytes(int command) const { return LEVIN_DEFAULT_MAX_PACKET_SIZE; }
|
||||
|
|
|
@ -372,14 +372,14 @@ TEST_F(positive_test_connection_to_levin_protocol_handler_calls, handler_process
|
|||
epee::levin::bucket_head2 resp_head;
|
||||
ASSERT_LT(sizeof(resp_head), send_data.size());
|
||||
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
|
||||
ASSERT_EQ(expected_out_data, out_data);
|
||||
ASSERT_EQ(LEVIN_SIGNATURE, SWAP64LE(resp_head.m_signature));
|
||||
ASSERT_EQ(expected_command, SWAP32LE(resp_head.m_command));
|
||||
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_EQ(SWAP32LE(LEVIN_PROTOCOL_VER_1), resp_head.m_protocol_version);
|
||||
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));
|
||||
|
||||
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();
|
||||
|
||||
|
@ -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);
|
||||
|
||||
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));
|
||||
|
||||
EXPECT_EQ(5u, fragmented.size() / 1024);
|
||||
|
|
|
@ -177,7 +177,7 @@ namespace
|
|||
handler_(std::addressof(endpoint_), connections, context_)
|
||||
{
|
||||
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;
|
||||
handler_.after_init_connection();
|
||||
}
|
||||
|
@ -397,7 +397,7 @@ TEST(make_header, expect_return)
|
|||
|
||||
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 =
|
||||
epee::levin::make_header(443, 0, LEVIN_PACKET_REQUEST, true);
|
||||
ASSERT_EQ(sizeof(header), message.size());
|
||||
|
@ -412,7 +412,7 @@ TEST(message_writer, invoke_with_payload)
|
|||
epee::levin::message_writer writer{};
|
||||
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 =
|
||||
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)
|
||||
{
|
||||
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 =
|
||||
epee::levin::make_header(443, 0, LEVIN_PACKET_REQUEST, false);
|
||||
ASSERT_EQ(sizeof(header), message.size());
|
||||
|
@ -438,7 +438,7 @@ TEST(message_writer, notify_with_payload)
|
|||
epee::levin::message_writer writer{};
|
||||
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 =
|
||||
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)
|
||||
{
|
||||
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::make_header(443, 0, LEVIN_PACKET_RESPONSE, false);
|
||||
header.m_return_code = SWAP32LE(1);
|
||||
|
@ -465,7 +465,7 @@ TEST(message_writer, response_with_payload)
|
|||
epee::levin::message_writer writer{};
|
||||
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::make_header(443, bytes.size(), LEVIN_PACKET_RESPONSE, false);
|
||||
header.m_return_code = SWAP32LE(6450);
|
||||
|
@ -480,9 +480,9 @@ TEST(message_writer, error)
|
|||
epee::levin::message_writer writer{};
|
||||
writer.buffer.clear();
|
||||
|
||||
EXPECT_THROW(writer.finalize_invoke(0), std::runtime_error);
|
||||
EXPECT_THROW(writer.finalize_notify(0), std::runtime_error);
|
||||
EXPECT_THROW(writer.finalize_response(0, 0), std::runtime_error);
|
||||
EXPECT_THROW(writer.finalize_invoke(0, false), std::runtime_error);
|
||||
EXPECT_THROW(writer.finalize_notify(0, false), std::runtime_error);
|
||||
EXPECT_THROW(writer.finalize_response(0, 0, false), std::runtime_error);
|
||||
}
|
||||
|
||||
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;
|
||||
EXPECT_EQ(txs, notification.txs);
|
||||
EXPECT_FALSE(notification._.empty());
|
||||
EXPECT_TRUE(notification._.empty());
|
||||
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;
|
||||
EXPECT_EQ(txs, notification.txs);
|
||||
EXPECT_FALSE(notification._.empty());
|
||||
EXPECT_TRUE(notification._.empty());
|
||||
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;
|
||||
EXPECT_EQ(sorted_txs, notification.txs);
|
||||
EXPECT_FALSE(notification._.empty());
|
||||
EXPECT_TRUE(notification._.empty());
|
||||
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;
|
||||
EXPECT_EQ(their_txs, notification.txs);
|
||||
EXPECT_FALSE(notification._.empty());
|
||||
EXPECT_TRUE(notification._.empty());
|
||||
EXPECT_EQ(!is_stem, notification.dandelionpp_fluff);
|
||||
}
|
||||
|
||||
|
@ -1294,7 +1294,7 @@ TEST_F(levin_notify, local_with_padding)
|
|||
EXPECT_EQ(1u, receiver_.notified_size());
|
||||
auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
|
||||
EXPECT_EQ(my_txs, notification.txs);
|
||||
EXPECT_FALSE(notification._.empty());
|
||||
EXPECT_TRUE(notification._.empty());
|
||||
EXPECT_TRUE(!notification.dandelionpp_fluff);
|
||||
|
||||
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;
|
||||
EXPECT_EQ(txs, notification.txs);
|
||||
EXPECT_FALSE(notification._.empty());
|
||||
EXPECT_TRUE(notification._.empty());
|
||||
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;
|
||||
EXPECT_EQ(txs, notification.txs);
|
||||
EXPECT_FALSE(notification._.empty());
|
||||
EXPECT_TRUE(notification._.empty());
|
||||
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;
|
||||
EXPECT_EQ(txs, notification.txs);
|
||||
EXPECT_FALSE(notification._.empty());
|
||||
EXPECT_TRUE(notification._.empty());
|
||||
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;
|
||||
EXPECT_EQ(txs, notification.txs);
|
||||
EXPECT_FALSE(notification._.empty());
|
||||
EXPECT_TRUE(notification._.empty());
|
||||
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;
|
||||
EXPECT_EQ(txs, notification.txs);
|
||||
EXPECT_FALSE(notification._.empty());
|
||||
EXPECT_TRUE(notification._.empty());
|
||||
EXPECT_TRUE(notification.dandelionpp_fluff);
|
||||
}
|
||||
}
|
||||
|
@ -2385,7 +2385,7 @@ TEST_F(levin_notify, command_max_bytes)
|
|||
{
|
||||
epee::levin::message_writer dest{};
|
||||
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()));
|
||||
|
@ -2401,7 +2401,7 @@ TEST_F(levin_notify, command_max_bytes)
|
|||
payload.push_back('h');
|
||||
epee::levin::message_writer dest{};
|
||||
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()));
|
||||
|
|
|
@ -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 {
|
||||
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
|
||||
return {};
|
||||
}
|
||||
virtual bool relay_notify_to_list(int command, epee::levin::message_writer in, connections_t connections) override {
|
||||
if (shared_state) {
|
||||
for (auto &e: connections)
|
||||
shared_state->send(in.finalize_notify(command), e.second);
|
||||
shared_state->send(in.finalize_notify(command, true), e.second);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
@ -1090,6 +1090,7 @@ TEST(node_server, race_condition)
|
|||
conn->get_context(context);
|
||||
event_t handshaked;
|
||||
typename messages::handshake::request_t msg{{
|
||||
std::string{},
|
||||
::config::NETWORK_ID,
|
||||
58080,
|
||||
}};
|
||||
|
|
|
@ -111,13 +111,13 @@ TEST(peerlist_storage, store)
|
|||
std::string buffer{};
|
||||
{
|
||||
nodetool::peerlist_types types{};
|
||||
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.gray.push_back({net::tor_address::unknown(), 99, 88});
|
||||
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({net::tor_address::unknown(), 14, 33});
|
||||
types.anchor.push_back({net::tor_address::unknown(), 24, 22});
|
||||
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.gray.push_back({net::tor_address::unknown(), {}, 99, 88});
|
||||
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({net::tor_address::unknown(), {}, 14, 33});
|
||||
types.anchor.push_back({net::tor_address::unknown(), {}, 24, 22});
|
||||
|
||||
std::ostringstream stream{};
|
||||
EXPECT_TRUE(peers.store(stream, types));
|
||||
|
|
Loading…
Reference in New Issue