Skip to content

Commit eb47b3f

Browse files
committed
Add SSL support to P2P
1 parent 389e3ba commit eb47b3f

29 files changed

+747
-306
lines changed

contrib/epee/include/net/abstract_tcp_server2.h

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -314,8 +314,19 @@ namespace net_utils
314314

315315

316316
bool speed_limit_is_enabled() const; ///< tells us should we be sleeping here (e.g. do not sleep on RPC connections)
317-
317+
void set_ssl_enabled()
318+
{
319+
m_state.ssl.enabled = true;
320+
m_state.ssl.handshaked = true;
321+
}
318322
bool cancel();
323+
324+
//! Used by boosted_tcp_server class in async_connect_internal
325+
template<typename F>
326+
auto wrap(F&& f)
327+
{
328+
return boost::asio::bind_executor(m_strand, std::forward<F>(f));
329+
}
319330

320331
private:
321332
//----------------- i_service_endpoint ---------------------
@@ -396,10 +407,11 @@ namespace net_utils
396407
}
397408

398409
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);
399-
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);
400-
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);
410+
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);
411+
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);
401412
template<class t_callback>
402-
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);
413+
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);
414+
403415

404416
boost::asio::ssl::context& get_ssl_context() noexcept
405417
{
@@ -497,6 +509,11 @@ namespace net_utils
497509

498510
bool is_thread_worker();
499511

512+
template<typename t_callback>
513+
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);
514+
515+
bool remove_connection(const connection_ptr& ptr);
516+
500517
const std::shared_ptr<typename connection<t_protocol_handler>::shared_state> m_state;
501518

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

contrib/epee/include/net/abstract_tcp_server2.inl

Lines changed: 110 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -935,7 +935,7 @@ namespace net_utils
935935
boost::uuids::random_generator()(),
936936
*real_remote,
937937
is_income,
938-
connection_basic::m_ssl_support == ssl_support_t::e_ssl_support_enabled
938+
connection_basic::m_ssl_support
939939
);
940940
m_host = real_remote->host_str();
941941
try { host_count(1); } catch(...) { /* ignore */ }
@@ -1624,7 +1624,7 @@ namespace net_utils
16241624
}
16251625
//---------------------------------------------------------------------------------
16261626
template<class t_protocol_handler>
1627-
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)
1627+
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)
16281628
{
16291629
TRY_ENTRY();
16301630

@@ -1700,7 +1700,7 @@ namespace net_utils
17001700
{
17011701
// Handshake
17021702
MDEBUG("Handshaking SSL...");
1703-
if (!new_connection_l->handshake(boost::asio::ssl::stream_base::client))
1703+
if (!new_connection_l->client_handshake(ssl_options))
17041704
{
17051705
if (ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect)
17061706
{
@@ -1714,6 +1714,7 @@ namespace net_utils
17141714
sock_.close();
17151715
return CONNECT_FAILURE;
17161716
}
1717+
new_connection_l->set_ssl_enabled();
17171718
}
17181719

17191720
return CONNECT_SUCCESS;
@@ -1722,11 +1723,11 @@ namespace net_utils
17221723
}
17231724
//---------------------------------------------------------------------------------
17241725
template<class t_protocol_handler>
1725-
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)
1726+
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)
17261727
{
17271728
TRY_ENTRY();
17281729

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

1817-
auto try_connect_result = try_connect(new_connection_l, adr, port, sock_, remote_endpoint, bind_ip_to_use, conn_timeout, ssl_support);
1818+
auto try_connect_result = try_connect(new_connection_l, adr, port, sock_, remote_endpoint, bind_ip_to_use, conn_timeout, ssl_options);
18181819
if (try_connect_result == CONNECT_FAILURE)
18191820
return false;
1820-
if (ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect && try_connect_result == CONNECT_NO_SSL)
1821+
if (ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect && try_connect_result == CONNECT_NO_SSL)
18211822
{
18221823
// we connected, but could not connect with SSL, try without
18231824
MERROR("SSL handshake failed on an autodetect connection, reconnecting without SSL");
18241825
new_connection_l->disable_ssl();
1825-
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);
1826+
ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_disabled;
1827+
try_connect_result = try_connect(new_connection_l, adr, port, sock_, remote_endpoint, bind_ip_to_use, conn_timeout, ssl_options);
18261828
if (try_connect_result != CONNECT_SUCCESS)
18271829
return false;
18281830
}
18291831

18301832
// start adds the connection to the config object's list, so we don't need to have it locally anymore
1831-
connections_mutex.lock();
1832-
connections_.erase(new_connection_l);
1833-
connections_mutex.unlock();
1834-
bool r = new_connection_l->start(false, 1 < m_threads_count);
1833+
bool r = remove_connection(new_connection_l) && new_connection_l->start(false, 1 < m_threads_count);
18351834
if (r)
18361835
{
18371836
new_connection_l->get_context(conn_context);
@@ -1850,10 +1849,10 @@ namespace net_utils
18501849
}
18511850
//---------------------------------------------------------------------------------
18521851
template<class t_protocol_handler> template<class t_callback>
1853-
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)
1852+
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)
18541853
{
18551854
TRY_ENTRY();
1856-
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_context_, m_state, m_connection_type, ssl_support) );
1855+
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_context_, m_state, m_connection_type, ssl_options.support) );
18571856
connections_mutex.lock();
18581857
connections_.insert(new_connection_l);
18591858
MDEBUG("connections_ size now " << connections_.size());
@@ -1930,60 +1929,113 @@ namespace net_utils
19301929
return false;
19311930
}
19321931
}
1933-
1934-
boost::shared_ptr<boost::asio::deadline_timer> sh_deadline(new boost::asio::deadline_timer(io_context_));
1935-
//start deadline
1936-
sh_deadline->expires_from_now(boost::posix_time::milliseconds(conn_timeout));
1937-
sh_deadline->async_wait([=](const boost::system::error_code& error)
1932+
1933+
ssl_options.configure(new_connection_l->socket_, boost::asio::ssl::stream_base::client);
1934+
return connect_async_internal(new_connection_l, remote_endpoint, conn_timeout, cb);
1935+
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::connect_async", false);
1936+
}
1937+
1938+
template<class t_protocol_handler> template<class t_callback>
1939+
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)
1940+
{
1941+
if (!new_connection_l)
1942+
return false;
1943+
1944+
TRY_ENTRY();
1945+
1946+
const auto on_timer = [=](boost::system::error_code error)
19381947
{
1939-
if(error != boost::asio::error::operation_aborted)
1940-
{
1941-
_dbg3("Failed to connect to " << adr << ':' << port << ", because of timeout (" << conn_timeout << ")");
1942-
new_connection_l->socket().close();
1943-
}
1944-
});
1945-
//start async connect
1946-
sock_.async_connect(remote_endpoint, [=](const boost::system::error_code& ec_)
1948+
if (error != boost::asio::error::operation_aborted)
1949+
{
1950+
_dbg3("Failed to connect to " << remote_endpoint << ", because of timeout (" << conn_timeout << ')');
1951+
new_connection_l->socket().close(error); // ignore errors
1952+
}
1953+
};
1954+
1955+
auto sh_deadline = std::make_shared<boost::asio::deadline_timer>(io_context_);
1956+
sh_deadline->expires_from_now(boost::posix_time::milliseconds(conn_timeout));
1957+
sh_deadline->async_wait(new_connection_l->wrap(on_timer));
1958+
1959+
new_connection_l->socket().async_connect(remote_endpoint, new_connection_l->wrap([=](const boost::system::error_code& ec_)
19471960
{
1948-
t_connection_context conn_context = AUTO_VAL_INIT(conn_context);
1949-
boost::system::error_code ignored_ec;
1950-
boost::asio::ip::tcp::socket::endpoint_type lep = new_connection_l->socket().local_endpoint(ignored_ec);
1961+
const auto on_cancel = [=](const boost::system::error_code& error)
1962+
{
1963+
boost::system::error_code ignored_ec{};
1964+
const auto lep = new_connection_l->socket().local_endpoint(ignored_ec);
1965+
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] to " << remote_endpoint << " from " << lep << " failed: " << error.message());
1966+
if (remove_connection(new_connection_l))
1967+
cb(t_connection_context{}, error);
1968+
};
1969+
19511970
if(!ec_)
19521971
{//success
1953-
if(!sh_deadline->cancel())
1954-
{
1955-
cb(conn_context, boost::asio::error::operation_aborted);//this mean that deadline timer already queued callback with cancel operation, rare situation
1956-
}else
1957-
{
1958-
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] Connected success to " << adr << ':' << port <<
1959-
" from " << lep.address().to_string() << ':' << lep.port());
1960-
1961-
// start adds the connection to the config object's list, so we don't need to have it locally anymore
1962-
connections_mutex.lock();
1963-
connections_.erase(new_connection_l);
1964-
connections_mutex.unlock();
1965-
bool r = new_connection_l->start(false, 1 < m_threads_count);
1966-
if (r)
1972+
const auto on_ready = [=] ()
19671973
{
1968-
new_connection_l->get_context(conn_context);
1969-
cb(conn_context, ec_);
1970-
}
1971-
else
1974+
if (sh_deadline->cancel())
1975+
{
1976+
boost::system::error_code ignored_ec{};
1977+
const auto lep = new_connection_l->socket().local_endpoint(ignored_ec);
1978+
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] Connected successfully to " << remote_endpoint <<
1979+
" from " << lep.address().to_string() << ':' << lep.port());
1980+
1981+
if (remove_connection(new_connection_l) && new_connection_l->start(false, 1 < m_threads_count))
1982+
{
1983+
t_connection_context conn_context{};
1984+
new_connection_l->get_context(conn_context);
1985+
cb(conn_context, ec_);
1986+
}
1987+
else
1988+
on_cancel(boost::asio::error::fault);
1989+
}
1990+
else // if timer already expired
1991+
on_cancel(boost::asio::error::operation_aborted);
1992+
};
1993+
1994+
if (new_connection_l->get_ssl_support() != ssl_support_t::e_ssl_support_disabled)
1995+
{
1996+
// set new timer for handshake
1997+
if (sh_deadline->expires_from_now(boost::posix_time::milliseconds(conn_timeout)))
19721998
{
1973-
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] Failed to start connection to " << adr << ':' << port);
1974-
cb(conn_context, boost::asio::error::fault);
1999+
sh_deadline->async_wait(new_connection_l->wrap(on_timer));
2000+
new_connection_l->socket_.async_handshake(boost::asio::ssl::stream_base::client, new_connection_l->wrap([=] (const boost::system::error_code& ec)
2001+
{
2002+
if (ec)
2003+
{
2004+
if (new_connection_l->get_ssl_support() == ssl_support_t::e_ssl_support_autodetect)
2005+
{
2006+
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] SSL connection to " <<
2007+
remote_endpoint << " failed: " << ec.message() << ". Trying without SSL");
2008+
new_connection_l->disable_ssl();
2009+
connect_async_internal(new_connection_l, remote_endpoint, conn_timeout, cb);
2010+
}
2011+
else // ssl mandatory and failed
2012+
on_cancel(ec);
2013+
}
2014+
else // ssl handshake complete
2015+
on_ready();
2016+
}));
19752017
}
2018+
else // if timer already expired
2019+
on_cancel(boost::asio::error::operation_aborted);
19762020
}
1977-
}else
1978-
{
1979-
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] Failed to connect to " << adr << ':' << port <<
1980-
" from " << lep.address().to_string() << ':' << lep.port() << ": " << ec_.message() << ':' << ec_.value());
1981-
cb(conn_context, ec_);
2021+
else // ssl disabled
2022+
on_ready();
19822023
}
1983-
});
2024+
else // ec_ has error
2025+
on_cancel(ec_);
2026+
}));
2027+
return true;
2028+
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::connect_async_internal", false);
2029+
}
2030+
2031+
template<class t_protocol_handler>
2032+
bool boosted_tcp_server<t_protocol_handler>::remove_connection(const connection_ptr& new_connection)
2033+
{
2034+
if (!new_connection)
2035+
return false;
2036+
const boost::lock_guard<boost::mutex> sync{connections_mutex};
2037+
connections_.erase(new_connection);
19842038
return true;
1985-
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::connect_async", false);
19862039
}
1987-
19882040
} // namespace
19892041
} // namespace

contrib/epee/include/net/connection_basic.hpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,15 @@ 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)
135+
{
136+
return ssl.handshake(strand_.context(), socket_, boost::asio::ssl::stream_base::client);
137+
}
138+
139+
bool server_handshake(boost::asio::const_buffer buffer)
135140
{
136141
//m_state != nullptr verified in constructor
137-
return m_state->ssl_options().handshake(strand_.context(), socket_, type, buffer);
142+
return m_state->ssl_options().handshake(strand_.context(), socket_, boost::asio::ssl::stream_base::server, buffer);
138143
}
139144

140145
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
@@ -130,7 +130,7 @@ namespace levin
130130
//! Provides space for levin (p2p) header, so that payload can be sent without copy
131131
class message_writer
132132
{
133-
byte_slice finalize(uint32_t command, uint32_t flags, uint32_t return_code, bool expect_response);
133+
byte_slice finalize(uint32_t command, uint32_t flags, uint32_t return_code, bool expect_response, bool pad);
134134
public:
135135
using header = bucket_head2;
136136

@@ -147,12 +147,13 @@ namespace levin
147147
{
148148
return buffer.size() < sizeof(header) ? 0 : buffer.size() - sizeof(header);
149149
}
150-
151-
byte_slice finalize_invoke(uint32_t command) { return finalize(command, LEVIN_PACKET_REQUEST, 0, true); }
152-
byte_slice finalize_notify(uint32_t command) { return finalize(command, LEVIN_PACKET_REQUEST, 0, false); }
153-
byte_slice finalize_response(uint32_t command, uint32_t return_code)
150+
151+
// `pad == true` will add 0-8192 of zero bytes (actual amount randomized)
152+
byte_slice finalize_invoke(uint32_t command, bool pad) { return finalize(command, LEVIN_PACKET_REQUEST, 0, true, pad); }
153+
byte_slice finalize_notify(uint32_t command, bool pad) { return finalize(command, LEVIN_PACKET_REQUEST, 0, false, pad); }
154+
byte_slice finalize_response(uint32_t command, uint32_t return_code, bool pad)
154155
{
155-
return finalize(command, LEVIN_PACKET_RESPONSE, return_code, false);
156+
return finalize(command, LEVIN_PACKET_RESPONSE, return_code, false, pad);
156157
}
157158

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

0 commit comments

Comments
 (0)