Skip to content

Commit 23eff78

Browse files
committed
Add SSL support to P2P
1 parent 5b2e404 commit 23eff78

30 files changed

Lines changed: 765 additions & 284 deletions

contrib/epee/include/net/abstract_tcp_server2.h

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -319,8 +319,19 @@ namespace net_utils
319319

320320

321321
bool speed_limit_is_enabled() const; ///< tells us should we be sleeping here (e.g. do not sleep on RPC connections)
322-
322+
void set_ssl_enabled()
323+
{
324+
m_state.ssl.enabled = true;
325+
m_state.ssl.handshaked = true;
326+
}
323327
bool cancel();
328+
329+
//! Used by boosted_tcp_server class in async_connect_internal
330+
template<typename F>
331+
auto wrap(F&& f)
332+
{
333+
return boost::asio::bind_executor(m_strand, std::forward<F>(f));
334+
}
324335

325336
private:
326337
//----------------- i_service_endpoint ---------------------
@@ -401,10 +412,10 @@ namespace net_utils
401412
}
402413

403414
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);
404-
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);
405-
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);
415+
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);
416+
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);
406417
template<class t_callback>
407-
bool connect_async(const std::string& adr, const std::string& port, std::chrono::milliseconds conn_timeout, 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, t_connection_context&& initial = t_connection_context{});
418+
bool connect_async(const std::string& adr, const std::string& port, std::chrono::milliseconds 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, t_connection_context&& initial = t_connection_context{});
408419

409420
boost::asio::ssl::context& get_ssl_context() noexcept
410421
{
@@ -503,6 +514,11 @@ namespace net_utils
503514

504515
bool is_thread_worker();
505516

517+
template<typename t_callback>
518+
bool connect_async_internal(const connection_ptr& new_connection_l, const boost::asio::ip::tcp::endpoint& remote_endpoint, std::chrono::milliseconds conn_timeout, const t_callback &cb);
519+
520+
bool remove_connection(const connection_ptr& ptr);
521+
506522
const std::shared_ptr<typename connection<t_protocol_handler>::shared_state> m_state;
507523

508524
/// The io_context used to perform asynchronous operations.

contrib/epee/include/net/abstract_tcp_server2.inl

Lines changed: 114 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -982,7 +982,7 @@ namespace net_utils
982982
boost::uuids::random_generator()(),
983983
*real_remote,
984984
is_income,
985-
connection_basic::m_ssl_support == ssl_support_t::e_ssl_support_enabled
985+
connection_basic::m_ssl_support
986986
);
987987
m_host = real_remote->host_str();
988988
try { host_count(1); } catch(...) { /* ignore */ }
@@ -1709,7 +1709,7 @@ namespace net_utils
17091709
}
17101710
//---------------------------------------------------------------------------------
17111711
template<class t_protocol_handler>
1712-
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)
1712+
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)
17131713
{
17141714
TRY_ENTRY();
17151715

@@ -1785,7 +1785,7 @@ namespace net_utils
17851785
{
17861786
// Handshake
17871787
MDEBUG("Handshaking SSL...");
1788-
if (!new_connection_l->handshake(boost::asio::ssl::stream_base::client))
1788+
if (!new_connection_l->client_handshake(ssl_options))
17891789
{
17901790
if (ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect)
17911791
{
@@ -1799,6 +1799,7 @@ namespace net_utils
17991799
sock_.close();
18001800
return CONNECT_FAILURE;
18011801
}
1802+
new_connection_l->set_ssl_enabled();
18021803
}
18031804

18041805
return CONNECT_SUCCESS;
@@ -1807,11 +1808,11 @@ namespace net_utils
18071808
}
18081809
//---------------------------------------------------------------------------------
18091810
template<class t_protocol_handler>
1810-
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)
1811+
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)
18111812
{
18121813
TRY_ENTRY();
18131814

1814-
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_context_, m_state, m_connection_type, ssl_support) );
1815+
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_context_, m_state, m_connection_type, ssl_options.support) );
18151816
connections_mutex.lock();
18161817
connections_.insert(new_connection_l);
18171818
MDEBUG("connections_ size now " << connections_.size());
@@ -1899,24 +1900,22 @@ namespace net_utils
18991900
//boost::asio::ip::tcp::endpoint remote_endpoint(boost::asio::ip::address::from_string(addr.c_str()), port);
19001901
boost::asio::ip::tcp::endpoint remote_endpoint(*iterator);
19011902

1902-
auto try_connect_result = try_connect(new_connection_l, adr, port, sock_, remote_endpoint, bind_ip_to_use, conn_timeout, ssl_support);
1903+
auto try_connect_result = try_connect(new_connection_l, adr, port, sock_, remote_endpoint, bind_ip_to_use, conn_timeout, ssl_options);
19031904
if (try_connect_result == CONNECT_FAILURE)
19041905
return false;
1905-
if (ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect && try_connect_result == CONNECT_NO_SSL)
1906+
if (ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect && try_connect_result == CONNECT_NO_SSL)
19061907
{
19071908
// we connected, but could not connect with SSL, try without
19081909
MERROR("SSL handshake failed on an autodetect connection, reconnecting without SSL");
19091910
new_connection_l->disable_ssl();
1910-
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);
1911+
ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_disabled;
1912+
try_connect_result = try_connect(new_connection_l, adr, port, sock_, remote_endpoint, bind_ip_to_use, conn_timeout, ssl_options);
19111913
if (try_connect_result != CONNECT_SUCCESS)
19121914
return false;
19131915
}
19141916

19151917
// start adds the connection to the config object's list, so we don't need to have it locally anymore
1916-
connections_mutex.lock();
1917-
connections_.erase(new_connection_l);
1918-
connections_mutex.unlock();
1919-
bool r = new_connection_l->start(false, 1 < m_threads_count);
1918+
bool r = remove_connection(new_connection_l) && new_connection_l->start(false, 1 < m_threads_count);
19201919
if (r)
19211920
{
19221921
new_connection_l->get_context(conn_context);
@@ -1935,10 +1934,10 @@ namespace net_utils
19351934
}
19361935
//---------------------------------------------------------------------------------
19371936
template<class t_protocol_handler> template<class t_callback>
1938-
bool boosted_tcp_server<t_protocol_handler>::connect_async(const std::string& adr, const std::string& port, const std::chrono::milliseconds conn_timeout, const t_callback &cb, const std::string& bind_ip, epee::net_utils::ssl_support_t ssl_support, t_connection_context&& initial)
1937+
bool boosted_tcp_server<t_protocol_handler>::connect_async(const std::string& adr, const std::string& port, const std::chrono::milliseconds conn_timeout, const t_callback &cb, const std::string& bind_ip, epee::net_utils::ssl_options_t ssl_options, t_connection_context&& initial)
19391938
{
19401939
TRY_ENTRY();
1941-
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_context_, m_state, m_connection_type, ssl_support, std::move(initial)) );
1940+
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_context_, m_state, m_connection_type, ssl_options.support, std::move(initial)) );
19421941
connections_mutex.lock();
19431942
connections_.insert(new_connection_l);
19441943
MDEBUG("connections_ size now " << connections_.size());
@@ -2015,60 +2014,117 @@ namespace net_utils
20152014
return false;
20162015
}
20172016
}
2018-
2019-
std::shared_ptr<boost::asio::steady_timer> sh_deadline(std::make_shared<boost::asio::steady_timer>(io_context_));
2020-
//start deadline
2021-
sh_deadline->expires_after(conn_timeout);
2022-
sh_deadline->async_wait([=](const boost::system::error_code& error)
2017+
2018+
ssl_options.configure(new_connection_l->socket_, boost::asio::ssl::stream_base::client);
2019+
return connect_async_internal(new_connection_l, remote_endpoint, conn_timeout, cb);
2020+
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::connect_async", false);
2021+
}
2022+
2023+
template<class t_protocol_handler> template<class t_callback>
2024+
bool boosted_tcp_server<t_protocol_handler>::connect_async_internal(const connection_ptr& new_connection_l, const boost::asio::ip::tcp::endpoint& remote_endpoint, const std::chrono::milliseconds conn_timeout, const t_callback &cb)
2025+
{
2026+
if (!new_connection_l)
2027+
return false;
2028+
2029+
TRY_ENTRY();
2030+
2031+
const auto on_timer = [=](boost::system::error_code error)
20232032
{
2024-
if(error != boost::asio::error::operation_aborted)
2025-
{
2026-
_dbg3("Failed to connect to " << adr << ':' << port << ", because of timeout (" << conn_timeout.count() << ")");
2027-
new_connection_l->socket().close();
2028-
}
2029-
});
2030-
//start async connect
2031-
sock_.async_connect(remote_endpoint, [=](const boost::system::error_code& ec_)
2033+
if (error != boost::asio::error::operation_aborted)
2034+
{
2035+
_dbg3("Failed to connect to " << remote_endpoint << ", because of timeout (" << conn_timeout.count() << " ms)");
2036+
new_connection_l->socket().close(error); // ignore errors
2037+
}
2038+
};
2039+
2040+
auto sh_deadline = std::make_shared<boost::asio::steady_timer>(io_context_);
2041+
sh_deadline->expires_after(conn_timeout);
2042+
sh_deadline->async_wait(new_connection_l->wrap(on_timer));
2043+
2044+
new_connection_l->socket().async_connect(remote_endpoint, new_connection_l->wrap([=](const boost::system::error_code& ec_)
20322045
{
2033-
t_connection_context conn_context = AUTO_VAL_INIT(conn_context);
2034-
boost::system::error_code ignored_ec;
2035-
boost::asio::ip::tcp::socket::endpoint_type lep = new_connection_l->socket().local_endpoint(ignored_ec);
2046+
const auto on_cancel = [=](const boost::system::error_code& error)
2047+
{
2048+
boost::system::error_code ignored_ec{};
2049+
const auto lep = new_connection_l->socket().local_endpoint(ignored_ec);
2050+
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] to " << remote_endpoint << " from " << lep << " failed: " << error.message());
2051+
if (remove_connection(new_connection_l))
2052+
cb(t_connection_context{}, error);
2053+
};
2054+
20362055
if(!ec_)
20372056
{//success
2038-
if(!sh_deadline->cancel())
2039-
{
2040-
cb(conn_context, boost::asio::error::operation_aborted);//this mean that deadline timer already queued callback with cancel operation, rare situation
2041-
}else
2042-
{
2043-
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] Connected success to " << adr << ':' << port <<
2044-
" from " << lep.address().to_string() << ':' << lep.port());
2045-
2046-
// start adds the connection to the config object's list, so we don't need to have it locally anymore
2047-
connections_mutex.lock();
2048-
connections_.erase(new_connection_l);
2049-
connections_mutex.unlock();
2050-
bool r = new_connection_l->start(false, 1 < m_threads_count);
2051-
if (r)
2057+
const auto on_ready = [=] ()
20522058
{
2053-
new_connection_l->get_context(conn_context);
2054-
cb(conn_context, ec_);
2055-
}
2056-
else
2059+
if (sh_deadline->cancel())
2060+
{
2061+
boost::system::error_code ignored_ec{};
2062+
const auto lep = new_connection_l->socket().local_endpoint(ignored_ec);
2063+
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] Connected successfully to " << remote_endpoint <<
2064+
" from " << lep.address().to_string() << ':' << lep.port());
2065+
2066+
if (remove_connection(new_connection_l) && new_connection_l->start(false, 1 < m_threads_count))
2067+
{
2068+
t_connection_context conn_context{};
2069+
new_connection_l->get_context(conn_context);
2070+
cb(conn_context, ec_);
2071+
}
2072+
else
2073+
on_cancel(boost::asio::error::fault);
2074+
}
2075+
else // if timer already expired
2076+
on_cancel(boost::asio::error::operation_aborted);
2077+
};
2078+
2079+
if (new_connection_l->get_ssl_support() != ssl_support_t::e_ssl_support_disabled)
2080+
{
2081+
// set new timer for handshake
2082+
if (sh_deadline->expires_after(conn_timeout))
20572083
{
2058-
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] Failed to start connection to " << adr << ':' << port);
2059-
cb(conn_context, boost::asio::error::fault);
2084+
sh_deadline->async_wait(new_connection_l->wrap(on_timer));
2085+
new_connection_l->socket_.async_handshake(boost::asio::ssl::stream_base::client, new_connection_l->wrap([=] (const boost::system::error_code& ec)
2086+
{
2087+
if (ec)
2088+
{
2089+
sh_deadline->cancel();
2090+
if (new_connection_l->get_ssl_support() == ssl_support_t::e_ssl_support_autodetect)
2091+
{
2092+
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] SSL connection to " <<
2093+
remote_endpoint << " failed: " << ec.message() << ". Trying without SSL");
2094+
new_connection_l->disable_ssl();
2095+
connect_async_internal(new_connection_l, remote_endpoint, conn_timeout, cb);
2096+
}
2097+
else // ssl mandatory and failed
2098+
on_cancel(ec);
2099+
}
2100+
else // ssl handshake complete
2101+
{
2102+
new_connection_l->set_ssl_enabled();
2103+
on_ready();
2104+
}
2105+
}));
20602106
}
2107+
else // if timer already expired
2108+
on_cancel(boost::asio::error::operation_aborted);
20612109
}
2062-
}else
2063-
{
2064-
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] Failed to connect to " << adr << ':' << port <<
2065-
" from " << lep.address().to_string() << ':' << lep.port() << ": " << ec_.message() << ':' << ec_.value());
2066-
cb(conn_context, ec_);
2110+
else // ssl disabled
2111+
on_ready();
20672112
}
2068-
});
2113+
else // ec_ has error
2114+
on_cancel(ec_);
2115+
}));
2116+
return true;
2117+
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::connect_async_internal", false);
2118+
}
2119+
2120+
template<class t_protocol_handler>
2121+
bool boosted_tcp_server<t_protocol_handler>::remove_connection(const connection_ptr& new_connection)
2122+
{
2123+
if (!new_connection)
2124+
return false;
2125+
const boost::lock_guard<boost::mutex> sync{connections_mutex};
2126+
connections_.erase(new_connection);
20692127
return true;
2070-
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::connect_async", false);
20712128
}
2072-
20732129
} // namespace
20742130
} // namespace

contrib/epee/include/net/connection_basic.hpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,9 @@ class connection_basic { // not-templated base class for rapid developmet of som
131131
ssl_support_t get_ssl_support() const { return m_ssl_support; }
132132
void disable_ssl() { m_ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_disabled; }
133133

134-
bool handshake(boost::asio::ssl::stream_base::handshake_type type, boost::asio::const_buffer buffer = {})
134+
bool client_handshake(ssl_options_t& ssl)
135135
{
136-
//m_state != nullptr verified in constructor
137-
return m_state->ssl_options().handshake(strand_.context(), socket_, type, buffer);
136+
return ssl.handshake(strand_.context(), socket_, boost::asio::ssl::stream_base::client);
138137
}
139138

140139
template<typename MutableBufferSequence, typename ReadHandler>

contrib/epee/include/net/levin_base.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ constexpr const std::chrono::milliseconds LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED{0}
131131
//! Provides space for levin (p2p) header, so that payload can be sent without copy
132132
class message_writer
133133
{
134-
byte_slice finalize(uint32_t command, uint32_t flags, uint32_t return_code, bool expect_response);
134+
byte_slice finalize(uint32_t command, uint32_t flags, uint32_t return_code, bool expect_response, bool pad);
135135
public:
136136
using header = bucket_head2;
137137

@@ -148,12 +148,13 @@ constexpr const std::chrono::milliseconds LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED{0}
148148
{
149149
return buffer.size() < sizeof(header) ? 0 : buffer.size() - sizeof(header);
150150
}
151-
152-
byte_slice finalize_invoke(uint32_t command) { return finalize(command, LEVIN_PACKET_REQUEST, 0, true); }
153-
byte_slice finalize_notify(uint32_t command) { return finalize(command, LEVIN_PACKET_REQUEST, 0, false); }
154-
byte_slice finalize_response(uint32_t command, uint32_t return_code)
151+
152+
// `pad == true` will add 0-8191 of zero bytes (actual amount randomized)
153+
byte_slice finalize_invoke(uint32_t command, bool pad) { return finalize(command, LEVIN_PACKET_REQUEST, 0, true, pad); }
154+
byte_slice finalize_notify(uint32_t command, bool pad) { return finalize(command, LEVIN_PACKET_REQUEST, 0, false, pad); }
155+
byte_slice finalize_response(uint32_t command, uint32_t return_code, bool pad)
155156
{
156-
return finalize(command, LEVIN_PACKET_RESPONSE, return_code, false);
157+
return finalize(command, LEVIN_PACKET_RESPONSE, return_code, false, pad);
157158
}
158159

159160
//! Has space for levin header until a finalize method is used

contrib/epee/include/net/levin_protocol_handler_async.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,7 @@ class async_protocol_handler
534534
if (m_current_head.m_command == m_connection_context.handshake_command() && m_connection_context.handshake_complete())
535535
m_max_packet_size = m_config.m_max_packet_size;
536536

537-
if(!send_message(return_message.finalize_response(m_current_head.m_command, return_code)))
537+
if(!send_message(return_message.finalize_response(m_current_head.m_command, return_code, m_connection_context.should_pad())))
538538
return false;
539539
}
540540
else
@@ -628,7 +628,7 @@ class async_protocol_handler
628628
if (command == m_connection_context.handshake_command())
629629
m_max_packet_size = m_config.m_max_packet_size;
630630

631-
if(!send_message(in_msg.finalize_invoke(command)))
631+
if(!send_message(in_msg.finalize_invoke(command, m_connection_context.should_pad())))
632632
{
633633
LOG_ERROR_CC(m_connection_context, "Failed to do_send");
634634
err_code = LEVIN_ERROR_CONNECTION;

0 commit comments

Comments
 (0)