Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions contrib/epee/include/net/abstract_tcp_server2.h
Original file line number Diff line number Diff line change
Expand Up @@ -319,8 +319,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 boost::asio::bind_executor(m_strand, std::forward<F>(f));
}

private:
//----------------- i_service_endpoint ---------------------
Expand Down Expand Up @@ -401,10 +412,10 @@ 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, 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{});
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{});

boost::asio::ssl::context& get_ssl_context() noexcept
{
Expand Down Expand Up @@ -503,6 +514,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, std::chrono::milliseconds 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_context used to perform asynchronous operations.
Expand Down
172 changes: 114 additions & 58 deletions contrib/epee/include/net/abstract_tcp_server2.inl
Original file line number Diff line number Diff line change
Expand Up @@ -982,7 +982,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 */ }
Expand Down Expand Up @@ -1709,7 +1709,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();

Expand Down Expand Up @@ -1785,7 +1785,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)
{
Expand All @@ -1799,6 +1799,7 @@ namespace net_utils
sock_.close();
return CONNECT_FAILURE;
}
new_connection_l->set_ssl_enabled();
}

return CONNECT_SUCCESS;
Expand All @@ -1807,11 +1808,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_context_, m_state, m_connection_type, ssl_support) );
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_context_, m_state, m_connection_type, ssl_options.support) );
connections_mutex.lock();
connections_.insert(new_connection_l);
MDEBUG("connections_ size now " << connections_.size());
Expand Down Expand Up @@ -1899,24 +1900,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);
Expand All @@ -1935,10 +1934,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, 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)
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)
{
TRY_ENTRY();
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_context_, m_state, m_connection_type, ssl_support, std::move(initial)) );
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_context_, m_state, m_connection_type, ssl_options.support, std::move(initial)) );
connections_mutex.lock();
connections_.insert(new_connection_l);
MDEBUG("connections_ size now " << connections_.size());
Expand Down Expand Up @@ -2015,60 +2014,117 @@ namespace net_utils
return false;
}
}

std::shared_ptr<boost::asio::steady_timer> sh_deadline(std::make_shared<boost::asio::steady_timer>(io_context_));
//start deadline
sh_deadline->expires_after(conn_timeout);
sh_deadline->async_wait([=](const boost::system::error_code& error)

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, const std::chrono::milliseconds 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 " << adr << ':' << port << ", because of timeout (" << conn_timeout.count() << ")");
new_connection_l->socket().close();
}
});
//start async connect
sock_.async_connect(remote_endpoint, [=](const boost::system::error_code& ec_)
if (error != boost::asio::error::operation_aborted)
{
_dbg3("Failed to connect to " << remote_endpoint << ", because of timeout (" << conn_timeout.count() << " ms)");
new_connection_l->socket().close(error); // ignore errors
}
};

auto sh_deadline = std::make_shared<boost::asio::steady_timer>(io_context_);
sh_deadline->expires_after(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_)
{
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);
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
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)
const auto on_ready = [=] ()
{
new_connection_l->get_context(conn_context);
cb(conn_context, ec_);
}
else
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_after(conn_timeout))
{
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] Failed to start connection to " << adr << ':' << port);
cb(conn_context, boost::asio::error::fault);
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)
{
sh_deadline->cancel();
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
{
new_connection_l->set_ssl_enabled();
on_ready();
}
}));
}
else // if timer already expired
on_cancel(boost::asio::error::operation_aborted);
}
}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_);
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;
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::connect_async", false);
}

} // namespace
} // namespace
5 changes: 2 additions & 3 deletions contrib/epee/include/net/connection_basic.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,9 @@ 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)
{
//m_state != nullptr verified in constructor
return m_state->ssl_options().handshake(strand_.context(), socket_, type, buffer);
return ssl.handshake(strand_.context(), socket_, boost::asio::ssl::stream_base::client);
}

template<typename MutableBufferSequence, typename ReadHandler>
Expand Down
13 changes: 7 additions & 6 deletions contrib/epee/include/net/levin_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ constexpr const std::chrono::milliseconds LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED{0}
//! 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;

Expand All @@ -148,12 +148,13 @@ constexpr const std::chrono::milliseconds LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED{0}
{
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-8191 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
Expand Down
4 changes: 2 additions & 2 deletions contrib/epee/include/net/levin_protocol_handler_async.h
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,7 @@ class async_protocol_handler
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
Expand Down Expand Up @@ -628,7 +628,7 @@ class async_protocol_handler
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;
Expand Down
Loading
Loading