Skip to content

Commit 0b18c43

Browse files
committed
Add SSL support to P2P
1 parent 8d6aff9 commit 0b18c43

29 files changed

+748
-307
lines changed

contrib/epee/include/net/abstract_tcp_server2.h

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
@file
2+
afile
33
@author from CrypoNote (see copyright below; Andrey N. Sabelnikov)
44
@monero rfree
55
@brief the connection templated-class for one peer connection
@@ -140,6 +140,7 @@ namespace net_utils
140140
void terminate();
141141
void on_terminating();
142142

143+
143144
bool send(epee::byte_slice message);
144145
bool start_internal(
145146
bool is_income,
@@ -314,8 +315,19 @@ namespace net_utils
314315

315316

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

320332
private:
321333
//----------------- i_service_endpoint ---------------------
@@ -396,10 +408,11 @@ namespace net_utils
396408
}
397409

398410
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);
411+
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);
412+
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);
401413
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);
414+
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);
415+
403416

404417
boost::asio::ssl::context& get_ssl_context() noexcept
405418
{
@@ -497,6 +510,11 @@ namespace net_utils
497510

498511
bool is_thread_worker();
499512

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

502520
/// 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
@@ -933,7 +933,7 @@ namespace net_utils
933933
boost::uuids::random_generator()(),
934934
*real_remote,
935935
is_income,
936-
connection_basic::m_ssl_support == ssl_support_t::e_ssl_support_enabled
936+
connection_basic::m_ssl_support
937937
);
938938
m_host = real_remote->host_str();
939939
try { host_count(1); } catch(...) { /* ignore */ }
@@ -1618,7 +1618,7 @@ namespace net_utils
16181618
}
16191619
//---------------------------------------------------------------------------------
16201620
template<class t_protocol_handler>
1621-
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)
1621+
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)
16221622
{
16231623
TRY_ENTRY();
16241624

@@ -1694,7 +1694,7 @@ namespace net_utils
16941694
{
16951695
// Handshake
16961696
MDEBUG("Handshaking SSL...");
1697-
if (!new_connection_l->handshake(boost::asio::ssl::stream_base::client))
1697+
if (!new_connection_l->client_handshake(ssl_options))
16981698
{
16991699
if (ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect)
17001700
{
@@ -1708,6 +1708,7 @@ namespace net_utils
17081708
sock_.close();
17091709
return CONNECT_FAILURE;
17101710
}
1711+
new_connection_l->set_ssl_enabled();
17111712
}
17121713

17131714
return CONNECT_SUCCESS;
@@ -1716,11 +1717,11 @@ namespace net_utils
17161717
}
17171718
//---------------------------------------------------------------------------------
17181719
template<class t_protocol_handler>
1719-
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)
1720+
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)
17201721
{
17211722
TRY_ENTRY();
17221723

1723-
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_context_, m_state, m_connection_type, ssl_support) );
1724+
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_context_, m_state, m_connection_type, ssl_options.support) );
17241725
connections_mutex.lock();
17251726
connections_.insert(new_connection_l);
17261727
MDEBUG("connections_ size now " << connections_.size());
@@ -1808,24 +1809,22 @@ namespace net_utils
18081809
//boost::asio::ip::tcp::endpoint remote_endpoint(boost::asio::ip::address::from_string(addr.c_str()), port);
18091810
boost::asio::ip::tcp::endpoint remote_endpoint(*iterator);
18101811

1811-
auto try_connect_result = try_connect(new_connection_l, adr, port, sock_, remote_endpoint, bind_ip_to_use, conn_timeout, ssl_support);
1812+
auto try_connect_result = try_connect(new_connection_l, adr, port, sock_, remote_endpoint, bind_ip_to_use, conn_timeout, ssl_options);
18121813
if (try_connect_result == CONNECT_FAILURE)
18131814
return false;
1814-
if (ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect && try_connect_result == CONNECT_NO_SSL)
1815+
if (ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect && try_connect_result == CONNECT_NO_SSL)
18151816
{
18161817
// we connected, but could not connect with SSL, try without
18171818
MERROR("SSL handshake failed on an autodetect connection, reconnecting without SSL");
18181819
new_connection_l->disable_ssl();
1819-
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);
1820+
ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_disabled;
1821+
try_connect_result = try_connect(new_connection_l, adr, port, sock_, remote_endpoint, bind_ip_to_use, conn_timeout, ssl_options);
18201822
if (try_connect_result != CONNECT_SUCCESS)
18211823
return false;
18221824
}
18231825

18241826
// start adds the connection to the config object's list, so we don't need to have it locally anymore
1825-
connections_mutex.lock();
1826-
connections_.erase(new_connection_l);
1827-
connections_mutex.unlock();
1828-
bool r = new_connection_l->start(false, 1 < m_threads_count);
1827+
bool r = remove_connection(new_connection_l) && new_connection_l->start(false, 1 < m_threads_count);
18291828
if (r)
18301829
{
18311830
new_connection_l->get_context(conn_context);
@@ -1844,10 +1843,10 @@ namespace net_utils
18441843
}
18451844
//---------------------------------------------------------------------------------
18461845
template<class t_protocol_handler> template<class t_callback>
1847-
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)
1846+
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)
18481847
{
18491848
TRY_ENTRY();
1850-
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_context_, m_state, m_connection_type, ssl_support) );
1849+
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_context_, m_state, m_connection_type, ssl_options.support) );
18511850
connections_mutex.lock();
18521851
connections_.insert(new_connection_l);
18531852
MDEBUG("connections_ size now " << connections_.size());
@@ -1924,60 +1923,113 @@ namespace net_utils
19241923
return false;
19251924
}
19261925
}
1927-
1928-
boost::shared_ptr<boost::asio::deadline_timer> sh_deadline(new boost::asio::deadline_timer(io_context_));
1929-
//start deadline
1930-
sh_deadline->expires_from_now(boost::posix_time::milliseconds(conn_timeout));
1931-
sh_deadline->async_wait([=](const boost::system::error_code& error)
1926+
1927+
ssl_options.configure(new_connection_l->socket_, boost::asio::ssl::stream_base::client);
1928+
return connect_async_internal(new_connection_l, remote_endpoint, conn_timeout, cb);
1929+
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::connect_async", false);
1930+
}
1931+
1932+
template<class t_protocol_handler> template<class t_callback>
1933+
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)
1934+
{
1935+
if (!new_connection_l)
1936+
return false;
1937+
1938+
TRY_ENTRY();
1939+
1940+
const auto on_timer = [=](boost::system::error_code error)
19321941
{
1933-
if(error != boost::asio::error::operation_aborted)
1934-
{
1935-
_dbg3("Failed to connect to " << adr << ':' << port << ", because of timeout (" << conn_timeout << ")");
1936-
new_connection_l->socket().close();
1937-
}
1938-
});
1939-
//start async connect
1940-
sock_.async_connect(remote_endpoint, [=](const boost::system::error_code& ec_)
1942+
if (error != boost::asio::error::operation_aborted)
1943+
{
1944+
_dbg3("Failed to connect to " << remote_endpoint << ", because of timeout (" << conn_timeout << ')');
1945+
new_connection_l->socket().close(error); // ignore errors
1946+
}
1947+
};
1948+
1949+
auto sh_deadline = std::make_shared<boost::asio::deadline_timer>(io_context_);
1950+
sh_deadline->expires_from_now(boost::posix_time::milliseconds(conn_timeout));
1951+
sh_deadline->async_wait(new_connection_l->wrap(on_timer));
1952+
1953+
new_connection_l->socket().async_connect(remote_endpoint, new_connection_l->wrap([=](const boost::system::error_code& ec_)
19411954
{
1942-
t_connection_context conn_context = AUTO_VAL_INIT(conn_context);
1943-
boost::system::error_code ignored_ec;
1944-
boost::asio::ip::tcp::socket::endpoint_type lep = new_connection_l->socket().local_endpoint(ignored_ec);
1955+
const auto on_cancel = [=](const boost::system::error_code& error)
1956+
{
1957+
boost::system::error_code ignored_ec{};
1958+
const auto lep = new_connection_l->socket().local_endpoint(ignored_ec);
1959+
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] to " << remote_endpoint << " from " << lep << " failed: " << error.message());
1960+
if (remove_connection(new_connection_l))
1961+
cb(t_connection_context{}, error);
1962+
};
1963+
19451964
if(!ec_)
19461965
{//success
1947-
if(!sh_deadline->cancel())
1948-
{
1949-
cb(conn_context, boost::asio::error::operation_aborted);//this mean that deadline timer already queued callback with cancel operation, rare situation
1950-
}else
1951-
{
1952-
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] Connected success to " << adr << ':' << port <<
1953-
" from " << lep.address().to_string() << ':' << lep.port());
1954-
1955-
// start adds the connection to the config object's list, so we don't need to have it locally anymore
1956-
connections_mutex.lock();
1957-
connections_.erase(new_connection_l);
1958-
connections_mutex.unlock();
1959-
bool r = new_connection_l->start(false, 1 < m_threads_count);
1960-
if (r)
1966+
const auto on_ready = [=] ()
19611967
{
1962-
new_connection_l->get_context(conn_context);
1963-
cb(conn_context, ec_);
1964-
}
1965-
else
1968+
if (sh_deadline->cancel())
1969+
{
1970+
boost::system::error_code ignored_ec{};
1971+
const auto lep = new_connection_l->socket().local_endpoint(ignored_ec);
1972+
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] Connected successfully to " << remote_endpoint <<
1973+
" from " << lep.address().to_string() << ':' << lep.port());
1974+
1975+
if (remove_connection(new_connection_l) && new_connection_l->start(false, 1 < m_threads_count))
1976+
{
1977+
t_connection_context conn_context{};
1978+
new_connection_l->get_context(conn_context);
1979+
cb(conn_context, ec_);
1980+
}
1981+
else
1982+
on_cancel(boost::asio::error::fault);
1983+
}
1984+
else // if timer already expired
1985+
on_cancel(boost::asio::error::operation_aborted);
1986+
};
1987+
1988+
if (new_connection_l->get_ssl_support() != ssl_support_t::e_ssl_support_disabled)
1989+
{
1990+
// set new timer for handshake
1991+
if (sh_deadline->expires_from_now(boost::posix_time::milliseconds(conn_timeout)))
19661992
{
1967-
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] Failed to start connection to " << adr << ':' << port);
1968-
cb(conn_context, boost::asio::error::fault);
1993+
sh_deadline->async_wait(new_connection_l->wrap(on_timer));
1994+
new_connection_l->socket_.async_handshake(boost::asio::ssl::stream_base::client, new_connection_l->wrap([=] (const boost::system::error_code& ec)
1995+
{
1996+
if (ec)
1997+
{
1998+
if (new_connection_l->get_ssl_support() == ssl_support_t::e_ssl_support_autodetect)
1999+
{
2000+
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] SSL connection to " <<
2001+
remote_endpoint << " failed: " << ec.message() << ". Trying without SSL");
2002+
new_connection_l->disable_ssl();
2003+
connect_async_internal(new_connection_l, remote_endpoint, conn_timeout, cb);
2004+
}
2005+
else // ssl mandatory and failed
2006+
on_cancel(ec);
2007+
}
2008+
else // ssl handshake complete
2009+
on_ready();
2010+
}));
19692011
}
2012+
else // if timer already expired
2013+
on_cancel(boost::asio::error::operation_aborted);
19702014
}
1971-
}else
1972-
{
1973-
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] Failed to connect to " << adr << ':' << port <<
1974-
" from " << lep.address().to_string() << ':' << lep.port() << ": " << ec_.message() << ':' << ec_.value());
1975-
cb(conn_context, ec_);
2015+
else // ssl disabled
2016+
on_ready();
19762017
}
1977-
});
2018+
else // ec_ has error
2019+
on_cancel(ec_);
2020+
}));
2021+
return true;
2022+
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::connect_async_internal", false);
2023+
}
2024+
2025+
template<class t_protocol_handler>
2026+
bool boosted_tcp_server<t_protocol_handler>::remove_connection(const connection_ptr& new_connection)
2027+
{
2028+
if (!new_connection)
2029+
return false;
2030+
const boost::lock_guard<boost::mutex> sync{connections_mutex};
2031+
connections_.erase(new_connection);
19782032
return true;
1979-
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::connect_async", false);
19802033
}
1981-
19822034
} // namespace
19832035
} // 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)