2015-01-05 12:30:17 -07:00
/**
@file
@author from CrypoNote (see copyright below; Andrey N. Sabelnikov)
@monero rfree
@brief the connection templated-class for one peer connection
*/
2014-07-25 10:29:08 -06:00
// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
2014-03-03 15:07:58 -07:00
// All rights reserved.
//
2014-07-25 10:29:08 -06:00
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of the Andrey N. Sabelnikov nor the
// names of its contributors may be used to endorse or promote products
// derived from this software without specific prior written permission.
2014-07-23 07:03:52 -06:00
//
2014-07-25 10:29:08 -06:00
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2014-07-23 07:03:52 -06:00
//
2014-07-25 10:29:08 -06:00
2014-03-03 15:07:58 -07:00
#include <boost/foreach.hpp>
#include <boost/uuid/random_generator.hpp>
#include <boost/chrono.hpp>
#include <boost/utility/value_init.hpp>
#include <boost/asio/deadline_timer.hpp>
2015-01-05 12:30:17 -07:00
#include <boost/date_time/posix_time/posix_time.hpp> // TODO
2018-04-19 00:55:05 -06:00
#include <boost/thread/condition_variable.hpp> // TODO
2018-12-16 10:57:44 -07:00
#include <boost/make_shared.hpp>
2020-11-09 12:49:11 -07:00
#include <boost/thread.hpp>
2018-10-12 09:20:42 -06:00
#include "warnings.h"
2020-11-12 11:14:32 -07:00
#include "string_tools_lexical.h"
2014-03-03 15:07:58 -07:00
#include "misc_language.h"
2015-01-05 12:30:17 -07:00
#include <sstream>
#include <iomanip>
#include <algorithm>
2019-09-06 19:35:47 -06:00
#include <functional>
#include <random>
2015-01-05 12:30:17 -07:00
Change logging to easylogging++
This replaces the epee and data_loggers logging systems with
a single one, and also adds filename:line and explicit severity
levels. Categories may be defined, and logging severity set
by category (or set of categories). epee style 0-4 log level
maps to a sensible severity configuration. Log files now also
rotate when reaching 100 MB.
To select which logs to output, use the MONERO_LOGS environment
variable, with a comma separated list of categories (globs are
supported), with their requested severity level after a colon.
If a log matches more than one such setting, the last one in
the configuration string applies. A few examples:
This one is (mostly) silent, only outputting fatal errors:
MONERO_LOGS=*:FATAL
This one is very verbose:
MONERO_LOGS=*:TRACE
This one is totally silent (logwise):
MONERO_LOGS=""
This one outputs all errors and warnings, except for the
"verify" category, which prints just fatal errors (the verify
category is used for logs about incoming transactions and
blocks, and it is expected that some/many will fail to verify,
hence we don't want the spam):
MONERO_LOGS=*:WARNING,verify:FATAL
Log levels are, in decreasing order of priority:
FATAL, ERROR, WARNING, INFO, DEBUG, TRACE
Subcategories may be added using prefixes and globs. This
example will output net.p2p logs at the TRACE level, but all
other net* logs only at INFO:
MONERO_LOGS=*:ERROR,net*:INFO,net.p2p:TRACE
Logs which are intended for the user (which Monero was using
a lot through epee, but really isn't a nice way to go things)
should use the "global" category. There are a few helper macros
for using this category, eg: MGINFO("this shows up by default")
or MGINFO_RED("this is red"), to try to keep a similar look
and feel for now.
Existing epee log macros still exist, and map to the new log
levels, but since they're used as a "user facing" UI element
as much as a logging system, they often don't map well to log
severities (ie, a log level 0 log may be an error, or may be
something we want the user to see, such as an important info).
In those cases, I tried to use the new macros. In other cases,
I left the existing macros in. When modifying logs, it is
probably best to switch to the new macros with explicit levels.
The --log-level options and set_log commands now also accept
category settings, in addition to the epee style log levels.
2017-01-01 09:34:23 -07:00
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "net"
2015-01-05 12:30:17 -07:00
2019-04-20 06:24:29 -06:00
#define AGGRESSIVE_TIMEOUT_THRESHOLD 120 // sockets
#define NEW_CONNECTION_TIMEOUT_LOCAL 1200000 // 2 minutes
#define NEW_CONNECTION_TIMEOUT_REMOTE 10000 // 10 seconds
2018-06-07 05:43:10 -06:00
#define DEFAULT_TIMEOUT_MS_LOCAL 1800000 // 30 minutes
#define DEFAULT_TIMEOUT_MS_REMOTE 300000 // 5 minutes
2018-05-26 12:34:13 -06:00
#define TIMEOUT_EXTRA_MS_PER_BYTE 0.2
2019-03-21 05:01:15 -06:00
2014-03-03 15:07:58 -07:00
namespace epee
{
namespace net_utils
{
2018-12-16 10:57:44 -07:00
template<typename T>
2019-05-16 14:34:22 -06:00
T& check_and_get(std::shared_ptr<T>& ptr)
2018-12-16 10:57:44 -07:00
{
CHECK_AND_ASSERT_THROW_MES(bool(ptr), "shared_state cannot be null");
return *ptr;
}
2014-03-03 15:07:58 -07:00
/************************************************************************/
/* */
/************************************************************************/
2021-06-28 13:13:02 -06:00
template<typename T>
unsigned int connection<T>::host_count(int delta)
2018-12-16 10:57:44 -07:00
{
2022-07-06 17:47:34 -06:00
static std::mutex hosts_mutex;
std::lock_guard<std::mutex> guard(hosts_mutex);
static std::map<std::string, unsigned int> hosts;
unsigned int &val = hosts[m_host];
2021-06-28 13:13:02 -06:00
if (delta > 0)
2022-07-06 17:47:34 -06:00
MTRACE("New connection from host " << m_host << ": " << val);
2021-06-28 13:13:02 -06:00
else if (delta < 0)
2022-07-06 17:47:34 -06:00
MTRACE("Closed connection from host " << m_host << ": " << val);
2021-06-28 13:13:02 -06:00
CHECK_AND_ASSERT_THROW_MES(delta >= 0 || val >= (unsigned)-delta, "Count would go negative");
CHECK_AND_ASSERT_THROW_MES(delta <= 0 || val <= std::numeric_limits<unsigned int>::max() - (unsigned)delta, "Count would wrap");
val += delta;
return val;
2018-12-16 10:57:44 -07:00
}
2021-06-28 13:13:02 -06:00
template<typename T>
typename connection<T>::duration_t connection<T>::get_default_timeout()
2014-03-03 15:07:58 -07:00
{
2021-06-28 13:13:02 -06:00
unsigned count{};
try { count = host_count(); } catch (...) {}
const unsigned shift = (
connection_basic::get_state().sock_count > AGGRESSIVE_TIMEOUT_THRESHOLD ?
std::min(std::max(count, 1u) - 1, 8u) :
0
);
return (
2022-07-06 17:47:34 -06:00
m_local ?
2021-06-28 13:13:02 -06:00
std::chrono::milliseconds(DEFAULT_TIMEOUT_MS_LOCAL >> shift) :
std::chrono::milliseconds(DEFAULT_TIMEOUT_MS_REMOTE >> shift)
);
2014-03-03 15:07:58 -07:00
}
2021-06-28 13:13:02 -06:00
template<typename T>
typename connection<T>::duration_t connection<T>::get_timeout_from_bytes_read(size_t bytes) const
2014-03-03 15:07:58 -07:00
{
2021-06-28 13:13:02 -06:00
return std::chrono::duration_cast<connection<T>::duration_t>(
std::chrono::duration<double, std::chrono::milliseconds::period>(
bytes * TIMEOUT_EXTRA_MS_PER_BYTE
)
);
2014-03-03 15:07:58 -07:00
}
2022-07-06 17:47:34 -06:00
template<typename T>
void connection<T>::state_status_check()
{
switch (m_state.status)
{
case status_t::RUNNING:
interrupt();
break;
case status_t::INTERRUPTED:
on_interrupted();
break;
case status_t::TERMINATING:
on_terminating();
break;
default:
break;
}
}
2021-06-28 13:13:02 -06:00
template<typename T>
void connection<T>::start_timer(duration_t duration, bool add)
{
2022-07-06 17:47:34 -06:00
if (m_state.timers.general.wait_expire) {
m_state.timers.general.cancel_expire = true;
m_state.timers.general.reset_expire = true;
2021-06-28 13:13:02 -06:00
ec_t ec;
2022-07-06 17:47:34 -06:00
m_timers.general.expires_from_now(
2021-06-28 13:13:02 -06:00
std::min(
2022-07-06 17:47:34 -06:00
duration + (add ? m_timers.general.expires_from_now() : duration_t{}),
2021-06-28 13:13:02 -06:00
get_default_timeout()
),
ec
);
2019-04-10 16:34:30 -06:00
}
2021-06-28 13:13:02 -06:00
else {
ec_t ec;
2022-07-06 17:47:34 -06:00
m_timers.general.expires_from_now(
2021-06-28 13:13:02 -06:00
std::min(
2022-07-06 17:47:34 -06:00
duration + (add ? m_timers.general.expires_from_now() : duration_t{}),
2021-06-28 13:13:02 -06:00
get_default_timeout()
),
ec
);
async_wait_timer();
2019-04-10 16:34:30 -06:00
}
2018-12-16 10:57:44 -07:00
}
2018-06-07 05:43:10 -06:00
2021-06-28 13:13:02 -06:00
template<typename T>
void connection<T>::async_wait_timer()
2014-03-03 15:07:58 -07:00
{
2022-07-06 17:47:34 -06:00
if (m_state.timers.general.wait_expire)
2021-06-28 13:13:02 -06:00
return;
2022-07-06 17:47:34 -06:00
m_state.timers.general.wait_expire = true;
2021-06-28 13:13:02 -06:00
auto self = connection<T>::shared_from_this();
2022-07-06 17:47:34 -06:00
m_timers.general.async_wait([this, self](const ec_t & ec){
std::lock_guard<std::mutex> guard(m_state.lock);
m_state.timers.general.wait_expire = false;
if (m_state.timers.general.cancel_expire) {
m_state.timers.general.cancel_expire = false;
if (m_state.timers.general.reset_expire) {
m_state.timers.general.reset_expire = false;
2021-06-28 13:13:02 -06:00
async_wait_timer();
}
2022-07-06 17:47:34 -06:00
else if (m_state.status == status_t::INTERRUPTED)
2021-06-28 13:13:02 -06:00
on_interrupted();
2022-07-06 17:47:34 -06:00
else if (m_state.status == status_t::TERMINATING)
2021-06-28 13:13:02 -06:00
on_terminating();
}
2022-07-06 17:47:34 -06:00
else if (m_state.status == status_t::RUNNING)
2021-06-28 13:13:02 -06:00
interrupt();
2022-07-06 17:47:34 -06:00
else if (m_state.status == status_t::INTERRUPTED)
2021-06-28 13:13:02 -06:00
terminate();
});
2014-03-03 15:07:58 -07:00
}
2021-06-28 13:13:02 -06:00
template<typename T>
void connection<T>::cancel_timer()
2014-03-03 15:07:58 -07:00
{
2022-07-06 17:47:34 -06:00
if (!m_state.timers.general.wait_expire)
2021-06-28 13:13:02 -06:00
return;
2022-07-06 17:47:34 -06:00
m_state.timers.general.cancel_expire = true;
m_state.timers.general.reset_expire = false;
2021-06-28 13:13:02 -06:00
ec_t ec;
2022-07-06 17:47:34 -06:00
m_timers.general.cancel(ec);
2014-03-03 15:07:58 -07:00
}
2021-06-28 13:13:02 -06:00
template<typename T>
void connection<T>::start_handshake()
2014-03-03 15:07:58 -07:00
{
2022-07-06 17:47:34 -06:00
if (m_state.socket.wait_handshake)
2021-06-28 13:13:02 -06:00
return;
static_assert(
2022-07-06 17:47:34 -06:00
epee::net_utils::get_ssl_magic_size() <= sizeof(m_state.data.read.buffer),
2021-06-28 13:13:02 -06:00
""
);
auto self = connection<T>::shared_from_this();
2022-07-06 17:47:34 -06:00
if (!m_state.ssl.forced && !m_state.ssl.detected) {
m_state.socket.wait_read = true;
2021-06-28 13:13:02 -06:00
boost::asio::async_read(
connection_basic::socket_.next_layer(),
boost::asio::buffer(
2022-07-06 17:47:34 -06:00
m_state.data.read.buffer.data(),
m_state.data.read.buffer.size()
2021-06-28 13:13:02 -06:00
),
boost::asio::transfer_exactly(epee::net_utils::get_ssl_magic_size()),
2022-07-06 17:47:34 -06:00
m_strand.wrap(
2021-06-28 13:13:02 -06:00
[this, self](const ec_t &ec, size_t bytes_transferred){
2022-07-06 17:47:34 -06:00
std::lock_guard<std::mutex> guard(m_state.lock);
m_state.socket.wait_read = false;
if (m_state.socket.cancel_read) {
m_state.socket.cancel_read = false;
state_status_check();
2021-06-28 13:13:02 -06:00
}
else if (ec.value()) {
terminate();
}
else if (
2022-07-06 17:47:34 -06:00
!epee::net_utils::is_ssl(
2021-06-28 13:13:02 -06:00
static_cast<const unsigned char *>(
2022-07-06 17:47:34 -06:00
m_state.data.read.buffer.data()
2021-06-28 13:13:02 -06:00
),
bytes_transferred
)
) {
2022-07-06 17:47:34 -06:00
m_state.ssl.enabled = false;
m_state.socket.handle_read = true;
2021-06-28 13:13:02 -06:00
connection_basic::strand_.post(
[this, self, bytes_transferred]{
2022-07-06 17:47:34 -06:00
bool success = m_handler.handle_recv(
reinterpret_cast<char *>(m_state.data.read.buffer.data()),
2021-06-28 13:13:02 -06:00
bytes_transferred
);
2022-07-06 17:47:34 -06:00
std::lock_guard<std::mutex> guard(m_state.lock);
m_state.socket.handle_read = false;
if (m_state.status == status_t::INTERRUPTED)
2021-06-28 13:13:02 -06:00
on_interrupted();
2022-07-06 17:47:34 -06:00
else if (m_state.status == status_t::TERMINATING)
2021-06-28 13:13:02 -06:00
on_terminating();
2022-07-06 17:47:34 -06:00
else if (!success)
2021-06-28 13:13:02 -06:00
interrupt();
else {
start_read();
}
}
);
}
else {
2022-07-06 17:47:34 -06:00
m_state.ssl.detected = true;
2021-06-28 13:13:02 -06:00
start_handshake();
}
}
)
);
return;
}
2022-07-06 17:47:34 -06:00
m_state.socket.wait_handshake = true;
2021-06-28 13:13:02 -06:00
auto on_handshake = [this, self](const ec_t &ec, size_t bytes_transferred){
2022-07-06 17:47:34 -06:00
std::lock_guard<std::mutex> guard(m_state.lock);
m_state.socket.wait_handshake = false;
if (m_state.socket.cancel_handshake) {
m_state.socket.cancel_handshake = false;
state_status_check();
2021-06-28 13:13:02 -06:00
}
else if (ec.value()) {
ec_t ec;
connection_basic::socket_.next_layer().shutdown(
socket_t::shutdown_both,
ec
);
connection_basic::socket_.next_layer().close(ec);
2022-07-06 17:47:34 -06:00
m_state.socket.connected = false;
2021-06-28 13:13:02 -06:00
interrupt();
}
else {
2022-07-06 17:47:34 -06:00
m_state.ssl.handshaked = true;
2021-06-28 13:13:02 -06:00
start_write();
start_read();
}
};
const auto handshake = handshake_t::server;
static_cast<shared_state&>(
connection_basic::get_state()
).ssl_options().configure(connection_basic::socket_, handshake);
2022-07-06 17:47:34 -06:00
m_strand.post(
2021-06-28 13:13:02 -06:00
[this, self, on_handshake]{
connection_basic::socket_.async_handshake(
handshake,
boost::asio::buffer(
2022-07-06 17:47:34 -06:00
m_state.data.read.buffer.data(),
m_state.ssl.forced ? 0 :
2021-06-28 13:13:02 -06:00
epee::net_utils::get_ssl_magic_size()
),
2022-07-06 17:47:34 -06:00
m_strand.wrap(on_handshake)
2021-06-28 13:13:02 -06:00
);
}
);
2014-03-03 15:07:58 -07:00
}
2016-03-27 05:53:20 -06:00
2021-06-28 13:13:02 -06:00
template<typename T>
void connection<T>::start_read()
{
2022-07-06 17:47:34 -06:00
if (m_state.timers.throttle.in.wait_expire || m_state.socket.wait_read ||
m_state.socket.handle_read
) {
2021-06-28 13:13:02 -06:00
return;
2022-07-06 17:47:34 -06:00
}
2021-06-28 13:13:02 -06:00
auto self = connection<T>::shared_from_this();
2022-07-06 17:47:34 -06:00
if (m_connection_type != e_connection_type_RPC) {
2021-06-28 13:13:02 -06:00
auto calc_duration = []{
CRITICAL_REGION_LOCAL(
network_throttle_manager_t::m_lock_get_global_throttle_in
);
return std::chrono::duration_cast<connection<T>::duration_t>(
std::chrono::duration<double, std::chrono::seconds::period>(
std::min(
network_throttle_manager_t::get_global_throttle_in(
).get_sleep_time_after_tick(1),
1.0
)
)
);
};
const auto duration = calc_duration();
if (duration > duration_t{}) {
ec_t ec;
2022-07-06 17:47:34 -06:00
m_timers.throttle.in.expires_from_now(duration, ec);
m_state.timers.throttle.in.wait_expire = true;
m_timers.throttle.in.async_wait([this, self](const ec_t &ec){
std::lock_guard<std::mutex> guard(m_state.lock);
m_state.timers.throttle.in.wait_expire = false;
if (m_state.timers.throttle.in.cancel_expire) {
m_state.timers.throttle.in.cancel_expire = false;
state_status_check();
2021-06-28 13:13:02 -06:00
}
else if (ec.value())
interrupt();
else
start_read();
});
return;
}
2016-03-27 05:53:20 -06:00
}
2022-07-06 17:47:34 -06:00
m_state.socket.wait_read = true;
2021-06-28 13:13:02 -06:00
auto on_read = [this, self](const ec_t &ec, size_t bytes_transferred){
2022-07-06 17:47:34 -06:00
std::lock_guard<std::mutex> guard(m_state.lock);
m_state.socket.wait_read = false;
if (m_state.socket.cancel_read) {
m_state.socket.cancel_read = false;
state_status_check();
2021-06-28 13:13:02 -06:00
}
else if (ec.value())
terminate();
else {
{
2022-07-06 17:47:34 -06:00
m_state.stat.in.throttle.handle_trafic_exact(bytes_transferred);
const auto speed = m_state.stat.in.throttle.get_current_speed();
m_conn_context.m_current_speed_down = speed;
m_conn_context.m_max_speed_down = std::max(
m_conn_context.m_max_speed_down,
2021-06-28 13:13:02 -06:00
speed
);
{
CRITICAL_REGION_LOCAL(
network_throttle_manager_t::m_lock_get_global_throttle_in
);
network_throttle_manager_t::get_global_throttle_in(
).handle_trafic_exact(bytes_transferred);
}
connection_basic::logger_handle_net_read(bytes_transferred);
2022-07-06 17:47:34 -06:00
m_conn_context.m_last_recv = time(NULL);
m_conn_context.m_recv_cnt += bytes_transferred;
2021-06-28 13:13:02 -06:00
start_timer(get_timeout_from_bytes_read(bytes_transferred), true);
}
2022-07-06 17:47:34 -06:00
// Post handle_recv to a separate `strand_`, distinct from `m_strand`
// which is listening for reads/writes. This avoids a circular dep.
// handle_recv can queue many writes, and `m_strand` will process those
// writes until the connection terminates without deadlocking waiting
// for handle_recv.
m_state.socket.handle_read = true;
2021-06-28 13:13:02 -06:00
connection_basic::strand_.post(
[this, self, bytes_transferred]{
2022-07-06 17:47:34 -06:00
bool success = m_handler.handle_recv(
reinterpret_cast<char *>(m_state.data.read.buffer.data()),
2021-06-28 13:13:02 -06:00
bytes_transferred
);
2022-07-06 17:47:34 -06:00
std::lock_guard<std::mutex> guard(m_state.lock);
m_state.socket.handle_read = false;
if (m_state.status == status_t::INTERRUPTED)
2021-06-28 13:13:02 -06:00
on_interrupted();
2022-07-06 17:47:34 -06:00
else if (m_state.status == status_t::TERMINATING)
2021-06-28 13:13:02 -06:00
on_terminating();
2022-07-06 17:47:34 -06:00
else if (!success)
2021-06-28 13:13:02 -06:00
interrupt();
else {
start_read();
}
}
);
}
};
2022-07-06 17:47:34 -06:00
if (!m_state.ssl.enabled)
2021-06-28 13:13:02 -06:00
connection_basic::socket_.next_layer().async_read_some(
boost::asio::buffer(
2022-07-06 17:47:34 -06:00
m_state.data.read.buffer.data(),
m_state.data.read.buffer.size()
2021-06-28 13:13:02 -06:00
),
2022-07-06 17:47:34 -06:00
m_strand.wrap(on_read)
2021-06-28 13:13:02 -06:00
);
2016-03-27 05:53:20 -06:00
else
2022-07-06 17:47:34 -06:00
m_strand.post(
2021-06-28 13:13:02 -06:00
[this, self, on_read]{
connection_basic::socket_.async_read_some(
boost::asio::buffer(
2022-07-06 17:47:34 -06:00
m_state.data.read.buffer.data(),
m_state.data.read.buffer.size()
2021-06-28 13:13:02 -06:00
),
2022-07-06 17:47:34 -06:00
m_strand.wrap(on_read)
2021-06-28 13:13:02 -06:00
);
}
);
2015-04-08 11:54:07 -06:00
}
2021-06-28 13:13:02 -06:00
template<typename T>
void connection<T>::start_write()
2014-03-03 15:07:58 -07:00
{
2022-07-06 17:47:34 -06:00
if (m_state.timers.throttle.out.wait_expire || m_state.socket.wait_write ||
m_state.data.write.queue.empty() ||
(m_state.ssl.enabled && !m_state.ssl.handshaked)
) {
2021-06-28 13:13:02 -06:00
return;
2022-07-06 17:47:34 -06:00
}
2021-06-28 13:13:02 -06:00
auto self = connection<T>::shared_from_this();
2022-07-06 17:47:34 -06:00
if (m_connection_type != e_connection_type_RPC) {
2021-06-28 13:13:02 -06:00
auto calc_duration = [this]{
CRITICAL_REGION_LOCAL(
network_throttle_manager_t::m_lock_get_global_throttle_out
);
return std::chrono::duration_cast<connection<T>::duration_t>(
std::chrono::duration<double, std::chrono::seconds::period>(
std::min(
network_throttle_manager_t::get_global_throttle_out(
).get_sleep_time_after_tick(
2022-07-06 17:47:34 -06:00
m_state.data.write.queue.back().size()
2021-06-28 13:13:02 -06:00
),
1.0
)
)
);
};
const auto duration = calc_duration();
if (duration > duration_t{}) {
ec_t ec;
2022-07-06 17:47:34 -06:00
m_timers.throttle.out.expires_from_now(duration, ec);
m_state.timers.throttle.out.wait_expire = true;
m_timers.throttle.out.async_wait([this, self](const ec_t &ec){
std::lock_guard<std::mutex> guard(m_state.lock);
m_state.timers.throttle.out.wait_expire = false;
if (m_state.timers.throttle.out.cancel_expire) {
m_state.timers.throttle.out.cancel_expire = false;
state_status_check();
2021-06-28 13:13:02 -06:00
}
else if (ec.value())
interrupt();
else
start_write();
});
}
}
2020-12-26 07:14:09 -07:00
2022-07-06 17:47:34 -06:00
m_state.socket.wait_write = true;
2021-06-28 13:13:02 -06:00
auto on_write = [this, self](const ec_t &ec, size_t bytes_transferred){
2022-07-06 17:47:34 -06:00
std::lock_guard<std::mutex> guard(m_state.lock);
m_state.socket.wait_write = false;
if (m_state.socket.cancel_write) {
m_state.socket.cancel_write = false;
m_state.data.write.queue.clear();
state_status_check();
2014-03-03 15:07:58 -07:00
}
2021-06-28 13:13:02 -06:00
else if (ec.value()) {
2022-07-06 17:47:34 -06:00
m_state.data.write.queue.clear();
2021-06-28 13:13:02 -06:00
interrupt();
2014-03-03 15:07:58 -07:00
}
2021-06-28 13:13:02 -06:00
else {
{
2022-07-06 17:47:34 -06:00
m_state.stat.out.throttle.handle_trafic_exact(bytes_transferred);
const auto speed = m_state.stat.out.throttle.get_current_speed();
m_conn_context.m_current_speed_up = speed;
m_conn_context.m_max_speed_down = std::max(
m_conn_context.m_max_speed_down,
2021-06-28 13:13:02 -06:00
speed
);
{
CRITICAL_REGION_LOCAL(
network_throttle_manager_t::m_lock_get_global_throttle_out
);
network_throttle_manager_t::get_global_throttle_out(
).handle_trafic_exact(bytes_transferred);
}
connection_basic::logger_handle_net_write(bytes_transferred);
2022-07-06 17:47:34 -06:00
m_conn_context.m_last_send = time(NULL);
m_conn_context.m_send_cnt += bytes_transferred;
2021-06-28 13:13:02 -06:00
start_timer(get_default_timeout(), true);
}
2022-07-06 17:47:34 -06:00
assert(bytes_transferred == m_state.data.write.queue.back().size());
m_state.data.write.queue.pop_back();
m_state.condition.notify_all();
2021-06-28 13:13:02 -06:00
start_write();
2018-06-07 05:43:10 -06:00
}
2021-06-28 13:13:02 -06:00
};
2022-07-06 17:47:34 -06:00
if (!m_state.ssl.enabled)
2021-06-28 13:13:02 -06:00
boost::asio::async_write(
connection_basic::socket_.next_layer(),
boost::asio::buffer(
2022-07-06 17:47:34 -06:00
m_state.data.write.queue.back().data(),
m_state.data.write.queue.back().size()
2021-06-28 13:13:02 -06:00
),
2022-07-06 17:47:34 -06:00
m_strand.wrap(on_write)
2021-06-28 13:13:02 -06:00
);
else
2022-07-06 17:47:34 -06:00
m_strand.post(
2021-06-28 13:13:02 -06:00
[this, self, on_write]{
boost::asio::async_write(
connection_basic::socket_,
boost::asio::buffer(
2022-07-06 17:47:34 -06:00
m_state.data.write.queue.back().data(),
m_state.data.write.queue.back().size()
2021-06-28 13:13:02 -06:00
),
2022-07-06 17:47:34 -06:00
m_strand.wrap(on_write)
2021-06-28 13:13:02 -06:00
);
}
);
2014-03-03 15:07:58 -07:00
}
2021-06-28 13:13:02 -06:00
template<typename T>
void connection<T>::start_shutdown()
epee: add SSL support
RPC connections now have optional tranparent SSL.
An optional private key and certificate file can be passed,
using the --{rpc,daemon}-ssl-private-key and
--{rpc,daemon}-ssl-certificate options. Those have as
argument a path to a PEM format private private key and
certificate, respectively.
If not given, a temporary self signed certificate will be used.
SSL can be enabled or disabled using --{rpc}-ssl, which
accepts autodetect (default), disabled or enabled.
Access can be restricted to particular certificates using the
--rpc-ssl-allowed-certificates, which takes a list of
paths to PEM encoded certificates. This can allow a wallet to
connect to only the daemon they think they're connected to,
by forcing SSL and listing the paths to the known good
certificates.
To generate long term certificates:
openssl genrsa -out /tmp/KEY 4096
openssl req -new -key /tmp/KEY -out /tmp/REQ
openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT
/tmp/KEY is the private key, and /tmp/CERT is the certificate,
both in PEM format. /tmp/REQ can be removed. Adjust the last
command to set expiration date, etc, as needed. It doesn't
make a whole lot of sense for monero anyway, since most servers
will run with one time temporary self signed certificates anyway.
SSL support is transparent, so all communication is done on the
existing ports, with SSL autodetection. This means you can start
using an SSL daemon now, but you should not enforce SSL yet or
nothing will talk to you.
2018-06-14 16:44:48 -06:00
{
2022-07-06 17:47:34 -06:00
if (m_state.socket.wait_shutdown)
2021-06-28 13:13:02 -06:00
return;
auto self = connection<T>::shared_from_this();
2022-07-06 17:47:34 -06:00
m_state.socket.wait_shutdown = true;
2021-06-28 13:13:02 -06:00
auto on_shutdown = [this, self](const ec_t &ec){
2022-07-06 17:47:34 -06:00
std::lock_guard<std::mutex> guard(m_state.lock);
m_state.socket.wait_shutdown = false;
if (m_state.socket.cancel_shutdown) {
m_state.socket.cancel_shutdown = false;
switch (m_state.status)
{
case status_t::RUNNING:
interrupt();
break;
case status_t::INTERRUPTED:
terminate();
break;
case status_t::TERMINATING:
on_terminating();
break;
default:
break;
}
2021-06-28 13:13:02 -06:00
}
else if (ec.value())
terminate();
else {
cancel_timer();
on_interrupted();
}
};
2022-07-06 17:47:34 -06:00
m_strand.post(
2021-06-28 13:13:02 -06:00
[this, self, on_shutdown]{
connection_basic::socket_.async_shutdown(
2022-07-06 17:47:34 -06:00
m_strand.wrap(on_shutdown)
2021-06-28 13:13:02 -06:00
);
}
);
start_timer(get_default_timeout());
}
2020-12-26 07:14:09 -07:00
2021-06-28 13:13:02 -06:00
template<typename T>
void connection<T>::cancel_socket()
{
bool wait_socket = false;
2022-07-06 17:47:34 -06:00
if (m_state.socket.wait_handshake)
wait_socket = m_state.socket.cancel_handshake = true;
if (m_state.timers.throttle.in.wait_expire) {
m_state.timers.throttle.in.cancel_expire = true;
2021-06-28 13:13:02 -06:00
ec_t ec;
2022-07-06 17:47:34 -06:00
m_timers.throttle.in.cancel(ec);
2021-06-28 13:13:02 -06:00
}
2022-07-06 17:47:34 -06:00
if (m_state.socket.wait_read)
wait_socket = m_state.socket.cancel_read = true;
if (m_state.timers.throttle.out.wait_expire) {
m_state.timers.throttle.out.cancel_expire = true;
2021-06-28 13:13:02 -06:00
ec_t ec;
2022-07-06 17:47:34 -06:00
m_timers.throttle.out.cancel(ec);
2021-06-28 13:13:02 -06:00
}
2022-07-06 17:47:34 -06:00
if (m_state.socket.wait_write)
wait_socket = m_state.socket.cancel_write = true;
if (m_state.socket.wait_shutdown)
wait_socket = m_state.socket.cancel_shutdown = true;
2021-06-28 13:13:02 -06:00
if (wait_socket) {
ec_t ec;
connection_basic::socket_.next_layer().cancel(ec);
}
}
2020-12-26 07:14:09 -07:00
2021-06-28 13:13:02 -06:00
template<typename T>
void connection<T>::cancel_handler()
{
2022-07-06 17:47:34 -06:00
if (m_state.protocol.released || m_state.protocol.wait_release)
epee: add SSL support
RPC connections now have optional tranparent SSL.
An optional private key and certificate file can be passed,
using the --{rpc,daemon}-ssl-private-key and
--{rpc,daemon}-ssl-certificate options. Those have as
argument a path to a PEM format private private key and
certificate, respectively.
If not given, a temporary self signed certificate will be used.
SSL can be enabled or disabled using --{rpc}-ssl, which
accepts autodetect (default), disabled or enabled.
Access can be restricted to particular certificates using the
--rpc-ssl-allowed-certificates, which takes a list of
paths to PEM encoded certificates. This can allow a wallet to
connect to only the daemon they think they're connected to,
by forcing SSL and listing the paths to the known good
certificates.
To generate long term certificates:
openssl genrsa -out /tmp/KEY 4096
openssl req -new -key /tmp/KEY -out /tmp/REQ
openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT
/tmp/KEY is the private key, and /tmp/CERT is the certificate,
both in PEM format. /tmp/REQ can be removed. Adjust the last
command to set expiration date, etc, as needed. It doesn't
make a whole lot of sense for monero anyway, since most servers
will run with one time temporary self signed certificates anyway.
SSL support is transparent, so all communication is done on the
existing ports, with SSL autodetection. This means you can start
using an SSL daemon now, but you should not enforce SSL yet or
nothing will talk to you.
2018-06-14 16:44:48 -06:00
return;
2022-07-06 17:47:34 -06:00
m_state.protocol.wait_release = true;
m_state.lock.unlock();
m_handler.release_protocol();
m_state.lock.lock();
m_state.protocol.wait_release = false;
m_state.protocol.released = true;
if (m_state.status == status_t::INTERRUPTED)
2021-06-28 13:13:02 -06:00
on_interrupted();
2022-07-06 17:47:34 -06:00
else if (m_state.status == status_t::TERMINATING)
2021-06-28 13:13:02 -06:00
on_terminating();
}
epee: add SSL support
RPC connections now have optional tranparent SSL.
An optional private key and certificate file can be passed,
using the --{rpc,daemon}-ssl-private-key and
--{rpc,daemon}-ssl-certificate options. Those have as
argument a path to a PEM format private private key and
certificate, respectively.
If not given, a temporary self signed certificate will be used.
SSL can be enabled or disabled using --{rpc}-ssl, which
accepts autodetect (default), disabled or enabled.
Access can be restricted to particular certificates using the
--rpc-ssl-allowed-certificates, which takes a list of
paths to PEM encoded certificates. This can allow a wallet to
connect to only the daemon they think they're connected to,
by forcing SSL and listing the paths to the known good
certificates.
To generate long term certificates:
openssl genrsa -out /tmp/KEY 4096
openssl req -new -key /tmp/KEY -out /tmp/REQ
openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT
/tmp/KEY is the private key, and /tmp/CERT is the certificate,
both in PEM format. /tmp/REQ can be removed. Adjust the last
command to set expiration date, etc, as needed. It doesn't
make a whole lot of sense for monero anyway, since most servers
will run with one time temporary self signed certificates anyway.
SSL support is transparent, so all communication is done on the
existing ports, with SSL autodetection. This means you can start
using an SSL daemon now, but you should not enforce SSL yet or
nothing will talk to you.
2018-06-14 16:44:48 -06:00
2021-06-28 13:13:02 -06:00
template<typename T>
void connection<T>::interrupt()
{
2022-07-06 17:47:34 -06:00
if (m_state.status != status_t::RUNNING)
epee: add SSL support
RPC connections now have optional tranparent SSL.
An optional private key and certificate file can be passed,
using the --{rpc,daemon}-ssl-private-key and
--{rpc,daemon}-ssl-certificate options. Those have as
argument a path to a PEM format private private key and
certificate, respectively.
If not given, a temporary self signed certificate will be used.
SSL can be enabled or disabled using --{rpc}-ssl, which
accepts autodetect (default), disabled or enabled.
Access can be restricted to particular certificates using the
--rpc-ssl-allowed-certificates, which takes a list of
paths to PEM encoded certificates. This can allow a wallet to
connect to only the daemon they think they're connected to,
by forcing SSL and listing the paths to the known good
certificates.
To generate long term certificates:
openssl genrsa -out /tmp/KEY 4096
openssl req -new -key /tmp/KEY -out /tmp/REQ
openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT
/tmp/KEY is the private key, and /tmp/CERT is the certificate,
both in PEM format. /tmp/REQ can be removed. Adjust the last
command to set expiration date, etc, as needed. It doesn't
make a whole lot of sense for monero anyway, since most servers
will run with one time temporary self signed certificates anyway.
SSL support is transparent, so all communication is done on the
existing ports, with SSL autodetection. This means you can start
using an SSL daemon now, but you should not enforce SSL yet or
nothing will talk to you.
2018-06-14 16:44:48 -06:00
return;
2022-07-06 17:47:34 -06:00
m_state.status = status_t::INTERRUPTED;
2021-06-28 13:13:02 -06:00
cancel_timer();
cancel_socket();
on_interrupted();
2022-07-06 17:47:34 -06:00
m_state.condition.notify_all();
2021-06-28 13:13:02 -06:00
cancel_handler();
}
epee: add SSL support
RPC connections now have optional tranparent SSL.
An optional private key and certificate file can be passed,
using the --{rpc,daemon}-ssl-private-key and
--{rpc,daemon}-ssl-certificate options. Those have as
argument a path to a PEM format private private key and
certificate, respectively.
If not given, a temporary self signed certificate will be used.
SSL can be enabled or disabled using --{rpc}-ssl, which
accepts autodetect (default), disabled or enabled.
Access can be restricted to particular certificates using the
--rpc-ssl-allowed-certificates, which takes a list of
paths to PEM encoded certificates. This can allow a wallet to
connect to only the daemon they think they're connected to,
by forcing SSL and listing the paths to the known good
certificates.
To generate long term certificates:
openssl genrsa -out /tmp/KEY 4096
openssl req -new -key /tmp/KEY -out /tmp/REQ
openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT
/tmp/KEY is the private key, and /tmp/CERT is the certificate,
both in PEM format. /tmp/REQ can be removed. Adjust the last
command to set expiration date, etc, as needed. It doesn't
make a whole lot of sense for monero anyway, since most servers
will run with one time temporary self signed certificates anyway.
SSL support is transparent, so all communication is done on the
existing ports, with SSL autodetection. This means you can start
using an SSL daemon now, but you should not enforce SSL yet or
nothing will talk to you.
2018-06-14 16:44:48 -06:00
2021-06-28 13:13:02 -06:00
template<typename T>
void connection<T>::on_interrupted()
{
2022-07-06 17:47:34 -06:00
assert(m_state.status == status_t::INTERRUPTED);
if (m_state.timers.general.wait_expire)
2021-06-28 13:13:02 -06:00
return;
2022-07-06 17:47:34 -06:00
if (m_state.socket.wait_handshake)
2021-06-28 13:13:02 -06:00
return;
2022-07-06 17:47:34 -06:00
if (m_state.timers.throttle.in.wait_expire)
2021-06-28 13:13:02 -06:00
return;
2022-07-06 17:47:34 -06:00
if (m_state.socket.wait_read)
2021-06-28 13:13:02 -06:00
return;
2022-07-06 17:47:34 -06:00
if (m_state.socket.handle_read)
2021-06-28 13:13:02 -06:00
return;
2022-07-06 17:47:34 -06:00
if (m_state.timers.throttle.out.wait_expire)
2021-06-28 13:13:02 -06:00
return;
2022-07-06 17:47:34 -06:00
if (m_state.socket.wait_write)
2021-06-28 13:13:02 -06:00
return;
2022-07-06 17:47:34 -06:00
if (m_state.socket.wait_shutdown)
2021-06-28 13:13:02 -06:00
return;
2022-07-06 17:47:34 -06:00
if (m_state.protocol.wait_init)
2021-06-28 13:13:02 -06:00
return;
2022-07-06 17:47:34 -06:00
if (m_state.protocol.wait_callback)
2021-06-28 13:13:02 -06:00
return;
2022-07-06 17:47:34 -06:00
if (m_state.protocol.wait_release)
2021-06-28 13:13:02 -06:00
return;
2022-07-06 17:47:34 -06:00
if (m_state.socket.connected) {
if (!m_state.ssl.enabled) {
2021-06-28 13:13:02 -06:00
ec_t ec;
connection_basic::socket_.next_layer().shutdown(
socket_t::shutdown_both,
ec
);
connection_basic::socket_.next_layer().close(ec);
2022-07-06 17:47:34 -06:00
m_state.socket.connected = false;
m_state.status = status_t::WASTED;
epee: add SSL support
RPC connections now have optional tranparent SSL.
An optional private key and certificate file can be passed,
using the --{rpc,daemon}-ssl-private-key and
--{rpc,daemon}-ssl-certificate options. Those have as
argument a path to a PEM format private private key and
certificate, respectively.
If not given, a temporary self signed certificate will be used.
SSL can be enabled or disabled using --{rpc}-ssl, which
accepts autodetect (default), disabled or enabled.
Access can be restricted to particular certificates using the
--rpc-ssl-allowed-certificates, which takes a list of
paths to PEM encoded certificates. This can allow a wallet to
connect to only the daemon they think they're connected to,
by forcing SSL and listing the paths to the known good
certificates.
To generate long term certificates:
openssl genrsa -out /tmp/KEY 4096
openssl req -new -key /tmp/KEY -out /tmp/REQ
openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT
/tmp/KEY is the private key, and /tmp/CERT is the certificate,
both in PEM format. /tmp/REQ can be removed. Adjust the last
command to set expiration date, etc, as needed. It doesn't
make a whole lot of sense for monero anyway, since most servers
will run with one time temporary self signed certificates anyway.
SSL support is transparent, so all communication is done on the
existing ports, with SSL autodetection. This means you can start
using an SSL daemon now, but you should not enforce SSL yet or
nothing will talk to you.
2018-06-14 16:44:48 -06:00
}
else
2021-06-28 13:13:02 -06:00
start_shutdown();
epee: add SSL support
RPC connections now have optional tranparent SSL.
An optional private key and certificate file can be passed,
using the --{rpc,daemon}-ssl-private-key and
--{rpc,daemon}-ssl-certificate options. Those have as
argument a path to a PEM format private private key and
certificate, respectively.
If not given, a temporary self signed certificate will be used.
SSL can be enabled or disabled using --{rpc}-ssl, which
accepts autodetect (default), disabled or enabled.
Access can be restricted to particular certificates using the
--rpc-ssl-allowed-certificates, which takes a list of
paths to PEM encoded certificates. This can allow a wallet to
connect to only the daemon they think they're connected to,
by forcing SSL and listing the paths to the known good
certificates.
To generate long term certificates:
openssl genrsa -out /tmp/KEY 4096
openssl req -new -key /tmp/KEY -out /tmp/REQ
openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT
/tmp/KEY is the private key, and /tmp/CERT is the certificate,
both in PEM format. /tmp/REQ can be removed. Adjust the last
command to set expiration date, etc, as needed. It doesn't
make a whole lot of sense for monero anyway, since most servers
will run with one time temporary self signed certificates anyway.
SSL support is transparent, so all communication is done on the
existing ports, with SSL autodetection. This means you can start
using an SSL daemon now, but you should not enforce SSL yet or
nothing will talk to you.
2018-06-14 16:44:48 -06:00
}
2020-12-26 18:55:12 -07:00
else
2022-07-06 17:47:34 -06:00
m_state.status = status_t::WASTED;
2021-06-28 13:13:02 -06:00
}
epee: add SSL support
RPC connections now have optional tranparent SSL.
An optional private key and certificate file can be passed,
using the --{rpc,daemon}-ssl-private-key and
--{rpc,daemon}-ssl-certificate options. Those have as
argument a path to a PEM format private private key and
certificate, respectively.
If not given, a temporary self signed certificate will be used.
SSL can be enabled or disabled using --{rpc}-ssl, which
accepts autodetect (default), disabled or enabled.
Access can be restricted to particular certificates using the
--rpc-ssl-allowed-certificates, which takes a list of
paths to PEM encoded certificates. This can allow a wallet to
connect to only the daemon they think they're connected to,
by forcing SSL and listing the paths to the known good
certificates.
To generate long term certificates:
openssl genrsa -out /tmp/KEY 4096
openssl req -new -key /tmp/KEY -out /tmp/REQ
openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT
/tmp/KEY is the private key, and /tmp/CERT is the certificate,
both in PEM format. /tmp/REQ can be removed. Adjust the last
command to set expiration date, etc, as needed. It doesn't
make a whole lot of sense for monero anyway, since most servers
will run with one time temporary self signed certificates anyway.
SSL support is transparent, so all communication is done on the
existing ports, with SSL autodetection. This means you can start
using an SSL daemon now, but you should not enforce SSL yet or
nothing will talk to you.
2018-06-14 16:44:48 -06:00
2021-06-28 13:13:02 -06:00
template<typename T>
void connection<T>::terminate()
{
2022-07-06 17:47:34 -06:00
if (m_state.status != status_t::RUNNING &&
m_state.status != status_t::INTERRUPTED
2021-06-28 13:13:02 -06:00
)
return;
2022-07-06 17:47:34 -06:00
m_state.status = status_t::TERMINATING;
2021-06-28 13:13:02 -06:00
cancel_timer();
cancel_socket();
on_terminating();
2022-07-06 17:47:34 -06:00
m_state.condition.notify_all();
2021-06-28 13:13:02 -06:00
cancel_handler();
epee: add SSL support
RPC connections now have optional tranparent SSL.
An optional private key and certificate file can be passed,
using the --{rpc,daemon}-ssl-private-key and
--{rpc,daemon}-ssl-certificate options. Those have as
argument a path to a PEM format private private key and
certificate, respectively.
If not given, a temporary self signed certificate will be used.
SSL can be enabled or disabled using --{rpc}-ssl, which
accepts autodetect (default), disabled or enabled.
Access can be restricted to particular certificates using the
--rpc-ssl-allowed-certificates, which takes a list of
paths to PEM encoded certificates. This can allow a wallet to
connect to only the daemon they think they're connected to,
by forcing SSL and listing the paths to the known good
certificates.
To generate long term certificates:
openssl genrsa -out /tmp/KEY 4096
openssl req -new -key /tmp/KEY -out /tmp/REQ
openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT
/tmp/KEY is the private key, and /tmp/CERT is the certificate,
both in PEM format. /tmp/REQ can be removed. Adjust the last
command to set expiration date, etc, as needed. It doesn't
make a whole lot of sense for monero anyway, since most servers
will run with one time temporary self signed certificates anyway.
SSL support is transparent, so all communication is done on the
existing ports, with SSL autodetection. This means you can start
using an SSL daemon now, but you should not enforce SSL yet or
nothing will talk to you.
2018-06-14 16:44:48 -06:00
}
2021-06-28 13:13:02 -06:00
template<typename T>
void connection<T>::on_terminating()
{
2022-07-06 17:47:34 -06:00
assert(m_state.status == status_t::TERMINATING);
if (m_state.timers.general.wait_expire)
2021-06-28 13:13:02 -06:00
return;
2022-07-06 17:47:34 -06:00
if (m_state.socket.wait_handshake)
2021-06-28 13:13:02 -06:00
return;
2022-07-06 17:47:34 -06:00
if (m_state.timers.throttle.in.wait_expire)
2021-06-28 13:13:02 -06:00
return;
2022-07-06 17:47:34 -06:00
if (m_state.socket.wait_read)
2021-06-28 13:13:02 -06:00
return;
2022-07-06 17:47:34 -06:00
if (m_state.socket.handle_read)
2021-06-28 13:13:02 -06:00
return;
2022-07-06 17:47:34 -06:00
if (m_state.timers.throttle.out.wait_expire)
2021-06-28 13:13:02 -06:00
return;
2022-07-06 17:47:34 -06:00
if (m_state.socket.wait_write)
2021-06-28 13:13:02 -06:00
return;
2022-07-06 17:47:34 -06:00
if (m_state.socket.wait_shutdown)
2021-06-28 13:13:02 -06:00
return;
2022-07-06 17:47:34 -06:00
if (m_state.protocol.wait_init)
2021-06-28 13:13:02 -06:00
return;
2022-07-06 17:47:34 -06:00
if (m_state.protocol.wait_callback)
2021-06-28 13:13:02 -06:00
return;
2022-07-06 17:47:34 -06:00
if (m_state.protocol.wait_release)
2021-06-28 13:13:02 -06:00
return;
2022-07-06 17:47:34 -06:00
if (m_state.socket.connected) {
2021-06-28 13:13:02 -06:00
ec_t ec;
connection_basic::socket_.next_layer().shutdown(
socket_t::shutdown_both,
ec
);
connection_basic::socket_.next_layer().close(ec);
2022-07-06 17:47:34 -06:00
m_state.socket.connected = false;
2014-03-03 15:07:58 -07:00
}
2022-07-06 17:47:34 -06:00
m_state.status = status_t::WASTED;
2021-06-28 13:13:02 -06:00
}
template<typename T>
2022-07-06 17:47:34 -06:00
bool connection<T>::send(epee::byte_slice message)
2014-03-03 15:07:58 -07:00
{
2022-07-06 17:47:34 -06:00
std::lock_guard<std::mutex> guard(m_state.lock);
if (m_state.status != status_t::RUNNING || m_state.socket.wait_handshake)
2014-03-03 15:07:58 -07:00
return false;
2022-07-06 17:47:34 -06:00
// Wait for the write queue to fall below the max. If it doesn't after a
// randomized delay, drop the connection.
2021-06-28 13:13:02 -06:00
auto wait_consume = [this] {
auto random_delay = []{
2019-09-06 19:35:47 -06:00
using engine = std::mt19937;
std::random_device dev;
2021-06-28 13:13:02 -06:00
std::seed_seq::result_type rand[
engine::state_size // Use complete bit space
]{};
2019-09-06 19:35:47 -06:00
std::generate_n(rand, engine::state_size, std::ref(dev));
std::seed_seq seed(rand, rand + engine::state_size);
2021-06-28 13:13:02 -06:00
engine rng(seed);
return std::chrono::milliseconds(
std::uniform_int_distribution<>(5000, 6000)(rng)
);
};
2022-07-06 17:47:34 -06:00
if (m_state.data.write.queue.size() <= ABSTRACT_SERVER_SEND_QUE_MAX_COUNT)
2021-06-28 13:13:02 -06:00
return true;
2022-07-06 17:47:34 -06:00
m_state.data.write.wait_consume = true;
bool success = m_state.condition.wait_for(
m_state.lock,
2021-06-28 13:13:02 -06:00
random_delay(),
[this]{
return (
2022-07-06 17:47:34 -06:00
m_state.status != status_t::RUNNING ||
m_state.data.write.queue.size() <=
2021-06-28 13:13:02 -06:00
ABSTRACT_SERVER_SEND_QUE_MAX_COUNT
);
}
);
2022-07-06 17:47:34 -06:00
m_state.data.write.wait_consume = false;
if (!success) {
2021-06-28 13:13:02 -06:00
terminate();
return false;
}
else
2022-07-06 17:47:34 -06:00
return m_state.status == status_t::RUNNING;
2021-06-28 13:13:02 -06:00
};
auto wait_sender = [this] {
2022-07-06 17:47:34 -06:00
m_state.condition.wait(
m_state.lock,
2021-06-28 13:13:02 -06:00
[this] {
return (
2022-07-06 17:47:34 -06:00
m_state.status != status_t::RUNNING ||
!m_state.data.write.wait_consume
2021-06-28 13:13:02 -06:00
);
2015-01-05 12:30:17 -07:00
}
2021-06-28 13:13:02 -06:00
);
2022-07-06 17:47:34 -06:00
return m_state.status == status_t::RUNNING;
2021-06-28 13:13:02 -06:00
};
2022-07-06 17:47:34 -06:00
if (!wait_sender())
2021-06-28 13:13:02 -06:00
return false;
constexpr size_t CHUNK_SIZE = 32 * 1024;
2022-07-06 17:47:34 -06:00
if (m_connection_type == e_connection_type_RPC ||
2021-06-28 13:13:02 -06:00
message.size() <= 2 * CHUNK_SIZE
) {
2022-07-06 17:47:34 -06:00
if (!wait_consume())
2021-06-28 13:13:02 -06:00
return false;
2022-07-06 17:47:34 -06:00
m_state.data.write.queue.emplace_front(std::move(message));
2021-06-28 13:13:02 -06:00
start_write();
2014-03-03 15:07:58 -07:00
}
2021-06-28 13:13:02 -06:00
else {
while (!message.empty()) {
2022-07-06 17:47:34 -06:00
if (!wait_consume())
2021-06-28 13:13:02 -06:00
return false;
2022-07-06 17:47:34 -06:00
m_state.data.write.queue.emplace_front(
2021-06-28 13:13:02 -06:00
message.take_slice(CHUNK_SIZE)
);
start_write();
}
2014-03-03 15:07:58 -07:00
}
2022-07-06 17:47:34 -06:00
m_state.condition.notify_all();
2021-06-28 13:13:02 -06:00
return true;
}
2015-01-05 12:30:17 -07:00
2021-06-28 13:13:02 -06:00
template<typename T>
bool connection<T>::start_internal(
bool is_income,
bool is_multithreaded,
boost::optional<network_address> real_remote
)
{
2022-07-06 17:47:34 -06:00
std::unique_lock<std::mutex> guard(m_state.lock);
if (m_state.status != status_t::TERMINATED)
2021-06-28 13:13:02 -06:00
return false;
2022-07-06 17:47:34 -06:00
if (!real_remote) {
2021-06-28 13:13:02 -06:00
ec_t ec;
auto endpoint = connection_basic::socket_.next_layer().remote_endpoint(
ec
);
if (ec.value())
return false;
real_remote = (
endpoint.address().is_v6() ?
network_address{
ipv6_network_address{endpoint.address().to_v6(), endpoint.port()}
} :
network_address{
ipv4_network_address{
uint32_t{
boost::asio::detail::socket_ops::host_to_network_long(
endpoint.address().to_v4().to_ulong()
)
},
endpoint.port()
}
2015-01-05 12:30:17 -07:00
}
2021-06-28 13:13:02 -06:00
);
2015-01-05 12:30:17 -07:00
}
2021-06-28 13:13:02 -06:00
auto *filter = static_cast<shared_state&>(
connection_basic::get_state()
).pfilter;
2022-07-06 17:47:34 -06:00
if (filter && !filter->is_remote_host_allowed(*real_remote))
2021-06-28 13:13:02 -06:00
return false;
ec_t ec;
#if !defined(_WIN32) || !defined(__i686)
connection_basic::socket_.next_layer().set_option(
boost::asio::detail::socket_option::integer<IPPROTO_IP, IP_TOS>{
connection_basic::get_tos_flag()
},
ec
);
if (ec.value())
return false;
#endif
connection_basic::socket_.next_layer().set_option(
boost::asio::ip::tcp::no_delay{false},
ec
);
if (ec.value())
return false;
connection_basic::m_is_multithreaded = is_multithreaded;
2022-07-06 17:47:34 -06:00
m_conn_context.set_details(
2021-06-28 13:13:02 -06:00
boost::uuids::random_generator()(),
*real_remote,
is_income,
connection_basic::m_ssl_support == ssl_support_t::e_ssl_support_enabled
);
2022-07-06 17:47:34 -06:00
m_host = real_remote->host_str();
2021-06-28 13:13:02 -06:00
try { host_count(1); } catch(...) { /* ignore */ }
2022-07-06 17:47:34 -06:00
m_local = real_remote->is_loopback() || real_remote->is_local();
m_state.ssl.enabled = (
2021-06-28 13:13:02 -06:00
connection_basic::m_ssl_support != ssl_support_t::e_ssl_support_disabled
);
2022-07-06 17:47:34 -06:00
m_state.ssl.forced = (
2021-06-28 13:13:02 -06:00
connection_basic::m_ssl_support == ssl_support_t::e_ssl_support_enabled
);
2022-07-06 17:47:34 -06:00
m_state.socket.connected = true;
m_state.status = status_t::RUNNING;
2021-06-28 13:13:02 -06:00
start_timer(
std::chrono::milliseconds(
2022-07-06 17:47:34 -06:00
m_local ? NEW_CONNECTION_TIMEOUT_LOCAL : NEW_CONNECTION_TIMEOUT_REMOTE
2021-06-28 13:13:02 -06:00
)
);
2022-07-06 17:47:34 -06:00
m_state.protocol.wait_init = true;
2021-06-28 13:13:02 -06:00
guard.unlock();
2022-07-06 17:47:34 -06:00
m_handler.after_init_connection();
2021-06-28 13:13:02 -06:00
guard.lock();
2022-07-06 17:47:34 -06:00
m_state.protocol.wait_init = false;
m_state.protocol.initialized = true;
if (m_state.status == status_t::INTERRUPTED)
2021-06-28 13:13:02 -06:00
on_interrupted();
2022-07-06 17:47:34 -06:00
else if (m_state.status == status_t::TERMINATING)
2021-06-28 13:13:02 -06:00
on_terminating();
2022-07-06 17:47:34 -06:00
else if (!is_income || !m_state.ssl.enabled)
2021-06-28 13:13:02 -06:00
start_read();
2018-05-26 12:34:13 -06:00
else
2021-06-28 13:13:02 -06:00
start_handshake();
return true;
2018-05-26 12:34:13 -06:00
}
2021-06-28 13:13:02 -06:00
template<typename T>
connection<T>::connection(
io_context_t &io_context,
std::shared_ptr<shared_state> shared_state,
t_connection_type connection_type,
ssl_support_t ssl_support
):
connection(
std::move(socket_t{io_context}),
std::move(shared_state),
connection_type,
ssl_support
)
2018-05-26 12:34:13 -06:00
{
}
2021-06-28 13:13:02 -06:00
template<typename T>
connection<T>::connection(
socket_t &&socket,
std::shared_ptr<shared_state> shared_state,
t_connection_type connection_type,
ssl_support_t ssl_support
):
connection_basic(std::move(socket), shared_state, ssl_support),
2022-07-06 17:47:34 -06:00
m_handler(this, *shared_state, m_conn_context),
m_connection_type(connection_type),
m_io_context{GET_IO_SERVICE(connection_basic::socket_)},
m_strand{m_io_context},
m_timers{m_io_context}
2018-06-07 05:43:10 -06:00
{
}
2021-06-28 13:13:02 -06:00
template<typename T>
connection<T>::~connection() noexcept(false)
2018-05-26 12:34:13 -06:00
{
2022-07-06 17:47:34 -06:00
std::lock_guard<std::mutex> guard(m_state.lock);
assert(m_state.status == status_t::TERMINATED ||
m_state.status == status_t::WASTED ||
m_io_context.stopped()
2021-06-28 13:13:02 -06:00
);
2022-07-06 17:47:34 -06:00
if (m_state.status != status_t::WASTED)
2019-12-16 11:24:29 -07:00
return;
2021-06-28 13:13:02 -06:00
try { host_count(-1); } catch (...) { /* ignore */ }
2018-05-26 12:34:13 -06:00
}
2021-06-28 13:13:02 -06:00
template<typename T>
bool connection<T>::start(
bool is_income,
bool is_multithreaded
)
2014-03-03 15:07:58 -07:00
{
2021-06-28 13:13:02 -06:00
return start_internal(is_income, is_multithreaded, {});
2014-03-03 15:07:58 -07:00
}
2021-06-28 13:13:02 -06:00
template<typename T>
bool connection<T>::start(
bool is_income,
bool is_multithreaded,
network_address real_remote
)
2014-03-03 15:07:58 -07:00
{
2021-06-28 13:13:02 -06:00
return start_internal(is_income, is_multithreaded, real_remote);
}
template<typename T>
void connection<T>::save_dbg_log()
{
2022-07-06 17:47:34 -06:00
std::lock_guard<std::mutex> guard(m_state.lock);
std::string address;
std::string port;
2021-06-28 13:13:02 -06:00
ec_t ec;
auto endpoint = connection_basic::socket().remote_endpoint(ec);
if (ec.value()) {
address = "<not connected>";
port = "<not connected>";
2014-03-03 15:07:58 -07:00
}
2021-06-28 13:13:02 -06:00
else {
address = endpoint.address().to_string();
port = std::to_string(endpoint.port());
}
MDEBUG(
2022-07-06 17:47:34 -06:00
" connection type " << std::to_string(m_connection_type) <<
2021-06-28 13:13:02 -06:00
" " << connection_basic::socket().local_endpoint().address().to_string() <<
":" << connection_basic::socket().local_endpoint().port() <<
2022-07-06 17:47:34 -06:00
" <--> " << m_conn_context.m_remote_address.str() <<
2021-06-28 13:13:02 -06:00
" (via " << address << ":" << port << ")"
);
2014-03-03 15:07:58 -07:00
}
2021-06-28 13:13:02 -06:00
template<typename T>
bool connection<T>::speed_limit_is_enabled() const
2018-06-07 05:43:10 -06:00
{
2022-07-06 17:47:34 -06:00
return m_connection_type != e_connection_type_RPC;
2018-06-07 05:43:10 -06:00
}
2021-06-28 13:13:02 -06:00
template<typename T>
bool connection<T>::cancel()
2015-12-22 05:31:22 -07:00
{
return close();
}
2021-06-28 13:13:02 -06:00
template<typename T>
bool connection<T>::do_send(byte_slice message)
2014-03-03 15:07:58 -07:00
{
2021-06-28 13:13:02 -06:00
return send(std::move(message));
}
2014-03-03 15:07:58 -07:00
2021-06-28 13:13:02 -06:00
template<typename T>
bool connection<T>::send_done()
{
return true;
}
2015-04-08 11:54:07 -06:00
2021-06-28 13:13:02 -06:00
template<typename T>
bool connection<T>::close()
{
2022-07-06 17:47:34 -06:00
std::lock_guard<std::mutex> guard(m_state.lock);
if (m_state.status != status_t::RUNNING)
2021-06-28 13:13:02 -06:00
return false;
terminate();
return true;
}
2015-04-08 11:54:07 -06:00
2021-06-28 13:13:02 -06:00
template<typename T>
bool connection<T>::call_run_once_service_io()
{
if(connection_basic::m_is_multithreaded) {
2022-07-06 17:47:34 -06:00
if (!m_io_context.poll_one())
2021-06-28 13:13:02 -06:00
misc_utils::sleep_no_w(1);
2014-03-03 15:07:58 -07:00
}
2021-06-28 13:13:02 -06:00
else {
2022-07-06 17:47:34 -06:00
if (!m_io_context.run_one())
2021-06-28 13:13:02 -06:00
return false;
2014-03-03 15:07:58 -07:00
}
2021-06-28 13:13:02 -06:00
return true;
}
2014-03-03 15:07:58 -07:00
2021-06-28 13:13:02 -06:00
template<typename T>
bool connection<T>::request_callback()
{
2022-07-06 17:47:34 -06:00
std::lock_guard<std::mutex> guard(m_state.lock);
if (m_state.status != status_t::RUNNING)
2021-06-28 13:13:02 -06:00
return false;
auto self = connection<T>::shared_from_this();
2022-07-06 17:47:34 -06:00
++m_state.protocol.wait_callback;
2021-06-28 13:13:02 -06:00
connection_basic::strand_.post([this, self]{
2022-07-06 17:47:34 -06:00
m_handler.handle_qued_callback();
std::lock_guard<std::mutex> guard(m_state.lock);
--m_state.protocol.wait_callback;
if (m_state.status == status_t::INTERRUPTED)
2021-06-28 13:13:02 -06:00
on_interrupted();
2022-07-06 17:47:34 -06:00
else if (m_state.status == status_t::TERMINATING)
2021-06-28 13:13:02 -06:00
on_terminating();
});
return true;
2014-03-03 15:07:58 -07:00
}
2015-04-08 11:54:07 -06:00
2021-06-28 13:13:02 -06:00
template<typename T>
typename connection<T>::io_context_t &connection<T>::get_io_service()
2015-01-05 12:30:17 -07:00
{
2022-07-06 17:47:34 -06:00
return m_io_context;
2015-01-05 12:30:17 -07:00
}
2015-04-08 11:54:07 -06:00
2021-06-28 13:13:02 -06:00
template<typename T>
bool connection<T>::add_ref()
{
try {
auto self = connection<T>::shared_from_this();
2022-07-06 17:47:34 -06:00
std::lock_guard<std::mutex> guard(m_state.lock);
2021-06-28 13:13:02 -06:00
this->self = std::move(self);
2022-07-06 17:47:34 -06:00
++m_state.protocol.reference_counter;
2021-06-28 13:13:02 -06:00
return true;
}
catch (boost::bad_weak_ptr &exception) {
return false;
}
}
2015-04-08 11:54:07 -06:00
2021-06-28 13:13:02 -06:00
template<typename T>
bool connection<T>::release()
{
connection_ptr self;
2022-07-06 17:47:34 -06:00
std::lock_guard<std::mutex> guard(m_state.lock);
if (!(--m_state.protocol.reference_counter))
2021-06-28 13:13:02 -06:00
self = std::move(this->self);
return true;
}
2015-04-08 11:54:07 -06:00
2021-06-28 13:13:02 -06:00
template<typename T>
void connection<T>::setRpcStation()
{
2022-07-06 17:47:34 -06:00
std::lock_guard<std::mutex> guard(m_state.lock);
m_connection_type = e_connection_type_RPC;
2021-06-28 13:13:02 -06:00
}
2015-04-08 11:54:07 -06:00
2014-03-03 15:07:58 -07:00
template<class t_protocol_handler>
2015-04-08 11:54:07 -06:00
boosted_tcp_server<t_protocol_handler>::boosted_tcp_server( t_connection_type connection_type ) :
2019-05-16 14:34:22 -06:00
m_state(std::make_shared<typename connection<t_protocol_handler>::shared_state>()),
2019-02-10 11:40:32 -07:00
m_io_service_local_instance(new worker()),
io_service_(m_io_service_local_instance->io_service),
2014-03-03 15:07:58 -07:00
acceptor_(io_service_),
2019-04-10 16:34:30 -06:00
acceptor_ipv6(io_service_),
2018-12-16 10:57:44 -07:00
default_remote(),
2015-01-05 12:30:17 -07:00
m_stop_signal_sent(false), m_port(0),
2018-12-16 10:57:44 -07:00
m_threads_count(0),
m_thread_index(0),
2015-04-08 11:54:07 -06:00
m_connection_type( connection_type ),
2019-04-10 16:34:30 -06:00
new_connection_(),
new_connection_ipv6()
2014-03-03 15:07:58 -07:00
{
2015-01-05 12:30:17 -07:00
create_server_type_map();
2014-03-03 15:07:58 -07:00
m_thread_name_prefix = "NET";
}
template<class t_protocol_handler>
2015-04-08 11:54:07 -06:00
boosted_tcp_server<t_protocol_handler>::boosted_tcp_server(boost::asio::io_service& extarnal_io_service, t_connection_type connection_type) :
2019-05-16 14:34:22 -06:00
m_state(std::make_shared<typename connection<t_protocol_handler>::shared_state>()),
2014-03-03 15:07:58 -07:00
io_service_(extarnal_io_service),
acceptor_(io_service_),
2019-04-10 16:34:30 -06:00
acceptor_ipv6(io_service_),
2018-12-16 10:57:44 -07:00
default_remote(),
m_stop_signal_sent(false), m_port(0),
m_threads_count(0),
m_thread_index(0),
2015-04-08 11:54:07 -06:00
m_connection_type(connection_type),
2019-04-10 16:34:30 -06:00
new_connection_(),
new_connection_ipv6()
2014-03-03 15:07:58 -07:00
{
2015-04-08 11:54:07 -06:00
create_server_type_map();
2014-03-03 15:07:58 -07:00
m_thread_name_prefix = "NET";
}
//---------------------------------------------------------------------------------
template<class t_protocol_handler>
boosted_tcp_server<t_protocol_handler>::~boosted_tcp_server()
{
this->send_stop_signal();
timed_wait_server_stop(10000);
}
//---------------------------------------------------------------------------------
template<class t_protocol_handler>
2015-01-05 12:30:17 -07:00
void boosted_tcp_server<t_protocol_handler>::create_server_type_map()
{
2015-04-08 11:54:07 -06:00
server_type_map["NET"] = e_connection_type_NET;
server_type_map["RPC"] = e_connection_type_RPC;
server_type_map["P2P"] = e_connection_type_P2P;
2015-01-05 12:30:17 -07:00
}
//---------------------------------------------------------------------------------
template<class t_protocol_handler>
2019-04-10 16:34:30 -06:00
bool boosted_tcp_server<t_protocol_handler>::init_server(uint32_t port, const std::string& address,
uint32_t port_ipv6, const std::string& address_ipv6, bool use_ipv6, bool require_ipv4,
ssl_options_t ssl_options)
2014-03-03 15:07:58 -07:00
{
TRY_ENTRY();
m_stop_signal_sent = false;
m_port = port;
2019-04-10 16:34:30 -06:00
m_port_ipv6 = port_ipv6;
2014-03-03 15:07:58 -07:00
m_address = address;
2019-04-10 16:34:30 -06:00
m_address_ipv6 = address_ipv6;
m_use_ipv6 = use_ipv6;
m_require_ipv4 = require_ipv4;
2019-03-14 22:03:32 -06:00
if (ssl_options)
m_state->configure_ssl(std::move(ssl_options));
2019-04-10 16:34:30 -06:00
std::string ipv4_failed = "";
std::string ipv6_failed = "";
try
{
boost::asio::ip::tcp::resolver resolver(io_service_);
boost::asio::ip::tcp::resolver::query query(address, boost::lexical_cast<std::string>(port), boost::asio::ip::tcp::resolver::query::canonical_name);
boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
acceptor_.open(endpoint.protocol());
2019-10-22 06:27:41 -06:00
#if !defined(_WIN32)
acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
#endif
2019-04-10 16:34:30 -06:00
acceptor_.bind(endpoint);
acceptor_.listen();
boost::asio::ip::tcp::endpoint binded_endpoint = acceptor_.local_endpoint();
m_port = binded_endpoint.port();
MDEBUG("start accept (IPv4)");
new_connection_.reset(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, m_state->ssl_options().support));
acceptor_.async_accept(new_connection_->socket(),
boost::bind(&boosted_tcp_server<t_protocol_handler>::handle_accept_ipv4, this,
boost::asio::placeholders::error));
}
catch (const std::exception &e)
{
ipv4_failed = e.what();
}
if (ipv4_failed != "")
{
MERROR("Failed to bind IPv4: " << ipv4_failed);
if (require_ipv4)
{
throw std::runtime_error("Failed to bind IPv4 (set to required)");
}
}
if (use_ipv6)
{
try
{
if (port_ipv6 == 0) port_ipv6 = port; // default arg means bind to same port as ipv4
boost::asio::ip::tcp::resolver resolver(io_service_);
boost::asio::ip::tcp::resolver::query query(address_ipv6, boost::lexical_cast<std::string>(port_ipv6), boost::asio::ip::tcp::resolver::query::canonical_name);
boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
acceptor_ipv6.open(endpoint.protocol());
2019-10-22 06:27:41 -06:00
#if !defined(_WIN32)
acceptor_ipv6.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
#endif
2019-04-10 16:34:30 -06:00
acceptor_ipv6.set_option(boost::asio::ip::v6_only(true));
acceptor_ipv6.bind(endpoint);
acceptor_ipv6.listen();
boost::asio::ip::tcp::endpoint binded_endpoint = acceptor_ipv6.local_endpoint();
m_port_ipv6 = binded_endpoint.port();
MDEBUG("start accept (IPv6)");
new_connection_ipv6.reset(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, m_state->ssl_options().support));
acceptor_ipv6.async_accept(new_connection_ipv6->socket(),
boost::bind(&boosted_tcp_server<t_protocol_handler>::handle_accept_ipv6, this,
boost::asio::placeholders::error));
}
catch (const std::exception &e)
{
ipv6_failed = e.what();
}
}
if (use_ipv6 && ipv6_failed != "")
{
MERROR("Failed to bind IPv6: " << ipv6_failed);
if (ipv4_failed != "")
{
throw std::runtime_error("Failed to bind IPv4 and IPv6");
}
}
2014-03-03 15:07:58 -07:00
return true;
2017-12-13 07:03:46 -07:00
}
catch (const std::exception &e)
{
MFATAL("Error starting server: " << e.what());
return false;
}
catch (...)
{
MFATAL("Error starting server");
return false;
}
2014-03-03 15:07:58 -07:00
}
//-----------------------------------------------------------------------------
template<class t_protocol_handler>
2019-04-10 16:34:30 -06:00
bool boosted_tcp_server<t_protocol_handler>::init_server(const std::string port, const std::string& address,
const std::string port_ipv6, const std::string address_ipv6, bool use_ipv6, bool require_ipv4,
ssl_options_t ssl_options)
2014-03-03 15:07:58 -07:00
{
uint32_t p = 0;
2019-04-10 16:34:30 -06:00
uint32_t p_ipv6 = 0;
2014-03-03 15:07:58 -07:00
if (port.size() && !string_tools::get_xtype_from_string(p, port)) {
Change logging to easylogging++
This replaces the epee and data_loggers logging systems with
a single one, and also adds filename:line and explicit severity
levels. Categories may be defined, and logging severity set
by category (or set of categories). epee style 0-4 log level
maps to a sensible severity configuration. Log files now also
rotate when reaching 100 MB.
To select which logs to output, use the MONERO_LOGS environment
variable, with a comma separated list of categories (globs are
supported), with their requested severity level after a colon.
If a log matches more than one such setting, the last one in
the configuration string applies. A few examples:
This one is (mostly) silent, only outputting fatal errors:
MONERO_LOGS=*:FATAL
This one is very verbose:
MONERO_LOGS=*:TRACE
This one is totally silent (logwise):
MONERO_LOGS=""
This one outputs all errors and warnings, except for the
"verify" category, which prints just fatal errors (the verify
category is used for logs about incoming transactions and
blocks, and it is expected that some/many will fail to verify,
hence we don't want the spam):
MONERO_LOGS=*:WARNING,verify:FATAL
Log levels are, in decreasing order of priority:
FATAL, ERROR, WARNING, INFO, DEBUG, TRACE
Subcategories may be added using prefixes and globs. This
example will output net.p2p logs at the TRACE level, but all
other net* logs only at INFO:
MONERO_LOGS=*:ERROR,net*:INFO,net.p2p:TRACE
Logs which are intended for the user (which Monero was using
a lot through epee, but really isn't a nice way to go things)
should use the "global" category. There are a few helper macros
for using this category, eg: MGINFO("this shows up by default")
or MGINFO_RED("this is red"), to try to keep a similar look
and feel for now.
Existing epee log macros still exist, and map to the new log
levels, but since they're used as a "user facing" UI element
as much as a logging system, they often don't map well to log
severities (ie, a log level 0 log may be an error, or may be
something we want the user to see, such as an important info).
In those cases, I tried to use the new macros. In other cases,
I left the existing macros in. When modifying logs, it is
probably best to switch to the new macros with explicit levels.
The --log-level options and set_log commands now also accept
category settings, in addition to the epee style log levels.
2017-01-01 09:34:23 -07:00
MERROR("Failed to convert port no = " << port);
2014-03-03 15:07:58 -07:00
return false;
}
2019-04-10 16:34:30 -06:00
if (port_ipv6.size() && !string_tools::get_xtype_from_string(p_ipv6, port_ipv6)) {
MERROR("Failed to convert port no = " << port_ipv6);
return false;
}
return this->init_server(p, address, p_ipv6, address_ipv6, use_ipv6, require_ipv4, std::move(ssl_options));
2014-03-03 15:07:58 -07:00
}
//---------------------------------------------------------------------------------
template<class t_protocol_handler>
bool boosted_tcp_server<t_protocol_handler>::worker_thread()
{
TRY_ENTRY();
2022-03-30 12:18:32 -06:00
const uint32_t local_thr_index = m_thread_index++; // atomically increment, getting value before increment
2014-03-03 15:07:58 -07:00
std::string thread_name = std::string("[") + m_thread_name_prefix;
thread_name += boost::to_string(local_thr_index) + "]";
Change logging to easylogging++
This replaces the epee and data_loggers logging systems with
a single one, and also adds filename:line and explicit severity
levels. Categories may be defined, and logging severity set
by category (or set of categories). epee style 0-4 log level
maps to a sensible severity configuration. Log files now also
rotate when reaching 100 MB.
To select which logs to output, use the MONERO_LOGS environment
variable, with a comma separated list of categories (globs are
supported), with their requested severity level after a colon.
If a log matches more than one such setting, the last one in
the configuration string applies. A few examples:
This one is (mostly) silent, only outputting fatal errors:
MONERO_LOGS=*:FATAL
This one is very verbose:
MONERO_LOGS=*:TRACE
This one is totally silent (logwise):
MONERO_LOGS=""
This one outputs all errors and warnings, except for the
"verify" category, which prints just fatal errors (the verify
category is used for logs about incoming transactions and
blocks, and it is expected that some/many will fail to verify,
hence we don't want the spam):
MONERO_LOGS=*:WARNING,verify:FATAL
Log levels are, in decreasing order of priority:
FATAL, ERROR, WARNING, INFO, DEBUG, TRACE
Subcategories may be added using prefixes and globs. This
example will output net.p2p logs at the TRACE level, but all
other net* logs only at INFO:
MONERO_LOGS=*:ERROR,net*:INFO,net.p2p:TRACE
Logs which are intended for the user (which Monero was using
a lot through epee, but really isn't a nice way to go things)
should use the "global" category. There are a few helper macros
for using this category, eg: MGINFO("this shows up by default")
or MGINFO_RED("this is red"), to try to keep a similar look
and feel for now.
Existing epee log macros still exist, and map to the new log
levels, but since they're used as a "user facing" UI element
as much as a logging system, they often don't map well to log
severities (ie, a log level 0 log may be an error, or may be
something we want the user to see, such as an important info).
In those cases, I tried to use the new macros. In other cases,
I left the existing macros in. When modifying logs, it is
probably best to switch to the new macros with explicit levels.
The --log-level options and set_log commands now also accept
category settings, in addition to the epee style log levels.
2017-01-01 09:34:23 -07:00
MLOG_SET_THREAD_NAME(thread_name);
2015-01-05 12:30:17 -07:00
// _fact("Thread name: " << m_thread_name_prefix);
2014-03-03 15:07:58 -07:00
while(!m_stop_signal_sent)
{
try
{
2019-02-10 11:40:32 -07:00
io_service_.run();
return true;
2014-03-03 15:07:58 -07:00
}
catch(const std::exception& ex)
{
2015-01-05 12:30:17 -07:00
_erro("Exception at server worker thread, what=" << ex.what());
2014-03-03 15:07:58 -07:00
}
catch(...)
{
2015-01-05 12:30:17 -07:00
_erro("Exception at server worker thread, unknown execption");
2014-03-03 15:07:58 -07:00
}
}
2015-01-05 12:30:17 -07:00
//_info("Worker thread finished");
2014-03-03 15:07:58 -07:00
return true;
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::worker_thread", false);
}
//---------------------------------------------------------------------------------
template<class t_protocol_handler>
void boosted_tcp_server<t_protocol_handler>::set_threads_prefix(const std::string& prefix_name)
{
m_thread_name_prefix = prefix_name;
2015-04-08 11:54:07 -06:00
auto it = server_type_map.find(m_thread_name_prefix);
2016-03-21 04:12:12 -06:00
if (it==server_type_map.end()) throw std::runtime_error("Unknown prefix/server type:" + std::string(prefix_name));
2015-04-08 11:54:07 -06:00
auto connection_type = it->second; // the value of type
Change logging to easylogging++
This replaces the epee and data_loggers logging systems with
a single one, and also adds filename:line and explicit severity
levels. Categories may be defined, and logging severity set
by category (or set of categories). epee style 0-4 log level
maps to a sensible severity configuration. Log files now also
rotate when reaching 100 MB.
To select which logs to output, use the MONERO_LOGS environment
variable, with a comma separated list of categories (globs are
supported), with their requested severity level after a colon.
If a log matches more than one such setting, the last one in
the configuration string applies. A few examples:
This one is (mostly) silent, only outputting fatal errors:
MONERO_LOGS=*:FATAL
This one is very verbose:
MONERO_LOGS=*:TRACE
This one is totally silent (logwise):
MONERO_LOGS=""
This one outputs all errors and warnings, except for the
"verify" category, which prints just fatal errors (the verify
category is used for logs about incoming transactions and
blocks, and it is expected that some/many will fail to verify,
hence we don't want the spam):
MONERO_LOGS=*:WARNING,verify:FATAL
Log levels are, in decreasing order of priority:
FATAL, ERROR, WARNING, INFO, DEBUG, TRACE
Subcategories may be added using prefixes and globs. This
example will output net.p2p logs at the TRACE level, but all
other net* logs only at INFO:
MONERO_LOGS=*:ERROR,net*:INFO,net.p2p:TRACE
Logs which are intended for the user (which Monero was using
a lot through epee, but really isn't a nice way to go things)
should use the "global" category. There are a few helper macros
for using this category, eg: MGINFO("this shows up by default")
or MGINFO_RED("this is red"), to try to keep a similar look
and feel for now.
Existing epee log macros still exist, and map to the new log
levels, but since they're used as a "user facing" UI element
as much as a logging system, they often don't map well to log
severities (ie, a log level 0 log may be an error, or may be
something we want the user to see, such as an important info).
In those cases, I tried to use the new macros. In other cases,
I left the existing macros in. When modifying logs, it is
probably best to switch to the new macros with explicit levels.
The --log-level options and set_log commands now also accept
category settings, in addition to the epee style log levels.
2017-01-01 09:34:23 -07:00
MINFO("Set server type to: " << connection_type << " from name: " << m_thread_name_prefix << ", prefix_name = " << prefix_name);
2014-03-03 15:07:58 -07:00
}
//---------------------------------------------------------------------------------
template<class t_protocol_handler>
void boosted_tcp_server<t_protocol_handler>::set_connection_filter(i_connection_filter* pfilter)
{
2018-12-16 10:57:44 -07:00
assert(m_state != nullptr); // always set in constructor
m_state->pfilter = pfilter;
2014-03-03 15:07:58 -07:00
}
//---------------------------------------------------------------------------------
template<class t_protocol_handler>
2014-04-30 14:50:06 -06:00
bool boosted_tcp_server<t_protocol_handler>::run_server(size_t threads_count, bool wait, const boost::thread::attributes& attrs)
2014-03-03 15:07:58 -07:00
{
TRY_ENTRY();
m_threads_count = threads_count;
m_main_thread_id = boost::this_thread::get_id();
Change logging to easylogging++
This replaces the epee and data_loggers logging systems with
a single one, and also adds filename:line and explicit severity
levels. Categories may be defined, and logging severity set
by category (or set of categories). epee style 0-4 log level
maps to a sensible severity configuration. Log files now also
rotate when reaching 100 MB.
To select which logs to output, use the MONERO_LOGS environment
variable, with a comma separated list of categories (globs are
supported), with their requested severity level after a colon.
If a log matches more than one such setting, the last one in
the configuration string applies. A few examples:
This one is (mostly) silent, only outputting fatal errors:
MONERO_LOGS=*:FATAL
This one is very verbose:
MONERO_LOGS=*:TRACE
This one is totally silent (logwise):
MONERO_LOGS=""
This one outputs all errors and warnings, except for the
"verify" category, which prints just fatal errors (the verify
category is used for logs about incoming transactions and
blocks, and it is expected that some/many will fail to verify,
hence we don't want the spam):
MONERO_LOGS=*:WARNING,verify:FATAL
Log levels are, in decreasing order of priority:
FATAL, ERROR, WARNING, INFO, DEBUG, TRACE
Subcategories may be added using prefixes and globs. This
example will output net.p2p logs at the TRACE level, but all
other net* logs only at INFO:
MONERO_LOGS=*:ERROR,net*:INFO,net.p2p:TRACE
Logs which are intended for the user (which Monero was using
a lot through epee, but really isn't a nice way to go things)
should use the "global" category. There are a few helper macros
for using this category, eg: MGINFO("this shows up by default")
or MGINFO_RED("this is red"), to try to keep a similar look
and feel for now.
Existing epee log macros still exist, and map to the new log
levels, but since they're used as a "user facing" UI element
as much as a logging system, they often don't map well to log
severities (ie, a log level 0 log may be an error, or may be
something we want the user to see, such as an important info).
In those cases, I tried to use the new macros. In other cases,
I left the existing macros in. When modifying logs, it is
probably best to switch to the new macros with explicit levels.
The --log-level options and set_log commands now also accept
category settings, in addition to the epee style log levels.
2017-01-01 09:34:23 -07:00
MLOG_SET_THREAD_NAME("[SRV_MAIN]");
2014-03-03 15:07:58 -07:00
while(!m_stop_signal_sent)
{
// Create a pool of threads to run all of the io_services.
CRITICAL_REGION_BEGIN(m_threads_lock);
for (std::size_t i = 0; i < threads_count; ++i)
{
boost::shared_ptr<boost::thread> thread(new boost::thread(
2014-04-30 14:50:06 -06:00
attrs, boost::bind(&boosted_tcp_server<t_protocol_handler>::worker_thread, this)));
2015-01-05 12:30:17 -07:00
_note("Run server thread name: " << m_thread_name_prefix);
2014-03-03 15:07:58 -07:00
m_threads.push_back(thread);
}
CRITICAL_REGION_END();
// Wait for all threads in the pool to exit.
2016-10-02 19:06:55 -06:00
if (wait)
2014-03-03 15:07:58 -07:00
{
2015-01-05 12:30:17 -07:00
_fact("JOINING all threads");
for (std::size_t i = 0; i < m_threads.size(); ++i) {
m_threads[i]->join();
}
_fact("JOINING all threads - almost");
2014-03-03 15:07:58 -07:00
m_threads.clear();
2015-01-05 12:30:17 -07:00
_fact("JOINING all threads - DONE");
2014-03-03 15:07:58 -07:00
2015-01-05 12:30:17 -07:00
}
else {
_dbg1("Reiniting OK.");
2014-03-03 15:07:58 -07:00
return true;
}
if(wait && !m_stop_signal_sent)
{
//some problems with the listening socket ?..
2015-01-05 12:30:17 -07:00
_dbg1("Net service stopped without stop request, restarting...");
2019-04-10 16:34:30 -06:00
if(!this->init_server(m_port, m_address, m_port_ipv6, m_address_ipv6, m_use_ipv6, m_require_ipv4))
2014-03-03 15:07:58 -07:00
{
2015-01-05 12:30:17 -07:00
_dbg1("Reiniting service failed, exit.");
2014-03-03 15:07:58 -07:00
return false;
}else
{
2015-01-05 12:30:17 -07:00
_dbg1("Reiniting OK.");
2014-03-03 15:07:58 -07:00
}
}
}
return true;
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::run_server", false);
}
//---------------------------------------------------------------------------------
template<class t_protocol_handler>
bool boosted_tcp_server<t_protocol_handler>::is_thread_worker()
{
TRY_ENTRY();
CRITICAL_REGION_LOCAL(m_threads_lock);
BOOST_FOREACH(boost::shared_ptr<boost::thread>& thp, m_threads)
{
if(thp->get_id() == boost::this_thread::get_id())
return true;
}
if(m_threads_count == 1 && boost::this_thread::get_id() == m_main_thread_id)
return true;
return false;
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::is_thread_worker", false);
}
//---------------------------------------------------------------------------------
template<class t_protocol_handler>
2014-03-20 05:46:11 -06:00
bool boosted_tcp_server<t_protocol_handler>::timed_wait_server_stop(uint64_t wait_mseconds)
2014-03-03 15:07:58 -07:00
{
TRY_ENTRY();
boost::chrono::milliseconds ms(wait_mseconds);
for (std::size_t i = 0; i < m_threads.size(); ++i)
{
if(m_threads[i]->joinable() && !m_threads[i]->try_join_for(ms))
{
2015-01-05 12:30:17 -07:00
_dbg1("Interrupting thread " << m_threads[i]->native_handle());
2014-03-03 15:07:58 -07:00
m_threads[i]->interrupt();
}
}
return true;
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::timed_wait_server_stop", false);
}
//---------------------------------------------------------------------------------
template<class t_protocol_handler>
void boosted_tcp_server<t_protocol_handler>::send_stop_signal()
{
m_stop_signal_sent = true;
2019-04-12 12:12:04 -06:00
typename connection<t_protocol_handler>::shared_state *state = static_cast<typename connection<t_protocol_handler>::shared_state*>(m_state.get());
state->stop_signal_sent = true;
2014-03-03 15:07:58 -07:00
TRY_ENTRY();
2015-12-22 05:31:22 -07:00
connections_mutex.lock();
for (auto &c: connections_)
{
2017-08-16 08:41:34 -06:00
c->cancel();
2015-12-22 05:31:22 -07:00
}
2015-12-23 04:19:16 -07:00
connections_.clear();
2015-12-22 05:31:22 -07:00
connections_mutex.unlock();
2014-03-03 15:07:58 -07:00
io_service_.stop();
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::send_stop_signal()", void());
}
//---------------------------------------------------------------------------------
template<class t_protocol_handler>
2019-04-10 16:34:30 -06:00
void boosted_tcp_server<t_protocol_handler>::handle_accept_ipv4(const boost::system::error_code& e)
{
this->handle_accept(e, false);
}
//---------------------------------------------------------------------------------
template<class t_protocol_handler>
void boosted_tcp_server<t_protocol_handler>::handle_accept_ipv6(const boost::system::error_code& e)
{
this->handle_accept(e, true);
}
//---------------------------------------------------------------------------------
template<class t_protocol_handler>
void boosted_tcp_server<t_protocol_handler>::handle_accept(const boost::system::error_code& e, bool ipv6)
2014-03-03 15:07:58 -07:00
{
Change logging to easylogging++
This replaces the epee and data_loggers logging systems with
a single one, and also adds filename:line and explicit severity
levels. Categories may be defined, and logging severity set
by category (or set of categories). epee style 0-4 log level
maps to a sensible severity configuration. Log files now also
rotate when reaching 100 MB.
To select which logs to output, use the MONERO_LOGS environment
variable, with a comma separated list of categories (globs are
supported), with their requested severity level after a colon.
If a log matches more than one such setting, the last one in
the configuration string applies. A few examples:
This one is (mostly) silent, only outputting fatal errors:
MONERO_LOGS=*:FATAL
This one is very verbose:
MONERO_LOGS=*:TRACE
This one is totally silent (logwise):
MONERO_LOGS=""
This one outputs all errors and warnings, except for the
"verify" category, which prints just fatal errors (the verify
category is used for logs about incoming transactions and
blocks, and it is expected that some/many will fail to verify,
hence we don't want the spam):
MONERO_LOGS=*:WARNING,verify:FATAL
Log levels are, in decreasing order of priority:
FATAL, ERROR, WARNING, INFO, DEBUG, TRACE
Subcategories may be added using prefixes and globs. This
example will output net.p2p logs at the TRACE level, but all
other net* logs only at INFO:
MONERO_LOGS=*:ERROR,net*:INFO,net.p2p:TRACE
Logs which are intended for the user (which Monero was using
a lot through epee, but really isn't a nice way to go things)
should use the "global" category. There are a few helper macros
for using this category, eg: MGINFO("this shows up by default")
or MGINFO_RED("this is red"), to try to keep a similar look
and feel for now.
Existing epee log macros still exist, and map to the new log
levels, but since they're used as a "user facing" UI element
as much as a logging system, they often don't map well to log
severities (ie, a log level 0 log may be an error, or may be
something we want the user to see, such as an important info).
In those cases, I tried to use the new macros. In other cases,
I left the existing macros in. When modifying logs, it is
probably best to switch to the new macros with explicit levels.
The --log-level options and set_log commands now also accept
category settings, in addition to the epee style log levels.
2017-01-01 09:34:23 -07:00
MDEBUG("handle_accept");
2019-04-10 16:34:30 -06:00
boost::asio::ip::tcp::acceptor* current_acceptor = &acceptor_;
connection_ptr* current_new_connection = &new_connection_;
auto accept_function_pointer = &boosted_tcp_server<t_protocol_handler>::handle_accept_ipv4;
if (ipv6)
{
current_acceptor = &acceptor_ipv6;
current_new_connection = &new_connection_ipv6;
accept_function_pointer = &boosted_tcp_server<t_protocol_handler>::handle_accept_ipv6;
}
2018-06-12 00:25:01 -06:00
try
{
2014-03-03 15:07:58 -07:00
if (!e)
{
2019-04-10 16:34:30 -06:00
if (m_connection_type == e_connection_type_RPC) {
const char *ssl_message = "unknown";
switch ((*current_new_connection)->get_ssl_support())
{
case epee::net_utils::ssl_support_t::e_ssl_support_disabled: ssl_message = "disabled"; break;
case epee::net_utils::ssl_support_t::e_ssl_support_enabled: ssl_message = "enabled"; break;
case epee::net_utils::ssl_support_t::e_ssl_support_autodetect: ssl_message = "autodetection"; break;
}
MDEBUG("New server for RPC connections, SSL " << ssl_message);
(*current_new_connection)->setRpcStation(); // hopefully this is not needed actually
}
connection_ptr conn(std::move((*current_new_connection)));
(*current_new_connection).reset(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, conn->get_ssl_support()));
current_acceptor->async_accept((*current_new_connection)->socket(),
boost::bind(accept_function_pointer, this,
boost::asio::placeholders::error));
2014-03-03 15:07:58 -07:00
2018-02-11 16:20:24 -07:00
boost::asio::socket_base::keep_alive opt(true);
conn->socket().set_option(opt);
epee: add SSL support
RPC connections now have optional tranparent SSL.
An optional private key and certificate file can be passed,
using the --{rpc,daemon}-ssl-private-key and
--{rpc,daemon}-ssl-certificate options. Those have as
argument a path to a PEM format private private key and
certificate, respectively.
If not given, a temporary self signed certificate will be used.
SSL can be enabled or disabled using --{rpc}-ssl, which
accepts autodetect (default), disabled or enabled.
Access can be restricted to particular certificates using the
--rpc-ssl-allowed-certificates, which takes a list of
paths to PEM encoded certificates. This can allow a wallet to
connect to only the daemon they think they're connected to,
by forcing SSL and listing the paths to the known good
certificates.
To generate long term certificates:
openssl genrsa -out /tmp/KEY 4096
openssl req -new -key /tmp/KEY -out /tmp/REQ
openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT
/tmp/KEY is the private key, and /tmp/CERT is the certificate,
both in PEM format. /tmp/REQ can be removed. Adjust the last
command to set expiration date, etc, as needed. It doesn't
make a whole lot of sense for monero anyway, since most servers
will run with one time temporary self signed certificates anyway.
SSL support is transparent, so all communication is done on the
existing ports, with SSL autodetection. This means you can start
using an SSL daemon now, but you should not enforce SSL yet or
nothing will talk to you.
2018-06-14 16:44:48 -06:00
bool res;
2018-12-16 10:57:44 -07:00
if (default_remote.get_type_id() == net_utils::address_type::invalid)
epee: add SSL support
RPC connections now have optional tranparent SSL.
An optional private key and certificate file can be passed,
using the --{rpc,daemon}-ssl-private-key and
--{rpc,daemon}-ssl-certificate options. Those have as
argument a path to a PEM format private private key and
certificate, respectively.
If not given, a temporary self signed certificate will be used.
SSL can be enabled or disabled using --{rpc}-ssl, which
accepts autodetect (default), disabled or enabled.
Access can be restricted to particular certificates using the
--rpc-ssl-allowed-certificates, which takes a list of
paths to PEM encoded certificates. This can allow a wallet to
connect to only the daemon they think they're connected to,
by forcing SSL and listing the paths to the known good
certificates.
To generate long term certificates:
openssl genrsa -out /tmp/KEY 4096
openssl req -new -key /tmp/KEY -out /tmp/REQ
openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT
/tmp/KEY is the private key, and /tmp/CERT is the certificate,
both in PEM format. /tmp/REQ can be removed. Adjust the last
command to set expiration date, etc, as needed. It doesn't
make a whole lot of sense for monero anyway, since most servers
will run with one time temporary self signed certificates anyway.
SSL support is transparent, so all communication is done on the
existing ports, with SSL autodetection. This means you can start
using an SSL daemon now, but you should not enforce SSL yet or
nothing will talk to you.
2018-06-14 16:44:48 -06:00
res = conn->start(true, 1 < m_threads_count);
2018-12-16 10:57:44 -07:00
else
epee: add SSL support
RPC connections now have optional tranparent SSL.
An optional private key and certificate file can be passed,
using the --{rpc,daemon}-ssl-private-key and
--{rpc,daemon}-ssl-certificate options. Those have as
argument a path to a PEM format private private key and
certificate, respectively.
If not given, a temporary self signed certificate will be used.
SSL can be enabled or disabled using --{rpc}-ssl, which
accepts autodetect (default), disabled or enabled.
Access can be restricted to particular certificates using the
--rpc-ssl-allowed-certificates, which takes a list of
paths to PEM encoded certificates. This can allow a wallet to
connect to only the daemon they think they're connected to,
by forcing SSL and listing the paths to the known good
certificates.
To generate long term certificates:
openssl genrsa -out /tmp/KEY 4096
openssl req -new -key /tmp/KEY -out /tmp/REQ
openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT
/tmp/KEY is the private key, and /tmp/CERT is the certificate,
both in PEM format. /tmp/REQ can be removed. Adjust the last
command to set expiration date, etc, as needed. It doesn't
make a whole lot of sense for monero anyway, since most servers
will run with one time temporary self signed certificates anyway.
SSL support is transparent, so all communication is done on the
existing ports, with SSL autodetection. This means you can start
using an SSL daemon now, but you should not enforce SSL yet or
nothing will talk to you.
2018-06-14 16:44:48 -06:00
res = conn->start(true, 1 < m_threads_count, default_remote);
if (!res)
{
conn->cancel();
return;
}
2015-04-08 11:54:07 -06:00
conn->save_dbg_log();
2018-06-12 00:25:01 -06:00
return;
}
else
{
MERROR("Error in boosted_tcp_server<t_protocol_handler>::handle_accept: " << e);
}
}
catch (const std::exception &e)
2014-03-03 15:07:58 -07:00
{
2018-06-12 00:25:01 -06:00
MERROR("Exception in boosted_tcp_server<t_protocol_handler>::handle_accept: " << e.what());
2014-03-03 15:07:58 -07:00
}
2018-06-12 00:25:01 -06:00
// error path, if e or exception
2018-12-16 10:57:44 -07:00
assert(m_state != nullptr); // always set in constructor
_erro("Some problems at accept: " << e.message() << ", connections_count = " << m_state->sock_count);
2018-06-12 00:25:01 -06:00
misc_utils::sleep_no_w(100);
2019-04-10 16:34:30 -06:00
(*current_new_connection).reset(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, (*current_new_connection)->get_ssl_support()));
current_acceptor->async_accept((*current_new_connection)->socket(),
boost::bind(accept_function_pointer, this,
boost::asio::placeholders::error));
2014-03-03 15:07:58 -07:00
}
//---------------------------------------------------------------------------------
template<class t_protocol_handler>
epee: add SSL support
RPC connections now have optional tranparent SSL.
An optional private key and certificate file can be passed,
using the --{rpc,daemon}-ssl-private-key and
--{rpc,daemon}-ssl-certificate options. Those have as
argument a path to a PEM format private private key and
certificate, respectively.
If not given, a temporary self signed certificate will be used.
SSL can be enabled or disabled using --{rpc}-ssl, which
accepts autodetect (default), disabled or enabled.
Access can be restricted to particular certificates using the
--rpc-ssl-allowed-certificates, which takes a list of
paths to PEM encoded certificates. This can allow a wallet to
connect to only the daemon they think they're connected to,
by forcing SSL and listing the paths to the known good
certificates.
To generate long term certificates:
openssl genrsa -out /tmp/KEY 4096
openssl req -new -key /tmp/KEY -out /tmp/REQ
openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT
/tmp/KEY is the private key, and /tmp/CERT is the certificate,
both in PEM format. /tmp/REQ can be removed. Adjust the last
command to set expiration date, etc, as needed. It doesn't
make a whole lot of sense for monero anyway, since most servers
will run with one time temporary self signed certificates anyway.
SSL support is transparent, so all communication is done on the
existing ports, with SSL autodetection. This means you can start
using an SSL daemon now, but you should not enforce SSL yet or
nothing will talk to you.
2018-06-14 16:44:48 -06:00
bool boosted_tcp_server<t_protocol_handler>::add_connection(t_connection_context& out, boost::asio::ip::tcp::socket&& sock, network_address real_remote, epee::net_utils::ssl_support_t ssl_support)
2018-12-16 10:57:44 -07:00
{
2019-03-21 05:01:15 -06:00
if(std::addressof(get_io_service()) == std::addressof(GET_IO_SERVICE(sock)))
2018-12-16 10:57:44 -07:00
{
2019-03-14 22:03:32 -06:00
connection_ptr conn(new connection<t_protocol_handler>(std::move(sock), m_state, m_connection_type, ssl_support));
2018-12-16 10:57:44 -07:00
if(conn->start(false, 1 < m_threads_count, std::move(real_remote)))
{
conn->get_context(out);
conn->save_dbg_log();
return true;
}
}
else
{
MWARNING(out << " was not added, socket/io_service mismatch");
}
return false;
}
//---------------------------------------------------------------------------------
template<class t_protocol_handler>
epee: add SSL support
RPC connections now have optional tranparent SSL.
An optional private key and certificate file can be passed,
using the --{rpc,daemon}-ssl-private-key and
--{rpc,daemon}-ssl-certificate options. Those have as
argument a path to a PEM format private private key and
certificate, respectively.
If not given, a temporary self signed certificate will be used.
SSL can be enabled or disabled using --{rpc}-ssl, which
accepts autodetect (default), disabled or enabled.
Access can be restricted to particular certificates using the
--rpc-ssl-allowed-certificates, which takes a list of
paths to PEM encoded certificates. This can allow a wallet to
connect to only the daemon they think they're connected to,
by forcing SSL and listing the paths to the known good
certificates.
To generate long term certificates:
openssl genrsa -out /tmp/KEY 4096
openssl req -new -key /tmp/KEY -out /tmp/REQ
openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT
/tmp/KEY is the private key, and /tmp/CERT is the certificate,
both in PEM format. /tmp/REQ can be removed. Adjust the last
command to set expiration date, etc, as needed. It doesn't
make a whole lot of sense for monero anyway, since most servers
will run with one time temporary self signed certificates anyway.
SSL support is transparent, so all communication is done on the
existing ports, with SSL autodetection. This means you can start
using an SSL daemon now, but you should not enforce SSL yet or
nothing will talk to you.
2018-06-14 16:44:48 -06:00
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)
2014-03-03 15:07:58 -07:00
{
TRY_ENTRY();
sock_.open(remote_endpoint.protocol());
if(bind_ip != "0.0.0.0" && bind_ip != "0" && bind_ip != "" )
{
2018-09-09 04:46:58 -06:00
boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(bind_ip.c_str()), 0);
2018-08-27 10:29:29 -06:00
boost::system::error_code ec;
sock_.bind(local_endpoint, ec);
if (ec)
{
2018-09-09 04:46:58 -06:00
MERROR("Error binding to " << bind_ip << ": " << ec.message());
2018-08-27 10:29:29 -06:00
if (sock_.is_open())
sock_.close();
epee: add SSL support
RPC connections now have optional tranparent SSL.
An optional private key and certificate file can be passed,
using the --{rpc,daemon}-ssl-private-key and
--{rpc,daemon}-ssl-certificate options. Those have as
argument a path to a PEM format private private key and
certificate, respectively.
If not given, a temporary self signed certificate will be used.
SSL can be enabled or disabled using --{rpc}-ssl, which
accepts autodetect (default), disabled or enabled.
Access can be restricted to particular certificates using the
--rpc-ssl-allowed-certificates, which takes a list of
paths to PEM encoded certificates. This can allow a wallet to
connect to only the daemon they think they're connected to,
by forcing SSL and listing the paths to the known good
certificates.
To generate long term certificates:
openssl genrsa -out /tmp/KEY 4096
openssl req -new -key /tmp/KEY -out /tmp/REQ
openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT
/tmp/KEY is the private key, and /tmp/CERT is the certificate,
both in PEM format. /tmp/REQ can be removed. Adjust the last
command to set expiration date, etc, as needed. It doesn't
make a whole lot of sense for monero anyway, since most servers
will run with one time temporary self signed certificates anyway.
SSL support is transparent, so all communication is done on the
existing ports, with SSL autodetection. This means you can start
using an SSL daemon now, but you should not enforce SSL yet or
nothing will talk to you.
2018-06-14 16:44:48 -06:00
return CONNECT_FAILURE;
2018-08-27 10:29:29 -06:00
}
2014-03-03 15:07:58 -07:00
}
/*
NOTICE: be careful to make sync connection from event handler: in case if all threads suddenly do sync connect, there will be no thread to dispatch events from io service.
*/
boost::system::error_code ec = boost::asio::error::would_block;
//have another free thread(s), work in wait mode, without event handling
struct local_async_context
{
boost::system::error_code ec;
boost::mutex connect_mut;
boost::condition_variable cond;
};
boost::shared_ptr<local_async_context> local_shared_context(new local_async_context());
local_shared_context->ec = boost::asio::error::would_block;
boost::unique_lock<boost::mutex> lock(local_shared_context->connect_mut);
auto connect_callback = [](boost::system::error_code ec_, boost::shared_ptr<local_async_context> shared_context)
{
2016-11-28 02:52:49 -07:00
shared_context->connect_mut.lock(); shared_context->ec = ec_; shared_context->cond.notify_one(); shared_context->connect_mut.unlock();
2014-03-03 15:07:58 -07:00
};
2020-05-31 19:18:11 -06:00
sock_.async_connect(remote_endpoint, std::bind<void>(connect_callback, std::placeholders::_1, local_shared_context));
2014-03-03 15:07:58 -07:00
while(local_shared_context->ec == boost::asio::error::would_block)
{
bool r = local_shared_context->cond.timed_wait(lock, boost::get_system_time() + boost::posix_time::milliseconds(conn_timeout));
2016-03-20 06:05:55 -06:00
if (m_stop_signal_sent)
{
if (sock_.is_open())
sock_.close();
epee: add SSL support
RPC connections now have optional tranparent SSL.
An optional private key and certificate file can be passed,
using the --{rpc,daemon}-ssl-private-key and
--{rpc,daemon}-ssl-certificate options. Those have as
argument a path to a PEM format private private key and
certificate, respectively.
If not given, a temporary self signed certificate will be used.
SSL can be enabled or disabled using --{rpc}-ssl, which
accepts autodetect (default), disabled or enabled.
Access can be restricted to particular certificates using the
--rpc-ssl-allowed-certificates, which takes a list of
paths to PEM encoded certificates. This can allow a wallet to
connect to only the daemon they think they're connected to,
by forcing SSL and listing the paths to the known good
certificates.
To generate long term certificates:
openssl genrsa -out /tmp/KEY 4096
openssl req -new -key /tmp/KEY -out /tmp/REQ
openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT
/tmp/KEY is the private key, and /tmp/CERT is the certificate,
both in PEM format. /tmp/REQ can be removed. Adjust the last
command to set expiration date, etc, as needed. It doesn't
make a whole lot of sense for monero anyway, since most servers
will run with one time temporary self signed certificates anyway.
SSL support is transparent, so all communication is done on the
existing ports, with SSL autodetection. This means you can start
using an SSL daemon now, but you should not enforce SSL yet or
nothing will talk to you.
2018-06-14 16:44:48 -06:00
return CONNECT_FAILURE;
2016-03-20 06:05:55 -06:00
}
2014-03-03 15:07:58 -07:00
if(local_shared_context->ec == boost::asio::error::would_block && !r)
{
//timeout
sock_.close();
2015-01-05 12:30:17 -07:00
_dbg3("Failed to connect to " << adr << ":" << port << ", because of timeout (" << conn_timeout << ")");
epee: add SSL support
RPC connections now have optional tranparent SSL.
An optional private key and certificate file can be passed,
using the --{rpc,daemon}-ssl-private-key and
--{rpc,daemon}-ssl-certificate options. Those have as
argument a path to a PEM format private private key and
certificate, respectively.
If not given, a temporary self signed certificate will be used.
SSL can be enabled or disabled using --{rpc}-ssl, which
accepts autodetect (default), disabled or enabled.
Access can be restricted to particular certificates using the
--rpc-ssl-allowed-certificates, which takes a list of
paths to PEM encoded certificates. This can allow a wallet to
connect to only the daemon they think they're connected to,
by forcing SSL and listing the paths to the known good
certificates.
To generate long term certificates:
openssl genrsa -out /tmp/KEY 4096
openssl req -new -key /tmp/KEY -out /tmp/REQ
openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT
/tmp/KEY is the private key, and /tmp/CERT is the certificate,
both in PEM format. /tmp/REQ can be removed. Adjust the last
command to set expiration date, etc, as needed. It doesn't
make a whole lot of sense for monero anyway, since most servers
will run with one time temporary self signed certificates anyway.
SSL support is transparent, so all communication is done on the
existing ports, with SSL autodetection. This means you can start
using an SSL daemon now, but you should not enforce SSL yet or
nothing will talk to you.
2018-06-14 16:44:48 -06:00
return CONNECT_FAILURE;
2014-03-03 15:07:58 -07:00
}
}
ec = local_shared_context->ec;
if (ec || !sock_.is_open())
{
2015-01-05 12:30:17 -07:00
_dbg3("Some problems at connect, message: " << ec.message());
2016-03-20 06:05:55 -06:00
if (sock_.is_open())
sock_.close();
epee: add SSL support
RPC connections now have optional tranparent SSL.
An optional private key and certificate file can be passed,
using the --{rpc,daemon}-ssl-private-key and
--{rpc,daemon}-ssl-certificate options. Those have as
argument a path to a PEM format private private key and
certificate, respectively.
If not given, a temporary self signed certificate will be used.
SSL can be enabled or disabled using --{rpc}-ssl, which
accepts autodetect (default), disabled or enabled.
Access can be restricted to particular certificates using the
--rpc-ssl-allowed-certificates, which takes a list of
paths to PEM encoded certificates. This can allow a wallet to
connect to only the daemon they think they're connected to,
by forcing SSL and listing the paths to the known good
certificates.
To generate long term certificates:
openssl genrsa -out /tmp/KEY 4096
openssl req -new -key /tmp/KEY -out /tmp/REQ
openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT
/tmp/KEY is the private key, and /tmp/CERT is the certificate,
both in PEM format. /tmp/REQ can be removed. Adjust the last
command to set expiration date, etc, as needed. It doesn't
make a whole lot of sense for monero anyway, since most servers
will run with one time temporary self signed certificates anyway.
SSL support is transparent, so all communication is done on the
existing ports, with SSL autodetection. This means you can start
using an SSL daemon now, but you should not enforce SSL yet or
nothing will talk to you.
2018-06-14 16:44:48 -06:00
return CONNECT_FAILURE;
2014-03-03 15:07:58 -07:00
}
2015-01-05 12:30:17 -07:00
_dbg3("Connected success to " << adr << ':' << port);
2014-03-03 15:07:58 -07:00
2019-03-14 22:03:32 -06:00
const ssl_support_t ssl_support = new_connection_l->get_ssl_support();
epee: add SSL support
RPC connections now have optional tranparent SSL.
An optional private key and certificate file can be passed,
using the --{rpc,daemon}-ssl-private-key and
--{rpc,daemon}-ssl-certificate options. Those have as
argument a path to a PEM format private private key and
certificate, respectively.
If not given, a temporary self signed certificate will be used.
SSL can be enabled or disabled using --{rpc}-ssl, which
accepts autodetect (default), disabled or enabled.
Access can be restricted to particular certificates using the
--rpc-ssl-allowed-certificates, which takes a list of
paths to PEM encoded certificates. This can allow a wallet to
connect to only the daemon they think they're connected to,
by forcing SSL and listing the paths to the known good
certificates.
To generate long term certificates:
openssl genrsa -out /tmp/KEY 4096
openssl req -new -key /tmp/KEY -out /tmp/REQ
openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT
/tmp/KEY is the private key, and /tmp/CERT is the certificate,
both in PEM format. /tmp/REQ can be removed. Adjust the last
command to set expiration date, etc, as needed. It doesn't
make a whole lot of sense for monero anyway, since most servers
will run with one time temporary self signed certificates anyway.
SSL support is transparent, so all communication is done on the
existing ports, with SSL autodetection. This means you can start
using an SSL daemon now, but you should not enforce SSL yet or
nothing will talk to you.
2018-06-14 16:44:48 -06:00
if (ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_enabled || ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect)
{
// Handshake
MDEBUG("Handshaking SSL...");
if (!new_connection_l->handshake(boost::asio::ssl::stream_base::client))
{
if (ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect)
{
boost::system::error_code ignored_ec;
sock_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
sock_.close();
return CONNECT_NO_SSL;
}
MERROR("SSL handshake failed");
if (sock_.is_open())
sock_.close();
return CONNECT_FAILURE;
}
}
return CONNECT_SUCCESS;
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::try_connect", CONNECT_FAILURE);
}
//---------------------------------------------------------------------------------
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)
{
TRY_ENTRY();
2019-03-14 22:03:32 -06:00
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, ssl_support) );
epee: add SSL support
RPC connections now have optional tranparent SSL.
An optional private key and certificate file can be passed,
using the --{rpc,daemon}-ssl-private-key and
--{rpc,daemon}-ssl-certificate options. Those have as
argument a path to a PEM format private private key and
certificate, respectively.
If not given, a temporary self signed certificate will be used.
SSL can be enabled or disabled using --{rpc}-ssl, which
accepts autodetect (default), disabled or enabled.
Access can be restricted to particular certificates using the
--rpc-ssl-allowed-certificates, which takes a list of
paths to PEM encoded certificates. This can allow a wallet to
connect to only the daemon they think they're connected to,
by forcing SSL and listing the paths to the known good
certificates.
To generate long term certificates:
openssl genrsa -out /tmp/KEY 4096
openssl req -new -key /tmp/KEY -out /tmp/REQ
openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT
/tmp/KEY is the private key, and /tmp/CERT is the certificate,
both in PEM format. /tmp/REQ can be removed. Adjust the last
command to set expiration date, etc, as needed. It doesn't
make a whole lot of sense for monero anyway, since most servers
will run with one time temporary self signed certificates anyway.
SSL support is transparent, so all communication is done on the
existing ports, with SSL autodetection. This means you can start
using an SSL daemon now, but you should not enforce SSL yet or
nothing will talk to you.
2018-06-14 16:44:48 -06:00
connections_mutex.lock();
connections_.insert(new_connection_l);
MDEBUG("connections_ size now " << connections_.size());
connections_mutex.unlock();
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ CRITICAL_REGION_LOCAL(connections_mutex); connections_.erase(new_connection_l); });
boost::asio::ip::tcp::socket& sock_ = new_connection_l->socket();
2019-04-10 16:34:30 -06:00
bool try_ipv6 = false;
epee: add SSL support
RPC connections now have optional tranparent SSL.
An optional private key and certificate file can be passed,
using the --{rpc,daemon}-ssl-private-key and
--{rpc,daemon}-ssl-certificate options. Those have as
argument a path to a PEM format private private key and
certificate, respectively.
If not given, a temporary self signed certificate will be used.
SSL can be enabled or disabled using --{rpc}-ssl, which
accepts autodetect (default), disabled or enabled.
Access can be restricted to particular certificates using the
--rpc-ssl-allowed-certificates, which takes a list of
paths to PEM encoded certificates. This can allow a wallet to
connect to only the daemon they think they're connected to,
by forcing SSL and listing the paths to the known good
certificates.
To generate long term certificates:
openssl genrsa -out /tmp/KEY 4096
openssl req -new -key /tmp/KEY -out /tmp/REQ
openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT
/tmp/KEY is the private key, and /tmp/CERT is the certificate,
both in PEM format. /tmp/REQ can be removed. Adjust the last
command to set expiration date, etc, as needed. It doesn't
make a whole lot of sense for monero anyway, since most servers
will run with one time temporary self signed certificates anyway.
SSL support is transparent, so all communication is done on the
existing ports, with SSL autodetection. This means you can start
using an SSL daemon now, but you should not enforce SSL yet or
nothing will talk to you.
2018-06-14 16:44:48 -06:00
boost::asio::ip::tcp::resolver resolver(io_service_);
boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), adr, port, boost::asio::ip::tcp::resolver::query::canonical_name);
2019-04-10 16:34:30 -06:00
boost::system::error_code resolve_error;
boost::asio::ip::tcp::resolver::iterator iterator;
try
{
//resolving ipv4 address as ipv6 throws, catch here and move on
iterator = resolver.resolve(query, resolve_error);
}
catch (const boost::system::system_error& e)
{
if (!m_use_ipv6 || (resolve_error != boost::asio::error::host_not_found &&
resolve_error != boost::asio::error::host_not_found_try_again))
{
throw;
}
try_ipv6 = true;
}
catch (...)
{
throw;
}
std::string bind_ip_to_use;
epee: add SSL support
RPC connections now have optional tranparent SSL.
An optional private key and certificate file can be passed,
using the --{rpc,daemon}-ssl-private-key and
--{rpc,daemon}-ssl-certificate options. Those have as
argument a path to a PEM format private private key and
certificate, respectively.
If not given, a temporary self signed certificate will be used.
SSL can be enabled or disabled using --{rpc}-ssl, which
accepts autodetect (default), disabled or enabled.
Access can be restricted to particular certificates using the
--rpc-ssl-allowed-certificates, which takes a list of
paths to PEM encoded certificates. This can allow a wallet to
connect to only the daemon they think they're connected to,
by forcing SSL and listing the paths to the known good
certificates.
To generate long term certificates:
openssl genrsa -out /tmp/KEY 4096
openssl req -new -key /tmp/KEY -out /tmp/REQ
openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT
/tmp/KEY is the private key, and /tmp/CERT is the certificate,
both in PEM format. /tmp/REQ can be removed. Adjust the last
command to set expiration date, etc, as needed. It doesn't
make a whole lot of sense for monero anyway, since most servers
will run with one time temporary self signed certificates anyway.
SSL support is transparent, so all communication is done on the
existing ports, with SSL autodetection. This means you can start
using an SSL daemon now, but you should not enforce SSL yet or
nothing will talk to you.
2018-06-14 16:44:48 -06:00
boost::asio::ip::tcp::resolver::iterator end;
if(iterator == end)
{
2019-04-10 16:34:30 -06:00
if (!m_use_ipv6)
{
_erro("Failed to resolve " << adr);
return false;
}
else
{
try_ipv6 = true;
MINFO("Resolving address as IPv4 failed, trying IPv6");
}
}
else
{
bind_ip_to_use = bind_ip;
epee: add SSL support
RPC connections now have optional tranparent SSL.
An optional private key and certificate file can be passed,
using the --{rpc,daemon}-ssl-private-key and
--{rpc,daemon}-ssl-certificate options. Those have as
argument a path to a PEM format private private key and
certificate, respectively.
If not given, a temporary self signed certificate will be used.
SSL can be enabled or disabled using --{rpc}-ssl, which
accepts autodetect (default), disabled or enabled.
Access can be restricted to particular certificates using the
--rpc-ssl-allowed-certificates, which takes a list of
paths to PEM encoded certificates. This can allow a wallet to
connect to only the daemon they think they're connected to,
by forcing SSL and listing the paths to the known good
certificates.
To generate long term certificates:
openssl genrsa -out /tmp/KEY 4096
openssl req -new -key /tmp/KEY -out /tmp/REQ
openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT
/tmp/KEY is the private key, and /tmp/CERT is the certificate,
both in PEM format. /tmp/REQ can be removed. Adjust the last
command to set expiration date, etc, as needed. It doesn't
make a whole lot of sense for monero anyway, since most servers
will run with one time temporary self signed certificates anyway.
SSL support is transparent, so all communication is done on the
existing ports, with SSL autodetection. This means you can start
using an SSL daemon now, but you should not enforce SSL yet or
nothing will talk to you.
2018-06-14 16:44:48 -06:00
}
2019-04-10 16:34:30 -06:00
if (try_ipv6)
{
boost::asio::ip::tcp::resolver::query query6(boost::asio::ip::tcp::v6(), adr, port, boost::asio::ip::tcp::resolver::query::canonical_name);
iterator = resolver.resolve(query6, resolve_error);
if(iterator == end)
{
_erro("Failed to resolve " << adr);
return false;
}
else
{
if (bind_ip == "0.0.0.0")
{
bind_ip_to_use = "::";
}
else
{
bind_ip_to_use = "";
}
}
}
2019-10-14 08:12:18 -06:00
MDEBUG("Trying to connect to " << adr << ":" << port << ", bind_ip = " << bind_ip_to_use);
epee: add SSL support
RPC connections now have optional tranparent SSL.
An optional private key and certificate file can be passed,
using the --{rpc,daemon}-ssl-private-key and
--{rpc,daemon}-ssl-certificate options. Those have as
argument a path to a PEM format private private key and
certificate, respectively.
If not given, a temporary self signed certificate will be used.
SSL can be enabled or disabled using --{rpc}-ssl, which
accepts autodetect (default), disabled or enabled.
Access can be restricted to particular certificates using the
--rpc-ssl-allowed-certificates, which takes a list of
paths to PEM encoded certificates. This can allow a wallet to
connect to only the daemon they think they're connected to,
by forcing SSL and listing the paths to the known good
certificates.
To generate long term certificates:
openssl genrsa -out /tmp/KEY 4096
openssl req -new -key /tmp/KEY -out /tmp/REQ
openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT
/tmp/KEY is the private key, and /tmp/CERT is the certificate,
both in PEM format. /tmp/REQ can be removed. Adjust the last
command to set expiration date, etc, as needed. It doesn't
make a whole lot of sense for monero anyway, since most servers
will run with one time temporary self signed certificates anyway.
SSL support is transparent, so all communication is done on the
existing ports, with SSL autodetection. This means you can start
using an SSL daemon now, but you should not enforce SSL yet or
nothing will talk to you.
2018-06-14 16:44:48 -06:00
//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);
2019-04-10 16:34:30 -06:00
auto try_connect_result = try_connect(new_connection_l, adr, port, sock_, remote_endpoint, bind_ip_to_use, conn_timeout, ssl_support);
epee: add SSL support
RPC connections now have optional tranparent SSL.
An optional private key and certificate file can be passed,
using the --{rpc,daemon}-ssl-private-key and
--{rpc,daemon}-ssl-certificate options. Those have as
argument a path to a PEM format private private key and
certificate, respectively.
If not given, a temporary self signed certificate will be used.
SSL can be enabled or disabled using --{rpc}-ssl, which
accepts autodetect (default), disabled or enabled.
Access can be restricted to particular certificates using the
--rpc-ssl-allowed-certificates, which takes a list of
paths to PEM encoded certificates. This can allow a wallet to
connect to only the daemon they think they're connected to,
by forcing SSL and listing the paths to the known good
certificates.
To generate long term certificates:
openssl genrsa -out /tmp/KEY 4096
openssl req -new -key /tmp/KEY -out /tmp/REQ
openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT
/tmp/KEY is the private key, and /tmp/CERT is the certificate,
both in PEM format. /tmp/REQ can be removed. Adjust the last
command to set expiration date, etc, as needed. It doesn't
make a whole lot of sense for monero anyway, since most servers
will run with one time temporary self signed certificates anyway.
SSL support is transparent, so all communication is done on the
existing ports, with SSL autodetection. This means you can start
using an SSL daemon now, but you should not enforce SSL yet or
nothing will talk to you.
2018-06-14 16:44:48 -06:00
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)
{
// 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();
2019-04-10 16:34:30 -06:00
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);
epee: add SSL support
RPC connections now have optional tranparent SSL.
An optional private key and certificate file can be passed,
using the --{rpc,daemon}-ssl-private-key and
--{rpc,daemon}-ssl-certificate options. Those have as
argument a path to a PEM format private private key and
certificate, respectively.
If not given, a temporary self signed certificate will be used.
SSL can be enabled or disabled using --{rpc}-ssl, which
accepts autodetect (default), disabled or enabled.
Access can be restricted to particular certificates using the
--rpc-ssl-allowed-certificates, which takes a list of
paths to PEM encoded certificates. This can allow a wallet to
connect to only the daemon they think they're connected to,
by forcing SSL and listing the paths to the known good
certificates.
To generate long term certificates:
openssl genrsa -out /tmp/KEY 4096
openssl req -new -key /tmp/KEY -out /tmp/REQ
openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT
/tmp/KEY is the private key, and /tmp/CERT is the certificate,
both in PEM format. /tmp/REQ can be removed. Adjust the last
command to set expiration date, etc, as needed. It doesn't
make a whole lot of sense for monero anyway, since most servers
will run with one time temporary self signed certificates anyway.
SSL support is transparent, so all communication is done on the
existing ports, with SSL autodetection. This means you can start
using an SSL daemon now, but you should not enforce SSL yet or
nothing will talk to you.
2018-06-14 16:44:48 -06:00
if (try_connect_result != CONNECT_SUCCESS)
return false;
}
2017-08-16 08:41:34 -06:00
// 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();
2014-03-03 15:07:58 -07:00
bool r = new_connection_l->start(false, 1 < m_threads_count);
if (r)
{
new_connection_l->get_context(conn_context);
2015-01-05 12:30:17 -07:00
//new_connection_l.reset(new connection<t_protocol_handler>(io_service_, m_config, m_sock_count, m_pfilter));
2014-03-03 15:07:58 -07:00
}
else
{
2018-12-16 10:57:44 -07:00
assert(m_state != nullptr); // always set in constructor
_erro("[sock " << new_connection_l->socket().native_handle() << "] Failed to start connection, connections_count = " << m_state->sock_count);
2014-03-03 15:07:58 -07:00
}
2015-04-08 11:54:07 -06:00
new_connection_l->save_dbg_log();
2014-03-03 15:07:58 -07:00
return r;
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::connect", false);
}
//---------------------------------------------------------------------------------
template<class t_protocol_handler> template<class t_callback>
epee: add SSL support
RPC connections now have optional tranparent SSL.
An optional private key and certificate file can be passed,
using the --{rpc,daemon}-ssl-private-key and
--{rpc,daemon}-ssl-certificate options. Those have as
argument a path to a PEM format private private key and
certificate, respectively.
If not given, a temporary self signed certificate will be used.
SSL can be enabled or disabled using --{rpc}-ssl, which
accepts autodetect (default), disabled or enabled.
Access can be restricted to particular certificates using the
--rpc-ssl-allowed-certificates, which takes a list of
paths to PEM encoded certificates. This can allow a wallet to
connect to only the daemon they think they're connected to,
by forcing SSL and listing the paths to the known good
certificates.
To generate long term certificates:
openssl genrsa -out /tmp/KEY 4096
openssl req -new -key /tmp/KEY -out /tmp/REQ
openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT
/tmp/KEY is the private key, and /tmp/CERT is the certificate,
both in PEM format. /tmp/REQ can be removed. Adjust the last
command to set expiration date, etc, as needed. It doesn't
make a whole lot of sense for monero anyway, since most servers
will run with one time temporary self signed certificates anyway.
SSL support is transparent, so all communication is done on the
existing ports, with SSL autodetection. This means you can start
using an SSL daemon now, but you should not enforce SSL yet or
nothing will talk to you.
2018-06-14 16:44:48 -06:00
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)
2014-03-03 15:07:58 -07:00
{
TRY_ENTRY();
2019-03-14 22:03:32 -06:00
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, ssl_support) );
2015-12-22 05:31:22 -07:00
connections_mutex.lock();
2017-08-16 08:41:34 -06:00
connections_.insert(new_connection_l);
Change logging to easylogging++
This replaces the epee and data_loggers logging systems with
a single one, and also adds filename:line and explicit severity
levels. Categories may be defined, and logging severity set
by category (or set of categories). epee style 0-4 log level
maps to a sensible severity configuration. Log files now also
rotate when reaching 100 MB.
To select which logs to output, use the MONERO_LOGS environment
variable, with a comma separated list of categories (globs are
supported), with their requested severity level after a colon.
If a log matches more than one such setting, the last one in
the configuration string applies. A few examples:
This one is (mostly) silent, only outputting fatal errors:
MONERO_LOGS=*:FATAL
This one is very verbose:
MONERO_LOGS=*:TRACE
This one is totally silent (logwise):
MONERO_LOGS=""
This one outputs all errors and warnings, except for the
"verify" category, which prints just fatal errors (the verify
category is used for logs about incoming transactions and
blocks, and it is expected that some/many will fail to verify,
hence we don't want the spam):
MONERO_LOGS=*:WARNING,verify:FATAL
Log levels are, in decreasing order of priority:
FATAL, ERROR, WARNING, INFO, DEBUG, TRACE
Subcategories may be added using prefixes and globs. This
example will output net.p2p logs at the TRACE level, but all
other net* logs only at INFO:
MONERO_LOGS=*:ERROR,net*:INFO,net.p2p:TRACE
Logs which are intended for the user (which Monero was using
a lot through epee, but really isn't a nice way to go things)
should use the "global" category. There are a few helper macros
for using this category, eg: MGINFO("this shows up by default")
or MGINFO_RED("this is red"), to try to keep a similar look
and feel for now.
Existing epee log macros still exist, and map to the new log
levels, but since they're used as a "user facing" UI element
as much as a logging system, they often don't map well to log
severities (ie, a log level 0 log may be an error, or may be
something we want the user to see, such as an important info).
In those cases, I tried to use the new macros. In other cases,
I left the existing macros in. When modifying logs, it is
probably best to switch to the new macros with explicit levels.
The --log-level options and set_log commands now also accept
category settings, in addition to the epee style log levels.
2017-01-01 09:34:23 -07:00
MDEBUG("connections_ size now " << connections_.size());
2015-12-22 05:31:22 -07:00
connections_mutex.unlock();
2017-08-16 08:41:34 -06:00
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ CRITICAL_REGION_LOCAL(connections_mutex); connections_.erase(new_connection_l); });
2014-03-03 15:07:58 -07:00
boost::asio::ip::tcp::socket& sock_ = new_connection_l->socket();
2019-04-10 16:34:30 -06:00
bool try_ipv6 = false;
2014-03-03 15:07:58 -07:00
boost::asio::ip::tcp::resolver resolver(io_service_);
2016-11-23 16:08:53 -07:00
boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), adr, port, boost::asio::ip::tcp::resolver::query::canonical_name);
2019-04-10 16:34:30 -06:00
boost::system::error_code resolve_error;
boost::asio::ip::tcp::resolver::iterator iterator;
try
{
//resolving ipv4 address as ipv6 throws, catch here and move on
iterator = resolver.resolve(query, resolve_error);
}
catch (const boost::system::system_error& e)
{
if (!m_use_ipv6 || (resolve_error != boost::asio::error::host_not_found &&
resolve_error != boost::asio::error::host_not_found_try_again))
{
throw;
}
try_ipv6 = true;
}
catch (...)
{
throw;
}
2014-03-03 15:07:58 -07:00
boost::asio::ip::tcp::resolver::iterator end;
if(iterator == end)
{
2019-04-10 16:34:30 -06:00
if (!try_ipv6)
{
_erro("Failed to resolve " << adr);
return false;
}
else
{
MINFO("Resolving address as IPv4 failed, trying IPv6");
}
2014-03-03 15:07:58 -07:00
}
2019-04-10 16:34:30 -06:00
if (try_ipv6)
{
boost::asio::ip::tcp::resolver::query query6(boost::asio::ip::tcp::v6(), adr, port, boost::asio::ip::tcp::resolver::query::canonical_name);
iterator = resolver.resolve(query6, resolve_error);
if(iterator == end)
{
_erro("Failed to resolve " << adr);
return false;
}
}
2014-03-03 15:07:58 -07:00
boost::asio::ip::tcp::endpoint remote_endpoint(*iterator);
sock_.open(remote_endpoint.protocol());
if(bind_ip != "0.0.0.0" && bind_ip != "0" && bind_ip != "" )
{
2018-09-09 04:46:58 -06:00
boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(bind_ip.c_str()), 0);
2018-08-27 10:29:29 -06:00
boost::system::error_code ec;
sock_.bind(local_endpoint, ec);
if (ec)
{
2018-09-09 04:46:58 -06:00
MERROR("Error binding to " << bind_ip << ": " << ec.message());
2018-08-27 10:29:29 -06:00
if (sock_.is_open())
sock_.close();
return false;
}
2014-03-03 15:07:58 -07:00
}
boost::shared_ptr<boost::asio::deadline_timer> sh_deadline(new boost::asio::deadline_timer(io_service_));
//start deadline
sh_deadline->expires_from_now(boost::posix_time::milliseconds(conn_timeout));
sh_deadline->async_wait([=](const boost::system::error_code& error)
{
if(error != boost::asio::error::operation_aborted)
{
2015-01-05 12:30:17 -07:00
_dbg3("Failed to connect to " << adr << ':' << port << ", because of timeout (" << conn_timeout << ")");
2014-03-03 15:07:58 -07:00
new_connection_l->socket().close();
}
});
//start async connect
sock_.async_connect(remote_endpoint, [=](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);
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
{
2015-01-05 12:30:17 -07:00
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] Connected success to " << adr << ':' << port <<
2014-03-03 15:07:58 -07:00
" from " << lep.address().to_string() << ':' << lep.port());
2017-08-16 08:41:34 -06:00
// 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();
2014-03-03 15:07:58 -07:00
bool r = new_connection_l->start(false, 1 < m_threads_count);
if (r)
{
new_connection_l->get_context(conn_context);
cb(conn_context, ec_);
}
else
{
2015-01-05 12:30:17 -07:00
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] Failed to start connection to " << adr << ':' << port);
2014-03-03 15:07:58 -07:00
cb(conn_context, boost::asio::error::fault);
}
}
}else
{
2015-01-05 12:30:17 -07:00
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] Failed to connect to " << adr << ':' << port <<
2014-03-03 15:07:58 -07:00
" from " << lep.address().to_string() << ':' << lep.port() << ": " << ec_.message() << ':' << ec_.value());
cb(conn_context, ec_);
}
});
return true;
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::connect_async", false);
}
2015-02-12 12:59:39 -07:00
2015-01-05 12:30:17 -07:00
} // namespace
} // namespace