wownero/contrib/epee/include/net/abstract_tcp_server2.inl

1918 lines
64 KiB
Plaintext
Raw Normal View History

/**
@file
@author from CrypoNote (see copyright below; Andrey N. Sabelnikov)
@monero rfree
@brief the connection templated-class for one peer connection
*/
// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
2014-03-03 15:07:58 -07:00
// All rights reserved.
//
// 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
//
// 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-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>
#include <boost/date_time/posix_time/posix_time.hpp> // TODO
epee: Drop deprecated Boost.Thread header In file included from src/cryptonote_basic/hardfork.cpp:33: In file included from src/blockchain_db/blockchain_db.h:42: In file included from src/cryptonote_basic/hardfork.h:31: contrib/epee/include/syncobj.h:37:10: fatal error: 'boost/thread/v2/thread.hpp' file not found #include <boost/thread/v2/thread.hpp> ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ In file included from src/rpc/daemon_handler.cpp:29: In file included from src/rpc/daemon_handler.h:36: In file included from src/p2p/net_node.h:41: In file included from contrib/epee/include/net/levin_server_cp2.h:32: In file included from contrib/epee/include/net/abstract_tcp_server2.h:324: contrib/epee/include/net/abstract_tcp_server2.inl:44:10: fatal error: 'boost/thread/v2/thread.hpp' file not found #include <boost/thread/v2/thread.hpp> // TODO ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ contrib/epee/include/math_helper.h: In member function 'bool epee::math_helper::average<val, default_base>::set_base()': contrib/epee/include/syncobj.h:227:56: error: 'sleep_for' is not a member of 'boost::this_thread' #define CRITICAL_REGION_LOCAL(x) {boost::this_thread::sleep_for(boost::chrono::milliseconds(epee::debug::g_test_dbg_lock_sleep()));} epee::critical_region_t<decltype(x)> critical_region_var(x) ^ contrib/epee/include/syncobj.h:227:56: note: in definition of macro 'CRITICAL_REGION_LOCAL' #define CRITICAL_REGION_LOCAL(x) {boost::this_thread::sleep_for(boost::chrono::milliseconds(epee::debug::g_test_dbg_lock_sleep()));} epee::critical_region_t<decltype(x)> critical_region_var(x) ^~~~~~~~~ contrib/epee/include/syncobj.h:227:56: note: suggested alternative: 'sleep' #define CRITICAL_REGION_LOCAL(x) {boost::this_thread::sleep_for(boost::chrono::milliseconds(epee::debug::g_test_dbg_lock_sleep()));} epee::critical_region_t<decltype(x)> critical_region_var(x) ^ contrib/epee/include/syncobj.h:227:56: note: in definition of macro 'CRITICAL_REGION_LOCAL' #define CRITICAL_REGION_LOCAL(x) {boost::this_thread::sleep_for(boost::chrono::milliseconds(epee::debug::g_test_dbg_lock_sleep()));} epee::critical_region_t<decltype(x)> critical_region_var(x) ^~~~~~~~~
2018-04-19 00:55:05 -06:00
#include <boost/thread/condition_variable.hpp> // TODO
#include <boost/make_shared.hpp>
#include <boost/thread.hpp>
#include "warnings.h"
#include "string_tools_lexical.h"
2014-03-03 15:07:58 -07:00
#include "misc_language.h"
#include <sstream>
#include <iomanip>
#include <algorithm>
#include <functional>
#include <random>
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"
#define AGGRESSIVE_TIMEOUT_THRESHOLD 120 // sockets
#define NEW_CONNECTION_TIMEOUT_LOCAL 1200000 // 2 minutes
#define NEW_CONNECTION_TIMEOUT_REMOTE 10000 // 10 seconds
#define DEFAULT_TIMEOUT_MS_LOCAL 1800000 // 30 minutes
#define DEFAULT_TIMEOUT_MS_REMOTE 300000 // 5 minutes
#define TIMEOUT_EXTRA_MS_PER_BYTE 0.2
2014-03-03 15:07:58 -07:00
namespace epee
{
namespace net_utils
{
template<typename T>
T& check_and_get(std::shared_ptr<T>& ptr)
{
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)
{
2021-06-28 13:13:02 -06:00
static lock_t hosts_mutex;
lock_guard_t guard(hosts_mutex);
static std::map<string_t, unsigned int> hosts;
unsigned int &val = hosts[host];
if (delta > 0)
MTRACE("New connection from host " << host << ": " << val);
else if (delta < 0)
MTRACE("Closed connection from host " << host << ": " << val);
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;
}
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 (
local ?
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
}
2021-06-28 13:13:02 -06:00
template<typename T>
void connection<T>::start_timer(duration_t duration, bool add)
{
if (state.timers.general.wait_expire) {
state.timers.general.cancel_expire = true;
state.timers.general.reset_expire = true;
ec_t ec;
timers.general.expires_from_now(
std::min(
duration + (add ? timers.general.expires_from_now() : duration_t{}),
get_default_timeout()
),
ec
);
}
2021-06-28 13:13:02 -06:00
else {
ec_t ec;
timers.general.expires_from_now(
std::min(
duration + (add ? timers.general.expires_from_now() : duration_t{}),
get_default_timeout()
),
ec
);
async_wait_timer();
}
}
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
{
2021-06-28 13:13:02 -06:00
if (state.timers.general.wait_expire)
return;
state.timers.general.wait_expire = true;
auto self = connection<T>::shared_from_this();
timers.general.async_wait([this, self](const ec_t & ec){
lock_guard_t guard(state.lock);
state.timers.general.wait_expire = false;
if (state.timers.general.cancel_expire) {
state.timers.general.cancel_expire = false;
if (state.timers.general.reset_expire) {
state.timers.general.reset_expire = false;
async_wait_timer();
}
else if (state.status == status_t::INTERRUPTED)
on_interrupted();
else if (state.status == status_t::TERMINATING)
on_terminating();
}
else if (state.status == status_t::RUNNING)
interrupt();
else if (state.status == status_t::INTERRUPTED)
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
{
2021-06-28 13:13:02 -06:00
if (not state.timers.general.wait_expire)
return;
state.timers.general.cancel_expire = true;
state.timers.general.reset_expire = false;
ec_t ec;
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
{
2021-06-28 13:13:02 -06:00
if (state.socket.wait_handshake)
return;
static_assert(
epee::net_utils::get_ssl_magic_size() <= sizeof(state.data.read.buffer),
""
);
auto self = connection<T>::shared_from_this();
if (not state.ssl.forced and not state.ssl.detected) {
state.socket.wait_read = true;
boost::asio::async_read(
connection_basic::socket_.next_layer(),
boost::asio::buffer(
state.data.read.buffer.data(),
state.data.read.buffer.size()
),
boost::asio::transfer_exactly(epee::net_utils::get_ssl_magic_size()),
strand.wrap(
[this, self](const ec_t &ec, size_t bytes_transferred){
lock_guard_t guard(state.lock);
state.socket.wait_read = false;
if (state.socket.cancel_read) {
state.socket.cancel_read = false;
if (state.status == status_t::RUNNING)
interrupt();
else if (state.status == status_t::INTERRUPTED)
on_interrupted();
else if (state.status == status_t::TERMINATING)
on_terminating();
}
else if (ec.value()) {
terminate();
}
else if (
not epee::net_utils::is_ssl(
static_cast<const unsigned char *>(
state.data.read.buffer.data()
),
bytes_transferred
)
) {
state.ssl.enabled = false;
state.socket.handle_read = true;
connection_basic::strand_.post(
[this, self, bytes_transferred]{
bool success = handler.handle_recv(
reinterpret_cast<char *>(state.data.read.buffer.data()),
bytes_transferred
);
lock_guard_t guard(state.lock);
state.socket.handle_read = false;
if (state.status == status_t::INTERRUPTED)
on_interrupted();
else if (state.status == status_t::TERMINATING)
on_terminating();
else if (not success)
interrupt();
else {
start_read();
}
}
);
}
else {
state.ssl.detected = true;
start_handshake();
}
}
)
);
return;
}
state.socket.wait_handshake = true;
auto on_handshake = [this, self](const ec_t &ec, size_t bytes_transferred){
lock_guard_t guard(state.lock);
state.socket.wait_handshake = false;
if (state.socket.cancel_handshake) {
state.socket.cancel_handshake = false;
if (state.status == status_t::RUNNING)
interrupt();
else if (state.status == status_t::INTERRUPTED)
on_interrupted();
else if (state.status == status_t::TERMINATING)
on_terminating();
}
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);
state.socket.connected = false;
interrupt();
}
else {
state.ssl.handshaked = true;
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);
strand.post(
[this, self, on_handshake]{
connection_basic::socket_.async_handshake(
handshake,
boost::asio::buffer(
state.data.read.buffer.data(),
state.ssl.forced ? 0 :
epee::net_utils::get_ssl_magic_size()
),
strand.wrap(on_handshake)
);
}
);
2014-03-03 15:07:58 -07:00
}
2021-06-28 13:13:02 -06:00
template<typename T>
void connection<T>::start_read()
{
if (state.timers.throttle.in.wait_expire || state.socket.wait_read ||
state.socket.handle_read
)
return;
auto self = connection<T>::shared_from_this();
if (connection_type != e_connection_type_RPC) {
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;
timers.throttle.in.expires_from_now(duration, ec);
state.timers.throttle.in.wait_expire = true;
timers.throttle.in.async_wait([this, self](const ec_t &ec){
lock_guard_t guard(state.lock);
state.timers.throttle.in.wait_expire = false;
if (state.timers.throttle.in.cancel_expire) {
state.timers.throttle.in.cancel_expire = false;
if (state.status == status_t::RUNNING)
interrupt();
else if (state.status == status_t::INTERRUPTED)
on_interrupted();
else if (state.status == status_t::TERMINATING)
on_terminating();
}
else if (ec.value())
interrupt();
else
start_read();
});
return;
}
}
2021-06-28 13:13:02 -06:00
state.socket.wait_read = true;
auto on_read = [this, self](const ec_t &ec, size_t bytes_transferred){
lock_guard_t guard(state.lock);
state.socket.wait_read = false;
if (state.socket.cancel_read) {
state.socket.cancel_read = false;
if (state.status == status_t::RUNNING)
interrupt();
else if (state.status == status_t::INTERRUPTED)
on_interrupted();
else if (state.status == status_t::TERMINATING)
on_terminating();
}
else if (ec.value())
terminate();
else {
{
state.stat.in.throttle.handle_trafic_exact(bytes_transferred);
const auto speed = state.stat.in.throttle.get_current_speed();
context.m_current_speed_down = speed;
context.m_max_speed_down = std::max(
context.m_max_speed_down,
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);
context.m_last_recv = time(NULL);
context.m_recv_cnt += bytes_transferred;
start_timer(get_timeout_from_bytes_read(bytes_transferred), true);
}
state.socket.handle_read = true;
connection_basic::strand_.post(
[this, self, bytes_transferred]{
bool success = handler.handle_recv(
reinterpret_cast<char *>(state.data.read.buffer.data()),
bytes_transferred
);
lock_guard_t guard(state.lock);
state.socket.handle_read = false;
if (state.status == status_t::INTERRUPTED)
on_interrupted();
else if (state.status == status_t::TERMINATING)
on_terminating();
else if (not success)
interrupt();
else {
start_read();
}
}
);
}
};
if (not state.ssl.enabled)
connection_basic::socket_.next_layer().async_read_some(
boost::asio::buffer(
state.data.read.buffer.data(),
state.data.read.buffer.size()
),
strand.wrap(on_read)
);
else
2021-06-28 13:13:02 -06:00
strand.post(
[this, self, on_read]{
connection_basic::socket_.async_read_some(
boost::asio::buffer(
state.data.read.buffer.data(),
state.data.read.buffer.size()
),
strand.wrap(on_read)
);
}
);
}
2021-06-28 13:13:02 -06:00
template<typename T>
void connection<T>::start_write()
2014-03-03 15:07:58 -07:00
{
2021-06-28 13:13:02 -06:00
if (state.timers.throttle.out.wait_expire || state.socket.wait_write ||
state.data.write.queue.empty() ||
(state.ssl.enabled && not state.ssl.handshaked)
)
return;
auto self = connection<T>::shared_from_this();
if (connection_type != e_connection_type_RPC) {
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(
state.data.write.queue.back().size()
),
1.0
)
)
);
};
const auto duration = calc_duration();
if (duration > duration_t{}) {
ec_t ec;
timers.throttle.out.expires_from_now(duration, ec);
state.timers.throttle.out.wait_expire = true;
timers.throttle.out.async_wait([this, self](const ec_t &ec){
lock_guard_t guard(state.lock);
state.timers.throttle.out.wait_expire = false;
if (state.timers.throttle.out.cancel_expire) {
state.timers.throttle.out.cancel_expire = false;
if (state.status == status_t::RUNNING)
interrupt();
else if (state.status == status_t::INTERRUPTED)
on_interrupted();
else if (state.status == status_t::TERMINATING)
on_terminating();
}
else if (ec.value())
interrupt();
else
start_write();
});
}
}
2021-06-28 13:13:02 -06:00
state.socket.wait_write = true;
auto on_write = [this, self](const ec_t &ec, size_t bytes_transferred){
lock_guard_t guard(state.lock);
state.socket.wait_write = false;
if (state.socket.cancel_write) {
state.socket.cancel_write = false;
state.data.write.queue.clear();
if (state.status == status_t::RUNNING)
interrupt();
else if (state.status == status_t::INTERRUPTED)
on_interrupted();
else if (state.status == status_t::TERMINATING)
on_terminating();
2014-03-03 15:07:58 -07:00
}
2021-06-28 13:13:02 -06:00
else if (ec.value()) {
state.data.write.queue.clear();
interrupt();
2014-03-03 15:07:58 -07:00
}
2021-06-28 13:13:02 -06:00
else {
{
state.stat.out.throttle.handle_trafic_exact(bytes_transferred);
const auto speed = state.stat.out.throttle.get_current_speed();
context.m_current_speed_up = speed;
context.m_max_speed_down = std::max(
context.m_max_speed_down,
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);
context.m_last_send = time(NULL);
context.m_send_cnt += bytes_transferred;
start_timer(get_default_timeout(), true);
}
assert(bytes_transferred == state.data.write.queue.back().size());
state.data.write.queue.pop_back();
state.condition.notify_all();
start_write();
}
2021-06-28 13:13:02 -06:00
};
if (not state.ssl.enabled)
boost::asio::async_write(
connection_basic::socket_.next_layer(),
boost::asio::buffer(
state.data.write.queue.back().data(),
state.data.write.queue.back().size()
),
strand.wrap(on_write)
);
else
strand.post(
[this, self, on_write]{
boost::asio::async_write(
connection_basic::socket_,
boost::asio::buffer(
state.data.write.queue.back().data(),
state.data.write.queue.back().size()
),
strand.wrap(on_write)
);
}
);
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
{
2021-06-28 13:13:02 -06:00
if (state.socket.wait_shutdown)
return;
auto self = connection<T>::shared_from_this();
state.socket.wait_shutdown = true;
auto on_shutdown = [this, self](const ec_t &ec){
lock_guard_t guard(state.lock);
state.socket.wait_shutdown = false;
if (state.socket.cancel_shutdown) {
state.socket.cancel_shutdown = false;
if (state.status == status_t::RUNNING)
interrupt();
else if (state.status == status_t::INTERRUPTED)
terminate();
else if (state.status == status_t::TERMINATING)
on_terminating();
}
else if (ec.value())
terminate();
else {
cancel_timer();
on_interrupted();
}
};
strand.post(
[this, self, on_shutdown]{
connection_basic::socket_.async_shutdown(
strand.wrap(on_shutdown)
);
}
);
start_timer(get_default_timeout());
}
2021-06-28 13:13:02 -06:00
template<typename T>
void connection<T>::cancel_socket()
{
bool wait_socket = false;
if (state.socket.wait_handshake)
wait_socket = state.socket.cancel_handshake = true;
if (state.timers.throttle.in.wait_expire) {
state.timers.throttle.in.cancel_expire = true;
ec_t ec;
timers.throttle.in.cancel(ec);
}
if (state.socket.wait_read)
wait_socket = state.socket.cancel_read = true;
if (state.timers.throttle.out.wait_expire) {
state.timers.throttle.out.cancel_expire = true;
ec_t ec;
timers.throttle.out.cancel(ec);
}
if (state.socket.wait_write)
wait_socket = state.socket.cancel_write = true;
if (state.socket.wait_shutdown)
wait_socket = state.socket.cancel_shutdown = true;
if (wait_socket) {
ec_t ec;
connection_basic::socket_.next_layer().cancel(ec);
}
}
2021-06-28 13:13:02 -06:00
template<typename T>
void connection<T>::cancel_handler()
{
if (state.protocol.released || 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;
2021-06-28 13:13:02 -06:00
state.protocol.wait_release = true;
state.lock.unlock();
handler.release_protocol();
state.lock.lock();
state.protocol.wait_release = false;
state.protocol.released = true;
if (state.status == status_t::INTERRUPTED)
on_interrupted();
else if (state.status == status_t::TERMINATING)
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()
{
if (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;
2021-06-28 13:13:02 -06:00
state.status = status_t::INTERRUPTED;
cancel_timer();
cancel_socket();
on_interrupted();
state.condition.notify_all();
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()
{
assert(state.status == status_t::INTERRUPTED);
if (state.timers.general.wait_expire)
return;
if (state.socket.wait_handshake)
return;
if (state.timers.throttle.in.wait_expire)
return;
if (state.socket.wait_read)
return;
if (state.socket.handle_read)
return;
if (state.timers.throttle.out.wait_expire)
return;
if (state.socket.wait_write)
return;
if (state.socket.wait_shutdown)
return;
if (state.protocol.wait_init)
return;
if (state.protocol.wait_callback)
return;
if (state.protocol.wait_release)
return;
if (state.socket.connected) {
if (not state.ssl.enabled) {
ec_t ec;
connection_basic::socket_.next_layer().shutdown(
socket_t::shutdown_both,
ec
);
connection_basic::socket_.next_layer().close(ec);
state.socket.connected = false;
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
2021-06-28 13:13:02 -06:00
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
2021-06-28 13:13:02 -06:00
template<typename T>
void connection<T>::terminate()
{
if (state.status != status_t::RUNNING &&
state.status != status_t::INTERRUPTED
)
return;
state.status = status_t::TERMINATING;
cancel_timer();
cancel_socket();
on_terminating();
state.condition.notify_all();
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()
{
assert(state.status == status_t::TERMINATING);
if (state.timers.general.wait_expire)
return;
if (state.socket.wait_handshake)
return;
if (state.timers.throttle.in.wait_expire)
return;
if (state.socket.wait_read)
return;
if (state.socket.handle_read)
return;
if (state.timers.throttle.out.wait_expire)
return;
if (state.socket.wait_write)
return;
if (state.socket.wait_shutdown)
return;
if (state.protocol.wait_init)
return;
if (state.protocol.wait_callback)
return;
if (state.protocol.wait_release)
return;
if (state.socket.connected) {
ec_t ec;
connection_basic::socket_.next_layer().shutdown(
socket_t::shutdown_both,
ec
);
connection_basic::socket_.next_layer().close(ec);
state.socket.connected = false;
2014-03-03 15:07:58 -07:00
}
2021-06-28 13:13:02 -06:00
state.status = status_t::WASTED;
}
template<typename T>
bool connection<T>::send(byte_slice_t message)
2014-03-03 15:07:58 -07:00
{
2021-06-28 13:13:02 -06:00
lock_guard_t guard(state.lock);
if (state.status != status_t::RUNNING || state.socket.wait_handshake)
2014-03-03 15:07:58 -07:00
return false;
2021-06-28 13:13:02 -06:00
auto wait_consume = [this] {
auto random_delay = []{
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
]{};
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)
);
};
if (state.data.write.queue.size() <= ABSTRACT_SERVER_SEND_QUE_MAX_COUNT)
return true;
state.data.write.wait_consume = true;
bool success = state.condition.wait_for(
state.lock,
random_delay(),
[this]{
return (
state.status != status_t::RUNNING ||
state.data.write.queue.size() <=
ABSTRACT_SERVER_SEND_QUE_MAX_COUNT
);
}
);
state.data.write.wait_consume = false;
if (not success) {
terminate();
return false;
}
else
return state.status == status_t::RUNNING;
};
auto wait_sender = [this] {
state.condition.wait(
state.lock,
[this] {
return (
state.status != status_t::RUNNING ||
not state.data.write.wait_consume
);
}
2021-06-28 13:13:02 -06:00
);
return state.status == status_t::RUNNING;
};
if (not wait_sender())
return false;
constexpr size_t CHUNK_SIZE = 32 * 1024;
if (connection_type == e_connection_type_RPC ||
message.size() <= 2 * CHUNK_SIZE
) {
if (not wait_consume())
return false;
state.data.write.queue.emplace_front(std::move(message));
start_write();
2014-03-03 15:07:58 -07:00
}
2021-06-28 13:13:02 -06:00
else {
while (!message.empty()) {
if (not wait_consume())
return false;
state.data.write.queue.emplace_front(
message.take_slice(CHUNK_SIZE)
);
start_write();
}
2014-03-03 15:07:58 -07:00
}
2021-06-28 13:13:02 -06:00
state.condition.notify_all();
return true;
}
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
)
{
unique_lock_t guard(state.lock);
if (state.status != status_t::TERMINATED)
return false;
if (not real_remote) {
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()
}
}
2021-06-28 13:13:02 -06:00
);
}
2021-06-28 13:13:02 -06:00
auto *filter = static_cast<shared_state&>(
connection_basic::get_state()
).pfilter;
if (filter and not filter->is_remote_host_allowed(*real_remote))
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;
context.set_details(
boost::uuids::random_generator()(),
*real_remote,
is_income,
connection_basic::m_ssl_support == ssl_support_t::e_ssl_support_enabled
);
host = real_remote->host_str();
try { host_count(1); } catch(...) { /* ignore */ }
local = real_remote->is_loopback() || real_remote->is_local();
state.ssl.enabled = (
connection_basic::m_ssl_support != ssl_support_t::e_ssl_support_disabled
);
state.ssl.forced = (
connection_basic::m_ssl_support == ssl_support_t::e_ssl_support_enabled
);
state.socket.connected = true;
state.status = status_t::RUNNING;
start_timer(
std::chrono::milliseconds(
local ? NEW_CONNECTION_TIMEOUT_LOCAL : NEW_CONNECTION_TIMEOUT_REMOTE
)
);
state.protocol.wait_init = true;
guard.unlock();
handler.after_init_connection();
guard.lock();
state.protocol.wait_init = false;
state.protocol.initialized = true;
if (state.status == status_t::INTERRUPTED)
on_interrupted();
else if (state.status == status_t::TERMINATING)
on_terminating();
else if (not is_income || not state.ssl.enabled)
start_read();
else
2021-06-28 13:13:02 -06:00
start_handshake();
return true;
}
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
)
{
}
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),
handler(this, *shared_state, context),
connection_type(connection_type),
io_context{GET_IO_SERVICE(connection_basic::socket_)},
strand{io_context},
timers{io_context}
{
}
2021-06-28 13:13:02 -06:00
template<typename T>
connection<T>::~connection() noexcept(false)
{
2021-06-28 13:13:02 -06:00
lock_guard_t guard(state.lock);
assert(state.status == status_t::TERMINATED ||
state.status == status_t::WASTED ||
io_context.stopped()
);
if (state.status != status_t::WASTED)
return;
2021-06-28 13:13:02 -06:00
try { host_count(-1); } catch (...) { /* ignore */ }
}
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()
{
lock_guard_t guard(state.lock);
string_t address;
string_t port;
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(
" connection type " << std::to_string(connection_type) <<
" " << connection_basic::socket().local_endpoint().address().to_string() <<
":" << connection_basic::socket().local_endpoint().port() <<
" <--> " << context.m_remote_address.str() <<
" (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
{
2021-06-28 13:13:02 -06:00
return connection_type != e_connection_type_RPC;
}
2021-06-28 13:13:02 -06:00
template<typename T>
bool connection<T>::cancel()
{
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;
}
2021-06-28 13:13:02 -06:00
template<typename T>
bool connection<T>::close()
{
lock_guard_t guard(state.lock);
if (state.status != status_t::RUNNING)
return false;
terminate();
return true;
}
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) {
if (not io_context.poll_one())
misc_utils::sleep_no_w(1);
2014-03-03 15:07:58 -07:00
}
2021-06-28 13:13:02 -06:00
else {
if (!io_context.run_one())
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()
{
lock_guard_t guard(state.lock);
if (state.status != status_t::RUNNING)
return false;
auto self = connection<T>::shared_from_this();
++state.protocol.wait_callback;
connection_basic::strand_.post([this, self]{
handler.handle_qued_callback();
lock_guard_t guard(state.lock);
--state.protocol.wait_callback;
if (state.status == status_t::INTERRUPTED)
on_interrupted();
else if (state.status == status_t::TERMINATING)
on_terminating();
});
return true;
2014-03-03 15:07:58 -07:00
}
2021-06-28 13:13:02 -06:00
template<typename T>
typename connection<T>::io_context_t &connection<T>::get_io_service()
{
2021-06-28 13:13:02 -06:00
return io_context;
}
2021-06-28 13:13:02 -06:00
template<typename T>
bool connection<T>::add_ref()
{
try {
auto self = connection<T>::shared_from_this();
lock_guard_t guard(state.lock);
this->self = std::move(self);
++state.protocol.reference_counter;
return true;
}
catch (boost::bad_weak_ptr &exception) {
return false;
}
}
2021-06-28 13:13:02 -06:00
template<typename T>
bool connection<T>::release()
{
connection_ptr self;
lock_guard_t guard(state.lock);
if (not --state.protocol.reference_counter)
self = std::move(this->self);
return true;
}
2021-06-28 13:13:02 -06:00
template<typename T>
void connection<T>::setRpcStation()
{
lock_guard_t guard(state.lock);
connection_type = e_connection_type_RPC;
}
2014-03-03 15:07:58 -07:00
template<class t_protocol_handler>
boosted_tcp_server<t_protocol_handler>::boosted_tcp_server( t_connection_type connection_type ) :
m_state(std::make_shared<typename connection<t_protocol_handler>::shared_state>()),
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_),
acceptor_ipv6(io_service_),
default_remote(),
m_stop_signal_sent(false), m_port(0),
m_threads_count(0),
m_thread_index(0),
m_connection_type( connection_type ),
new_connection_(),
new_connection_ipv6()
2014-03-03 15:07:58 -07: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(boost::asio::io_service& extarnal_io_service, t_connection_type connection_type) :
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_),
acceptor_ipv6(io_service_),
default_remote(),
m_stop_signal_sent(false), m_port(0),
m_threads_count(0),
m_thread_index(0),
m_connection_type(connection_type),
new_connection_(),
new_connection_ipv6()
2014-03-03 15:07:58 -07: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>
void boosted_tcp_server<t_protocol_handler>::create_server_type_map()
{
server_type_map["NET"] = e_connection_type_NET;
server_type_map["RPC"] = e_connection_type_RPC;
server_type_map["P2P"] = e_connection_type_P2P;
}
//---------------------------------------------------------------------------------
template<class t_protocol_handler>
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;
m_port_ipv6 = port_ipv6;
2014-03-03 15:07:58 -07:00
m_address = address;
m_address_ipv6 = address_ipv6;
m_use_ipv6 = use_ipv6;
m_require_ipv4 = require_ipv4;
if (ssl_options)
m_state->configure_ssl(std::move(ssl_options));
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());
#if !defined(_WIN32)
acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
#endif
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());
#if !defined(_WIN32)
acceptor_ipv6.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
#endif
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;
}
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>
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;
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;
}
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();
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);
// _fact("Thread name: " << m_thread_name_prefix);
2014-03-03 15:07:58 -07:00
while(!m_stop_signal_sent)
{
try
{
io_service_.run();
return true;
2014-03-03 15:07:58 -07:00
}
catch(const std::exception& ex)
{
_erro("Exception at server worker thread, what=" << ex.what());
2014-03-03 15:07:58 -07:00
}
catch(...)
{
_erro("Exception at server worker thread, unknown execption");
2014-03-03 15:07:58 -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;
auto it = server_type_map.find(m_thread_name_prefix);
if (it==server_type_map.end()) throw std::runtime_error("Unknown prefix/server type:" + std::string(prefix_name));
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)
{
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)));
_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
{
_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();
_fact("JOINING all threads - DONE");
2014-03-03 15:07:58 -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 ?..
_dbg1("Net service stopped without stop request, restarting...");
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
{
_dbg1("Reiniting service failed, exit.");
2014-03-03 15:07:58 -07:00
return false;
}else
{
_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))
{
_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;
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();
connections_mutex.lock();
for (auto &c: connections_)
{
c->cancel();
}
connections_.clear();
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>
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");
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;
}
try
{
2014-03-03 15:07:58 -07:00
if (!e)
{
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;
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);
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;
}
conn->save_dbg_log();
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
{
MERROR("Exception in boosted_tcp_server<t_protocol_handler>::handle_accept: " << e.what());
2014-03-03 15:07:58 -07:00
}
// error path, if e or exception
assert(m_state != nullptr); // always set in constructor
_erro("Some problems at accept: " << e.message() << ", connections_count = " << m_state->sock_count);
misc_utils::sleep_no_w(100);
(*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)
{
if(std::addressof(get_io_service()) == std::addressof(GET_IO_SERVICE(sock)))
{
connection_ptr conn(new connection<t_protocol_handler>(std::move(sock), m_state, m_connection_type, ssl_support));
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 != "" )
{
boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(bind_ip.c_str()), 0);
boost::system::error_code ec;
sock_.bind(local_endpoint, ec);
if (ec)
{
MERROR("Error binding to " << bind_ip << ": " << ec.message());
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
}
/*
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)
{
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
};
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));
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;
}
2014-03-03 15:07:58 -07:00
if(local_shared_context->ec == boost::asio::error::would_block && !r)
{
//timeout
sock_.close();
_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())
{
_dbg3("Some problems at connect, message: " << ec.message());
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
}
_dbg3("Connected success to " << adr << ':' << port);
2014-03-03 15:07:58 -07: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();
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();
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);
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)
{
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
}
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 = "";
}
}
}
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);
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();
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;
}
// 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);
//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
{
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
}
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();
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, ssl_support) );
connections_mutex.lock();
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());
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); });
2014-03-03 15:07:58 -07:00
boost::asio::ip::tcp::socket& sock_ = new_connection_l->socket();
bool try_ipv6 = false;
2014-03-03 15:07:58 -07: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);
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)
{
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
}
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 != "" )
{
boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(bind_ip.c_str()), 0);
boost::system::error_code ec;
sock_.bind(local_endpoint, ec);
if (ec)
{
MERROR("Error binding to " << bind_ip << ": " << ec.message());
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)
{
_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
{
_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());
// 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
{
_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
{
_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);
}
} // namespace
} // namespace