Skip to content

Commit bb1eaab

Browse files
committed
Add SSL support to P2P
1 parent 41ad523 commit bb1eaab

29 files changed

+745
-305
lines changed

contrib/epee/include/net/abstract_tcp_server2.h

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

317317

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

322333
private:
323334
//----------------- i_service_endpoint ---------------------
@@ -398,10 +409,10 @@ namespace net_utils
398409
}
399410

400411
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);
401-
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);
402-
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);
412+
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);
413+
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);
403414
template<class t_callback>
404-
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, t_connection_context&& initial = t_connection_context{});
415+
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, t_connection_context&& initial = t_connection_context{});
405416

406417
boost::asio::ssl::context& get_ssl_context() noexcept
407418
{
@@ -499,6 +510,11 @@ namespace net_utils
499510

500511
bool is_thread_worker();
501512

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+
502518
const std::shared_ptr<typename connection<t_protocol_handler>::shared_state> m_state;
503519

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

contrib/epee/include/net/abstract_tcp_server2.inl

Lines changed: 111 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 */ }
@@ -1628,7 +1628,7 @@ namespace net_utils
16281628
}
16291629
//---------------------------------------------------------------------------------
16301630
template<class t_protocol_handler>
1631-
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)
1631+
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)
16321632
{
16331633
TRY_ENTRY();
16341634

@@ -1704,7 +1704,7 @@ namespace net_utils
17041704
{
17051705
// Handshake
17061706
MDEBUG("Handshaking SSL...");
1707-
if (!new_connection_l->handshake(boost::asio::ssl::stream_base::client))
1707+
if (!new_connection_l->client_handshake(ssl_options))
17081708
{
17091709
if (ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect)
17101710
{
@@ -1718,6 +1718,7 @@ namespace net_utils
17181718
sock_.close();
17191719
return CONNECT_FAILURE;
17201720
}
1721+
new_connection_l->set_ssl_enabled();
17211722
}
17221723

17231724
return CONNECT_SUCCESS;
@@ -1726,11 +1727,11 @@ namespace net_utils
17261727
}
17271728
//---------------------------------------------------------------------------------
17281729
template<class t_protocol_handler>
1729-
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)
1730+
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)
17301731
{
17311732
TRY_ENTRY();
17321733

1733-
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_context_, m_state, m_connection_type, ssl_support) );
1734+
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_context_, m_state, m_connection_type, ssl_options.support) );
17341735
connections_mutex.lock();
17351736
connections_.insert(new_connection_l);
17361737
MDEBUG("connections_ size now " << connections_.size());
@@ -1818,24 +1819,22 @@ namespace net_utils
18181819
//boost::asio::ip::tcp::endpoint remote_endpoint(boost::asio::ip::address::from_string(addr.c_str()), port);
18191820
boost::asio::ip::tcp::endpoint remote_endpoint(*iterator);
18201821

1821-
auto try_connect_result = try_connect(new_connection_l, adr, port, sock_, remote_endpoint, bind_ip_to_use, conn_timeout, ssl_support);
1822+
auto try_connect_result = try_connect(new_connection_l, adr, port, sock_, remote_endpoint, bind_ip_to_use, conn_timeout, ssl_options);
18221823
if (try_connect_result == CONNECT_FAILURE)
18231824
return false;
1824-
if (ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect && try_connect_result == CONNECT_NO_SSL)
1825+
if (ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect && try_connect_result == CONNECT_NO_SSL)
18251826
{
18261827
// we connected, but could not connect with SSL, try without
18271828
MERROR("SSL handshake failed on an autodetect connection, reconnecting without SSL");
18281829
new_connection_l->disable_ssl();
1829-
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);
1830+
ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_disabled;
1831+
try_connect_result = try_connect(new_connection_l, adr, port, sock_, remote_endpoint, bind_ip_to_use, conn_timeout, ssl_options);
18301832
if (try_connect_result != CONNECT_SUCCESS)
18311833
return false;
18321834
}
18331835

18341836
// start adds the connection to the config object's list, so we don't need to have it locally anymore
1835-
connections_mutex.lock();
1836-
connections_.erase(new_connection_l);
1837-
connections_mutex.unlock();
1838-
bool r = new_connection_l->start(false, 1 < m_threads_count);
1837+
bool r = remove_connection(new_connection_l) && new_connection_l->start(false, 1 < m_threads_count);
18391838
if (r)
18401839
{
18411840
new_connection_l->get_context(conn_context);
@@ -1854,10 +1853,10 @@ namespace net_utils
18541853
}
18551854
//---------------------------------------------------------------------------------
18561855
template<class t_protocol_handler> template<class t_callback>
1857-
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, t_connection_context&& initial)
1856+
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, t_connection_context&& initial)
18581857
{
18591858
TRY_ENTRY();
1860-
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_context_, m_state, m_connection_type, ssl_support, std::move(initial)) );
1859+
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_context_, m_state, m_connection_type, ssl_options.support, std::move(initial)) );
18611860
connections_mutex.lock();
18621861
connections_.insert(new_connection_l);
18631862
MDEBUG("connections_ size now " << connections_.size());
@@ -1934,60 +1933,114 @@ namespace net_utils
19341933
return false;
19351934
}
19361935
}
1937-
1938-
boost::shared_ptr<boost::asio::deadline_timer> sh_deadline(new boost::asio::deadline_timer(io_context_));
1939-
//start deadline
1940-
sh_deadline->expires_from_now(boost::posix_time::milliseconds(conn_timeout));
1941-
sh_deadline->async_wait([=](const boost::system::error_code& error)
1936+
1937+
ssl_options.configure(new_connection_l->socket_, boost::asio::ssl::stream_base::client);
1938+
return connect_async_internal(new_connection_l, remote_endpoint, conn_timeout, cb);
1939+
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::connect_async", false);
1940+
}
1941+
1942+
template<class t_protocol_handler> template<class t_callback>
1943+
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)
1944+
{
1945+
if (!new_connection_l)
1946+
return false;
1947+
1948+
TRY_ENTRY();
1949+
1950+
const auto on_timer = [=](boost::system::error_code error)
19421951
{
1943-
if(error != boost::asio::error::operation_aborted)
1944-
{
1945-
_dbg3("Failed to connect to " << adr << ':' << port << ", because of timeout (" << conn_timeout << ")");
1946-
new_connection_l->socket().close();
1947-
}
1948-
});
1949-
//start async connect
1950-
sock_.async_connect(remote_endpoint, [=](const boost::system::error_code& ec_)
1952+
if (error != boost::asio::error::operation_aborted)
1953+
{
1954+
_dbg3("Failed to connect to " << remote_endpoint << ", because of timeout (" << conn_timeout << ')');
1955+
new_connection_l->socket().close(error); // ignore errors
1956+
}
1957+
};
1958+
1959+
auto sh_deadline = std::make_shared<boost::asio::deadline_timer>(io_context_);
1960+
sh_deadline->expires_from_now(boost::posix_time::milliseconds(conn_timeout));
1961+
sh_deadline->async_wait(new_connection_l->wrap(on_timer));
1962+
1963+
new_connection_l->socket().async_connect(remote_endpoint, new_connection_l->wrap([=](const boost::system::error_code& ec_)
19511964
{
1952-
t_connection_context conn_context = AUTO_VAL_INIT(conn_context);
1953-
boost::system::error_code ignored_ec;
1954-
boost::asio::ip::tcp::socket::endpoint_type lep = new_connection_l->socket().local_endpoint(ignored_ec);
1965+
const auto on_cancel = [=](const boost::system::error_code& error)
1966+
{
1967+
boost::system::error_code ignored_ec{};
1968+
const auto lep = new_connection_l->socket().local_endpoint(ignored_ec);
1969+
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] to " << remote_endpoint << " from " << lep << " failed: " << error.message());
1970+
if (remove_connection(new_connection_l))
1971+
cb(t_connection_context{}, error);
1972+
};
1973+
19551974
if(!ec_)
19561975
{//success
1957-
if(!sh_deadline->cancel())
1958-
{
1959-
cb(conn_context, boost::asio::error::operation_aborted);//this mean that deadline timer already queued callback with cancel operation, rare situation
1960-
}else
1961-
{
1962-
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] Connected success to " << adr << ':' << port <<
1963-
" from " << lep.address().to_string() << ':' << lep.port());
1964-
1965-
// start adds the connection to the config object's list, so we don't need to have it locally anymore
1966-
connections_mutex.lock();
1967-
connections_.erase(new_connection_l);
1968-
connections_mutex.unlock();
1969-
bool r = new_connection_l->start(false, 1 < m_threads_count);
1970-
if (r)
1976+
const auto on_ready = [=] ()
19711977
{
1972-
new_connection_l->get_context(conn_context);
1973-
cb(conn_context, ec_);
1974-
}
1975-
else
1978+
if (sh_deadline->cancel())
1979+
{
1980+
boost::system::error_code ignored_ec{};
1981+
const auto lep = new_connection_l->socket().local_endpoint(ignored_ec);
1982+
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] Connected successfully to " << remote_endpoint <<
1983+
" from " << lep.address().to_string() << ':' << lep.port());
1984+
1985+
if (remove_connection(new_connection_l) && new_connection_l->start(false, 1 < m_threads_count))
1986+
{
1987+
t_connection_context conn_context{};
1988+
new_connection_l->get_context(conn_context);
1989+
cb(conn_context, ec_);
1990+
}
1991+
else
1992+
on_cancel(boost::asio::error::fault);
1993+
}
1994+
else // if timer already expired
1995+
on_cancel(boost::asio::error::operation_aborted);
1996+
};
1997+
1998+
if (new_connection_l->get_ssl_support() != ssl_support_t::e_ssl_support_disabled)
1999+
{
2000+
// set new timer for handshake
2001+
if (sh_deadline->expires_from_now(boost::posix_time::milliseconds(conn_timeout)))
19762002
{
1977-
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] Failed to start connection to " << adr << ':' << port);
1978-
cb(conn_context, boost::asio::error::fault);
2003+
sh_deadline->async_wait(new_connection_l->wrap(on_timer));
2004+
new_connection_l->socket_.async_handshake(boost::asio::ssl::stream_base::client, new_connection_l->wrap([=] (const boost::system::error_code& ec)
2005+
{
2006+
if (ec)
2007+
{
2008+
sh_deadline->cancel();
2009+
if (new_connection_l->get_ssl_support() == ssl_support_t::e_ssl_support_autodetect)
2010+
{
2011+
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] SSL connection to " <<
2012+
remote_endpoint << " failed: " << ec.message() << ". Trying without SSL");
2013+
new_connection_l->disable_ssl();
2014+
connect_async_internal(new_connection_l, remote_endpoint, conn_timeout, cb);
2015+
}
2016+
else // ssl mandatory and failed
2017+
on_cancel(ec);
2018+
}
2019+
else // ssl handshake complete
2020+
on_ready();
2021+
}));
19792022
}
2023+
else // if timer already expired
2024+
on_cancel(boost::asio::error::operation_aborted);
19802025
}
1981-
}else
1982-
{
1983-
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] Failed to connect to " << adr << ':' << port <<
1984-
" from " << lep.address().to_string() << ':' << lep.port() << ": " << ec_.message() << ':' << ec_.value());
1985-
cb(conn_context, ec_);
2026+
else // ssl disabled
2027+
on_ready();
19862028
}
1987-
});
2029+
else // ec_ has error
2030+
on_cancel(ec_);
2031+
}));
2032+
return true;
2033+
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::connect_async_internal", false);
2034+
}
2035+
2036+
template<class t_protocol_handler>
2037+
bool boosted_tcp_server<t_protocol_handler>::remove_connection(const connection_ptr& new_connection)
2038+
{
2039+
if (!new_connection)
2040+
return false;
2041+
const boost::lock_guard<boost::mutex> sync{connections_mutex};
2042+
connections_.erase(new_connection);
19882043
return true;
1989-
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::connect_async", false);
19902044
}
1991-
19922045
} // namespace
19932046
} // 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
@@ -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

contrib/epee/include/net/levin_protocol_handler_async.h

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

529-
if(!send_message(return_message.finalize_response(m_current_head.m_command, return_code)))
529+
if(!send_message(return_message.finalize_response(m_current_head.m_command, return_code, m_connection_context.should_pad())))
530530
return false;
531531
}
532532
else
@@ -620,7 +620,7 @@ class async_protocol_handler
620620
if (command == m_connection_context.handshake_command())
621621
m_max_packet_size = m_config.m_max_packet_size;
622622

623-
if(!send_message(in_msg.finalize_invoke(command)))
623+
if(!send_message(in_msg.finalize_invoke(command, m_connection_context.should_pad())))
624624
{
625625
LOG_ERROR_CC(m_connection_context, "Failed to do_send");
626626
err_code = LEVIN_ERROR_CONNECTION;

0 commit comments

Comments
 (0)