Adding initial support for broadcasting transactions over Tor

- Support for ".onion" in --add-exclusive-node and --add-peer
  - Add --anonymizing-proxy for outbound Tor connections
  - Add --anonymous-inbounds for inbound Tor connections
  - Support for sharing ".onion" addresses over Tor connections
  - Support for broadcasting transactions received over RPC exclusively
    over Tor (else broadcast over public IP when Tor not enabled).
This commit is contained in:
Lee Clagett 2018-12-16 17:57:44 +00:00
parent 1e5cd3b35a
commit 973403bc9f
39 changed files with 4298 additions and 831 deletions

View file

@ -40,6 +40,7 @@ target_link_libraries(p2p
PUBLIC
version
cryptonote_core
net
${UPNP_LIBRARIES}
${Boost_CHRONO_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}

View file

@ -28,9 +28,79 @@
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#include <boost/algorithm/string/find_iterator.hpp>
#include <boost/algorithm/string/finder.hpp>
#include <boost/chrono/duration.hpp>
#include <boost/endian/conversion.hpp>
#include <boost/optional/optional.hpp>
#include <boost/thread/future.hpp>
#include <boost/utility/string_ref.hpp>
#include <chrono>
#include <utility>
#include "common/command_line.h"
#include "cryptonote_core/cryptonote_core.h"
#include "cryptonote_protocol/cryptonote_protocol_defs.h"
#include "net_node.h"
#include "net/net_utils_base.h"
#include "net/socks.h"
#include "net/parse.h"
#include "net/tor_address.h"
#include "p2p/p2p_protocol_defs.h"
#include "string_tools.h"
namespace
{
constexpr const boost::chrono::milliseconds future_poll_interval{500};
constexpr const std::chrono::seconds tor_connect_timeout{P2P_DEFAULT_TOR_CONNECT_TIMEOUT};
std::int64_t get_max_connections(const boost::iterator_range<boost::string_ref::const_iterator> value) noexcept
{
// -1 is default, 0 is error
if (value.empty())
return -1;
std::uint32_t out = 0;
if (epee::string_tools::get_xtype_from_string(out, std::string{value.begin(), value.end()}))
return out;
return 0;
}
template<typename T>
epee::net_utils::network_address get_address(const boost::string_ref value)
{
expect<T> address = T::make(value);
if (!address)
{
MERROR(
"Failed to parse " << epee::net_utils::zone_to_string(T::get_zone()) << " address \"" << value << "\": " << address.error().message()
);
return {};
}
return {std::move(*address)};
}
bool start_socks(std::shared_ptr<net::socks::client> client, const boost::asio::ip::tcp::endpoint& proxy, const epee::net_utils::network_address& remote)
{
CHECK_AND_ASSERT_MES(client != nullptr, false, "Unexpected null client");
bool set = false;
switch (remote.get_type_id())
{
case net::tor_address::get_type_id():
set = client->set_connect_command(remote.as<net::tor_address>());
break;
default:
MERROR("Unsupported network address in socks_connect");
return false;
}
const bool sent =
set && net::socks::client::connect_and_send(std::move(client), proxy);
CHECK_AND_ASSERT_MES(sent, false, "Unexpected failure to init socks client");
return true;
}
}
namespace nodetool
{
@ -55,6 +125,8 @@ namespace nodetool
const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_add_exclusive_node = {"add-exclusive-node", "Specify list of peers to connect to only."
" If this option is given the options add-priority-node and seed-node are ignored"};
const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_seed_node = {"seed-node", "Connect to a node to retrieve peer addresses, and disconnect"};
const command_line::arg_descriptor<std::vector<std::string> > arg_proxy = {"proxy", "<network-type>,<socks-ip:port>[,max_connections] i.e. \"tor,127.0.0.1:9050,100\""};
const command_line::arg_descriptor<std::vector<std::string> > arg_anonymous_inbound = {"anonymous-inbound", "<hidden-service-address>,<[bind-ip:]port>[,max_connections] i.e. \"x.onion,127.0.0.1:18083,100\""};
const command_line::arg_descriptor<bool> arg_p2p_hide_my_port = {"hide-my-port", "Do not announce yourself as peerlist candidate", false, true};
const command_line::arg_descriptor<bool> arg_no_igd = {"no-igd", "Disable UPnP port mapping"};
@ -67,4 +139,196 @@ namespace nodetool
const command_line::arg_descriptor<int64_t> arg_limit_rate = {"limit-rate", "set limit-rate [kB/s]", -1};
const command_line::arg_descriptor<bool> arg_save_graph = {"save-graph", "Save data for dr monero", false};
boost::optional<std::vector<proxy>> get_proxies(boost::program_options::variables_map const& vm)
{
namespace ip = boost::asio::ip;
std::vector<proxy> proxies{};
const std::vector<std::string> args = command_line::get_arg(vm, arg_proxy);
proxies.reserve(args.size());
for (const boost::string_ref arg : args)
{
proxies.emplace_back();
auto next = boost::algorithm::make_split_iterator(arg, boost::algorithm::first_finder(","));
CHECK_AND_ASSERT_MES(!next.eof() && !next->empty(), boost::none, "No network type for --" << arg_proxy.name);
const boost::string_ref zone{next->begin(), next->size()};
++next;
CHECK_AND_ASSERT_MES(!next.eof() && !next->empty(), boost::none, "No ipv4:port given for --" << arg_proxy.name);
const boost::string_ref proxy{next->begin(), next->size()};
++next;
if (!next.eof())
{
proxies.back().max_connections = get_max_connections(*next);
if (proxies.back().max_connections == 0)
{
MERROR("Invalid max connections given to --" << arg_proxy.name);
return boost::none;
}
}
switch (epee::net_utils::zone_from_string(zone))
{
case epee::net_utils::zone::tor:
proxies.back().zone = epee::net_utils::zone::tor;
break;
default:
MERROR("Invalid network for --" << arg_proxy.name);
return boost::none;
}
std::uint32_t ip = 0;
std::uint16_t port = 0;
if (!epee::string_tools::parse_peer_from_string(ip, port, std::string{proxy}) || port == 0)
{
MERROR("Invalid ipv4:port given for --" << arg_proxy.name);
return boost::none;
}
proxies.back().address = ip::tcp::endpoint{ip::address_v4{boost::endian::native_to_big(ip)}, port};
}
return proxies;
}
boost::optional<std::vector<anonymous_inbound>> get_anonymous_inbounds(boost::program_options::variables_map const& vm)
{
std::vector<anonymous_inbound> inbounds{};
const std::vector<std::string> args = command_line::get_arg(vm, arg_anonymous_inbound);
inbounds.reserve(args.size());
for (const boost::string_ref arg : args)
{
inbounds.emplace_back();
auto next = boost::algorithm::make_split_iterator(arg, boost::algorithm::first_finder(","));
CHECK_AND_ASSERT_MES(!next.eof() && !next->empty(), boost::none, "No inbound address for --" << arg_anonymous_inbound.name);
const boost::string_ref address{next->begin(), next->size()};
++next;
CHECK_AND_ASSERT_MES(!next.eof() && !next->empty(), boost::none, "No local ipv4:port given for --" << arg_anonymous_inbound.name);
const boost::string_ref bind{next->begin(), next->size()};
const std::size_t colon = bind.find_first_of(':');
CHECK_AND_ASSERT_MES(colon < bind.size(), boost::none, "No local port given for --" << arg_anonymous_inbound.name);
++next;
if (!next.eof())
{
inbounds.back().max_connections = get_max_connections(*next);
if (inbounds.back().max_connections == 0)
{
MERROR("Invalid max connections given to --" << arg_proxy.name);
return boost::none;
}
}
expect<epee::net_utils::network_address> our_address = net::get_network_address(address, 0);
switch (our_address ? our_address->get_type_id() : epee::net_utils::address_type::invalid)
{
case net::tor_address::get_type_id():
inbounds.back().our_address = std::move(*our_address);
inbounds.back().default_remote = net::tor_address::unknown();
break;
default:
MERROR("Invalid inbound address (" << address << ") for --" << arg_anonymous_inbound.name << ": " << (our_address ? "invalid type" : our_address.error().message()));
return boost::none;
}
// get_address returns default constructed address on error
if (inbounds.back().our_address == epee::net_utils::network_address{})
return boost::none;
std::uint32_t ip = 0;
std::uint16_t port = 0;
if (!epee::string_tools::parse_peer_from_string(ip, port, std::string{bind}))
{
MERROR("Invalid ipv4:port given for --" << arg_anonymous_inbound.name);
return boost::none;
}
inbounds.back().local_ip = std::string{bind.substr(0, colon)};
inbounds.back().local_port = std::string{bind.substr(colon + 1)};
}
return inbounds;
}
bool is_filtered_command(const epee::net_utils::network_address& address, int command)
{
switch (command)
{
case nodetool::COMMAND_HANDSHAKE_T<cryptonote::CORE_SYNC_DATA>::ID:
case nodetool::COMMAND_TIMED_SYNC_T<cryptonote::CORE_SYNC_DATA>::ID:
case cryptonote::NOTIFY_NEW_TRANSACTIONS::ID:
return false;
default:
break;
}
if (address.get_zone() == epee::net_utils::zone::public_)
return false;
MWARNING("Filtered command (#" << command << ") to/from " << address.str());
return true;
}
boost::optional<boost::asio::ip::tcp::socket>
socks_connect_internal(const std::atomic<bool>& stop_signal, boost::asio::io_service& service, const boost::asio::ip::tcp::endpoint& proxy, const epee::net_utils::network_address& remote)
{
using socket_type = net::socks::client::stream_type::socket;
using client_result = std::pair<boost::system::error_code, socket_type>;
struct notify
{
boost::promise<client_result> socks_promise;
void operator()(boost::system::error_code error, socket_type&& sock)
{
socks_promise.set_value(std::make_pair(error, std::move(sock)));
}
};
boost::unique_future<client_result> socks_result{};
{
boost::promise<client_result> socks_promise{};
socks_result = socks_promise.get_future();
auto client = net::socks::make_connect_client(
boost::asio::ip::tcp::socket{service}, net::socks::version::v4a, notify{std::move(socks_promise)}
);
if (!start_socks(std::move(client), proxy, remote))
return boost::none;
}
const auto start = std::chrono::steady_clock::now();
while (socks_result.wait_for(future_poll_interval) == boost::future_status::timeout)
{
if (tor_connect_timeout < std::chrono::steady_clock::now() - start)
{
MERROR("Timeout on socks connect (" << proxy << " to " << remote.str() << ")");
return boost::none;
}
if (stop_signal)
return boost::none;
}
try
{
auto result = socks_result.get();
if (!result.first)
return {std::move(result.second)};
MERROR("Failed to make socks connection to " << remote.str() << " (via " << proxy << "): " << result.first.message());
}
catch (boost::broken_promise const&)
{}
return boost::none;
}
}

View file

@ -30,12 +30,17 @@
#pragma once
#include <array>
#include <atomic>
#include <boost/asio/io_service.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/thread.hpp>
#include <boost/optional/optional_fwd.hpp>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/variables_map.hpp>
#include <boost/serialization/version.hpp>
#include <boost/uuid/uuid.hpp>
#include <boost/serialization/map.hpp>
#include <functional>
#include <utility>
#include <vector>
#include "cryptonote_config.h"
#include "warnings.h"
@ -47,6 +52,8 @@
#include "net_peerlist.h"
#include "math_helper.h"
#include "net_node_common.h"
#include "net/enums.h"
#include "net/fwd.h"
#include "common/command_line.h"
PUSH_WARNINGS
@ -54,6 +61,47 @@ DISABLE_VS_WARNINGS(4355)
namespace nodetool
{
struct proxy
{
proxy()
: max_connections(-1),
address(),
zone(epee::net_utils::zone::invalid)
{}
std::int64_t max_connections;
boost::asio::ip::tcp::endpoint address;
epee::net_utils::zone zone;
};
struct anonymous_inbound
{
anonymous_inbound()
: max_connections(-1),
local_ip(),
local_port(),
our_address(),
default_remote()
{}
std::int64_t max_connections;
std::string local_ip;
std::string local_port;
epee::net_utils::network_address our_address;
epee::net_utils::network_address default_remote;
};
boost::optional<std::vector<proxy>> get_proxies(const boost::program_options::variables_map& vm);
boost::optional<std::vector<anonymous_inbound>> get_anonymous_inbounds(const boost::program_options::variables_map& vm);
//! \return True if `commnd` is filtered (ignored/dropped) for `address`
bool is_filtered_command(epee::net_utils::network_address const& address, int command);
// hides boost::future and chrono stuff from mondo template file
boost::optional<boost::asio::ip::tcp::socket>
socks_connect_internal(const std::atomic<bool>& stop_signal, boost::asio::io_service& service, const boost::asio::ip::tcp::endpoint& proxy, const epee::net_utils::network_address& remote);
template<class base_type>
struct p2p_connection_context_t: base_type //t_payload_net_handler::connection_context //public net_utils::connection_context_base
{
@ -78,53 +126,124 @@ namespace nodetool
typedef COMMAND_HANDSHAKE_T<typename t_payload_net_handler::payload_type> COMMAND_HANDSHAKE;
typedef COMMAND_TIMED_SYNC_T<typename t_payload_net_handler::payload_type> COMMAND_TIMED_SYNC;
typedef epee::net_utils::boosted_tcp_server<epee::levin::async_protocol_handler<p2p_connection_context>> net_server;
struct network_zone;
using connect_func = boost::optional<p2p_connection_context>(network_zone&, epee::net_utils::network_address const&);
struct config
{
config()
: m_net_config(),
m_peer_id(crypto::rand<uint64_t>()),
m_support_flags(0)
{}
network_config m_net_config;
uint64_t m_peer_id;
uint32_t m_support_flags;
};
struct network_zone
{
network_zone()
: m_connect(nullptr),
m_net_server(epee::net_utils::e_connection_type_P2P),
m_bind_ip(),
m_port(),
m_our_address(),
m_peerlist(),
m_config{},
m_proxy_address(),
m_current_number_of_out_peers(0),
m_current_number_of_in_peers(0),
m_can_pingback(false)
{
set_config_defaults();
}
network_zone(boost::asio::io_service& public_service)
: m_connect(nullptr),
m_net_server(public_service, epee::net_utils::e_connection_type_P2P),
m_bind_ip(),
m_port(),
m_our_address(),
m_peerlist(),
m_config{},
m_proxy_address(),
m_current_number_of_out_peers(0),
m_current_number_of_in_peers(0),
m_can_pingback(false)
{
set_config_defaults();
}
connect_func* m_connect;
net_server m_net_server;
std::string m_bind_ip;
std::string m_port;
epee::net_utils::network_address m_our_address; // in anonymity networks
peerlist_manager m_peerlist;
config m_config;
boost::asio::ip::tcp::endpoint m_proxy_address;
std::atomic<unsigned int> m_current_number_of_out_peers;
std::atomic<unsigned int> m_current_number_of_in_peers;
bool m_can_pingback;
private:
void set_config_defaults() noexcept
{
// at this moment we have a hardcoded config
m_config.m_net_config.handshake_interval = P2P_DEFAULT_HANDSHAKE_INTERVAL;
m_config.m_net_config.packet_max_size = P2P_DEFAULT_PACKET_MAX_SIZE;
m_config.m_net_config.config_id = 0;
m_config.m_net_config.connection_timeout = P2P_DEFAULT_CONNECTION_TIMEOUT;
m_config.m_net_config.ping_connection_timeout = P2P_DEFAULT_PING_CONNECTION_TIMEOUT;
m_config.m_net_config.send_peerlist_sz = P2P_DEFAULT_PEERS_IN_HANDSHAKE;
m_config.m_support_flags = 0; // only set in public zone
}
};
public:
typedef t_payload_net_handler payload_net_handler;
node_server(t_payload_net_handler& payload_handler)
:m_payload_handler(payload_handler),
m_current_number_of_out_peers(0),
m_current_number_of_in_peers(0),
m_allow_local_ip(false),
m_hide_my_port(false),
m_no_igd(false),
m_offline(false),
m_save_graph(false),
is_closing(false),
m_net_server( epee::net_utils::e_connection_type_P2P ) // this is a P2P connection of the main p2p node server, because this is class node_server<>
{}
virtual ~node_server()
: m_payload_handler(payload_handler),
m_external_port(0),
m_allow_local_ip(false),
m_hide_my_port(false),
m_no_igd(false),
m_offline(false),
m_save_graph(false),
is_closing(false),
m_network_id()
{}
virtual ~node_server();
static void init_options(boost::program_options::options_description& desc);
bool run();
network_zone& add_zone(epee::net_utils::zone zone);
bool init(const boost::program_options::variables_map& vm);
bool deinit();
bool send_stop_signal();
uint32_t get_this_peer_port(){return m_listening_port;}
t_payload_net_handler& get_payload_object();
template <class Archive, class t_version_type>
void serialize(Archive &a, const t_version_type ver)
{
a & m_peerlist;
if (ver == 0)
{
// from v1, we do not store the peer id anymore
peerid_type peer_id = AUTO_VAL_INIT (peer_id);
a & peer_id;
}
}
// debug functions
bool log_peerlist();
bool log_connections();
virtual uint64_t get_connections_count();
size_t get_outgoing_connections_count();
size_t get_incoming_connections_count();
peerlist_manager& get_peerlist_manager(){return m_peerlist;}
void delete_out_connections(size_t count);
void delete_in_connections(size_t count);
// These functions only return information for the "public" zone
virtual uint64_t get_public_connections_count();
size_t get_public_outgoing_connections_count();
size_t get_public_white_peers_count();
size_t get_public_gray_peers_count();
void get_public_peerlist(std::vector<peerlist_entry>& gray, std::vector<peerlist_entry>& white);
size_t get_zone_count() const { return m_network_zones.size(); }
void change_max_out_public_peers(size_t count);
void change_max_in_public_peers(size_t count);
virtual bool block_host(const epee::net_utils::network_address &adress, time_t seconds = P2P_IP_BLOCKTIME);
virtual bool unblock_host(const epee::net_utils::network_address &address);
virtual std::map<std::string, time_t> get_blocked_hosts() { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); return m_blocked_hosts; }
@ -150,6 +269,9 @@ namespace nodetool
CHAIN_LEVIN_NOTIFY_MAP2(p2p_connection_context); //move levin_commands_handler interface notify(...) callbacks into nothing
BEGIN_INVOKE_MAP2(node_server)
if (is_filtered_command(context.m_remote_address, command))
return LEVIN_ERROR_CONNECTION_HANDLER_NOT_DEFINED;
HANDLE_INVOKE_T2(COMMAND_HANDSHAKE, &node_server::handle_handshake)
HANDLE_INVOKE_T2(COMMAND_TIMED_SYNC, &node_server::handle_timed_sync)
HANDLE_INVOKE_T2(COMMAND_PING, &node_server::handle_ping)
@ -178,7 +300,7 @@ namespace nodetool
bool make_default_peer_id();
bool make_default_config();
bool store_config();
bool check_trust(const proof_of_trust& tr);
bool check_trust(const proof_of_trust& tr, epee::net_utils::zone zone_type);
//----------------- levin_commands_handler -------------------------------------------------------------
@ -186,8 +308,7 @@ namespace nodetool
virtual void on_connection_close(p2p_connection_context& context);
virtual void callback(p2p_connection_context& context);
//----------------- i_p2p_endpoint -------------------------------------------------------------
virtual bool relay_notify_to_list(int command, const epee::span<const uint8_t> data_buff, const std::list<boost::uuids::uuid> &connections);
virtual bool relay_notify_to_all(int command, const epee::span<const uint8_t> data_buff, const epee::net_utils::connection_context_base& context);
virtual bool relay_notify_to_list(int command, const epee::span<const uint8_t> data_buff, std::vector<std::pair<epee::net_utils::zone, boost::uuids::uuid>> connections);
virtual bool invoke_command_to_peer(int command, const epee::span<const uint8_t> req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context);
virtual bool invoke_notify_to_peer(int command, const epee::span<const uint8_t> req_buff, const epee::net_utils::connection_context_base& context);
virtual bool drop_connection(const epee::net_utils::connection_context_base& context);
@ -204,7 +325,7 @@ namespace nodetool
);
bool idle_worker();
bool handle_remote_peerlist(const std::vector<peerlist_entry>& peerlist, time_t local_time, const epee::net_utils::connection_context_base& context);
bool get_local_node_data(basic_node_data& node_data);
bool get_local_node_data(basic_node_data& node_data, const network_zone& zone);
//bool get_local_handshake_data(handshake_data& hshd);
bool merge_peerlist_with_local(const std::vector<peerlist_entry>& bs);
@ -216,7 +337,7 @@ namespace nodetool
bool do_peer_timed_sync(const epee::net_utils::connection_context_base& context, peerid_type peer_id);
bool make_new_connection_from_anchor_peerlist(const std::vector<anchor_peerlist_entry>& anchor_peerlist);
bool make_new_connection_from_peerlist(bool use_white_list);
bool make_new_connection_from_peerlist(network_zone& zone, bool use_white_list);
bool try_to_connect_and_handshake_with_new_peer(const epee::net_utils::network_address& na, bool just_take_peerlist = false, uint64_t last_seen_stamp = 0, PeerType peer_type = white, uint64_t first_seen_stamp = 0);
size_t get_random_index_with_fixed_probability(size_t max_index);
bool is_peer_used(const peerlist_entry& peer);
@ -227,7 +348,7 @@ namespace nodetool
template<class t_callback>
bool try_ping(basic_node_data& node_data, p2p_connection_context& context, const t_callback &cb);
bool try_get_support_flags(const p2p_connection_context& context, std::function<void(p2p_connection_context&, const uint32_t&)> f);
bool make_expected_connections_count(PeerType peer_type, size_t expected_connections);
bool make_expected_connections_count(network_zone& zone, PeerType peer_type, size_t expected_connections);
void cache_connect_fail_info(const epee::net_utils::network_address& addr);
bool is_addr_recently_failed(const epee::net_utils::network_address& addr);
bool is_priority_node(const epee::net_utils::network_address& na);
@ -240,14 +361,8 @@ namespace nodetool
template <class Container>
bool parse_peers_and_add_to_container(const boost::program_options::variables_map& vm, const command_line::arg_descriptor<std::vector<std::string> > & arg, Container& container);
bool set_max_out_peers(const boost::program_options::variables_map& vm, int64_t max);
bool get_max_out_peers() const { return m_config.m_net_config.max_out_connection_count; }
bool get_current_out_peers() const { return m_current_number_of_out_peers; }
bool set_max_in_peers(const boost::program_options::variables_map& vm, int64_t max);
bool get_max_in_peers() const { return m_config.m_net_config.max_in_connection_count; }
bool get_current_in_peers() const { return m_current_number_of_in_peers; }
bool set_max_out_peers(network_zone& zone, int64_t max);
bool set_max_in_peers(network_zone& zone, int64_t max);
bool set_tos_flag(const boost::program_options::variables_map& vm, int limit);
bool set_rate_up_limit(const boost::program_options::variables_map& vm, int64_t limit);
@ -255,6 +370,11 @@ namespace nodetool
bool set_rate_limit(const boost::program_options::variables_map& vm, int64_t limit);
bool has_too_many_connections(const epee::net_utils::network_address &address);
uint64_t get_connections_count();
size_t get_incoming_connections_count();
size_t get_incoming_connections_count(network_zone&);
size_t get_outgoing_connections_count();
size_t get_outgoing_connections_count(network_zone&);
bool check_connection_and_handshake_with_peer(const epee::net_utils::network_address& na, uint64_t last_seen_stamp);
bool gray_peerlist_housekeeping();
@ -272,25 +392,7 @@ namespace nodetool
std::string print_connections_container();
typedef epee::net_utils::boosted_tcp_server<epee::levin::async_protocol_handler<p2p_connection_context> > net_server;
struct config
{
network_config m_net_config;
uint64_t m_peer_id;
uint32_t m_support_flags;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(m_net_config)
KV_SERIALIZE(m_peer_id)
KV_SERIALIZE(m_support_flags)
END_KV_SERIALIZE_MAP()
};
public:
config m_config; // TODO was private, add getters?
std::atomic<unsigned int> m_current_number_of_out_peers;
std::atomic<unsigned int> m_current_number_of_in_peers;
void set_save_graph(bool save_graph)
{
@ -304,7 +406,6 @@ namespace nodetool
bool m_first_connection_maker_call;
uint32_t m_listening_port;
uint32_t m_external_port;
uint32_t m_ip_address;
bool m_allow_local_ip;
bool m_hide_my_port;
bool m_no_igd;
@ -316,7 +417,7 @@ namespace nodetool
//connections_indexed_container m_connections;
t_payload_net_handler& m_payload_handler;
peerlist_manager m_peerlist;
peerlist_storage m_peerlist_storage;
epee::math_helper::once_a_time_seconds<P2P_DEFAULT_HANDSHAKE_INTERVAL> m_peer_handshake_idle_maker_interval;
epee::math_helper::once_a_time_seconds<1> m_connections_maker_interval;
@ -324,8 +425,6 @@ namespace nodetool
epee::math_helper::once_a_time_seconds<60> m_gray_peerlist_housekeeping_interval;
epee::math_helper::once_a_time_seconds<3600, false> m_incoming_connections_interval;
std::string m_bind_ip;
std::string m_port;
#ifdef ALLOW_DEBUG_COMMANDS
uint64_t m_last_stat_request_time;
#endif
@ -333,11 +432,22 @@ namespace nodetool
std::vector<epee::net_utils::network_address> m_exclusive_peers;
std::vector<epee::net_utils::network_address> m_seed_nodes;
bool m_fallback_seed_nodes_added;
std::list<nodetool::peerlist_entry> m_command_line_peers;
std::vector<nodetool::peerlist_entry> m_command_line_peers;
uint64_t m_peer_livetime;
//keep connections to initiate some interactions
net_server m_net_server;
boost::uuids::uuid m_network_id;
static boost::optional<p2p_connection_context> public_connect(network_zone&, epee::net_utils::network_address const&);
static boost::optional<p2p_connection_context> socks_connect(network_zone&, epee::net_utils::network_address const&);
/* A `std::map` provides constant iterators and key/value pointers even with
inserts/erases to _other_ elements. This makes the configuration step easier
since references can safely be stored on the stack. Do not insert/erase
after configuration and before destruction, lock safety would need to be
added. `std::map::operator[]` WILL insert! */
std::map<epee::net_utils::zone, network_zone> m_network_zones;
std::map<epee::net_utils::network_address, time_t> m_conn_fails_cache;
epee::critical_section m_conn_fails_cache_lock;
@ -351,6 +461,7 @@ namespace nodetool
boost::mutex m_used_stripe_peers_mutex;
std::array<std::list<epee::net_utils::network_address>, 1 << CRYPTONOTE_PRUNING_LOG_STRIPES> m_used_stripe_peers;
boost::uuids::uuid m_network_id;
cryptonote::network_type m_nettype;
};
@ -364,6 +475,8 @@ namespace nodetool
extern const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_add_priority_node;
extern const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_add_exclusive_node;
extern const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_seed_node;
extern const command_line::arg_descriptor<std::vector<std::string> > arg_proxy;
extern const command_line::arg_descriptor<std::vector<std::string> > arg_anonymous_inbound;
extern const command_line::arg_descriptor<bool> arg_p2p_hide_my_port;
extern const command_line::arg_descriptor<bool> arg_no_igd;
@ -380,3 +493,4 @@ namespace nodetool
}
POP_WARNINGS

File diff suppressed because it is too large Load diff

View file

@ -31,6 +31,8 @@
#pragma once
#include <boost/uuid/uuid.hpp>
#include <utility>
#include <vector>
#include "net/net_utils_base.h"
#include "p2p_protocol_defs.h"
@ -43,13 +45,13 @@ namespace nodetool
template<class t_connection_context>
struct i_p2p_endpoint
{
virtual bool relay_notify_to_list(int command, const epee::span<const uint8_t> data_buff, const std::list<boost::uuids::uuid>& connections)=0;
virtual bool relay_notify_to_all(int command, const epee::span<const uint8_t> data_buff, const epee::net_utils::connection_context_base& context)=0;
virtual bool relay_notify_to_list(int command, const epee::span<const uint8_t> data_buff, std::vector<std::pair<epee::net_utils::zone, boost::uuids::uuid>> connections)=0;
virtual bool invoke_command_to_peer(int command, const epee::span<const uint8_t> req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context)=0;
virtual bool invoke_notify_to_peer(int command, const epee::span<const uint8_t> req_buff, const epee::net_utils::connection_context_base& context)=0;
virtual bool drop_connection(const epee::net_utils::connection_context_base& context)=0;
virtual void request_callback(const epee::net_utils::connection_context_base& context)=0;
virtual uint64_t get_connections_count()=0;
virtual uint64_t get_public_connections_count()=0;
virtual size_t get_zone_count() const=0;
virtual void for_each_connection(std::function<bool(t_connection_context&, peerid_type, uint32_t)> f)=0;
virtual bool for_connection(const boost::uuids::uuid&, std::function<bool(t_connection_context&, peerid_type, uint32_t)> f)=0;
virtual bool block_host(const epee::net_utils::network_address &address, time_t seconds = 0)=0;
@ -64,11 +66,7 @@ namespace nodetool
template<class t_connection_context>
struct p2p_endpoint_stub: public i_p2p_endpoint<t_connection_context>
{
virtual bool relay_notify_to_list(int command, const epee::span<const uint8_t> data_buff, const std::list<boost::uuids::uuid>& connections)
{
return false;
}
virtual bool relay_notify_to_all(int command, const epee::span<const uint8_t> data_buff, const epee::net_utils::connection_context_base& context)
virtual bool relay_notify_to_list(int command, const epee::span<const uint8_t> data_buff, std::vector<std::pair<epee::net_utils::zone, boost::uuids::uuid>> connections)
{
return false;
}
@ -97,7 +95,12 @@ namespace nodetool
return false;
}
virtual uint64_t get_connections_count()
virtual size_t get_zone_count() const
{
return 0;
}
virtual uint64_t get_public_connections_count()
{
return false;
}

295
src/p2p/net_peerlist.cpp Normal file
View file

@ -0,0 +1,295 @@
// Copyright (c) 2018, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. 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.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// 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 HOLDER OR CONTRIBUTORS 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.
#include "net_peerlist.h"
#include <algorithm>
#include <functional>
#include <fstream>
#include <iterator>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/portable_binary_oarchive.hpp>
#include <boost/archive/portable_binary_iarchive.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/range/join.hpp>
#include <boost/serialization/version.hpp>
#include "net_peerlist_boost_serialization.h"
namespace nodetool
{
namespace
{
constexpr unsigned CURRENT_PEERLIST_STORAGE_ARCHIVE_VER = 6;
struct by_zone
{
using zone = epee::net_utils::zone;
template<typename T>
bool operator()(const T& left, const zone right) const
{
return left.adr.get_zone() < right;
}
template<typename T>
bool operator()(const zone left, const T& right) const
{
return left < right.adr.get_zone();
}
template<typename T, typename U>
bool operator()(const T& left, const U& right) const
{
return left.adr.get_zone() < right.adr.get_zone();
}
};
template<typename Elem, typename Archive>
std::vector<Elem> load_peers(Archive& a, unsigned ver)
{
// at v6, we drop existing peerlists, because annoying change
if (ver < 6)
return {};
uint64_t size = 0;
a & size;
Elem ple{};
std::vector<Elem> elems{};
elems.reserve(size);
while (size--)
{
a & ple;
elems.push_back(std::move(ple));
}
return elems;
}
template<typename Archive, typename Range>
void save_peers(Archive& a, const Range& elems)
{
const uint64_t size = elems.size();
a & size;
for (const auto& elem : elems)
a & elem;
}
template<typename T>
std::vector<T> do_take_zone(std::vector<T>& src, epee::net_utils::zone zone)
{
const auto start = std::lower_bound(src.begin(), src.end(), zone, by_zone{});
const auto end = std::upper_bound(start, src.end(), zone, by_zone{});
std::vector<T> out{};
out.assign(std::make_move_iterator(start), std::make_move_iterator(end));
src.erase(start, end);
return out;
}
template<typename Container, typename T>
void add_peers(Container& dest, std::vector<T>&& src)
{
dest.insert(std::make_move_iterator(src.begin()), std::make_move_iterator(src.end()));
}
template<typename Container, typename Range>
void copy_peers(Container& dest, const Range& src)
{
std::copy(src.begin(), src.end(), std::back_inserter(dest));
}
} // anonymous
struct peerlist_join
{
const peerlist_types& ours;
const peerlist_types& other;
};
template<typename Archive>
void serialize(Archive& a, peerlist_types& elem, unsigned ver)
{
elem.white = load_peers<peerlist_entry>(a, ver);
elem.gray = load_peers<peerlist_entry>(a, ver);
elem.anchor = load_peers<anchor_peerlist_entry>(a, ver);
if (ver == 0)
{
// from v1, we do not store the peer id anymore
peerid_type peer_id{};
a & peer_id;
}
}
template<typename Archive>
void serialize(Archive& a, peerlist_join elem, unsigned ver)
{
save_peers(a, boost::range::join(elem.ours.white, elem.other.white));
save_peers(a, boost::range::join(elem.ours.gray, elem.other.gray));
save_peers(a, boost::range::join(elem.ours.anchor, elem.other.anchor));
}
boost::optional<peerlist_storage> peerlist_storage::open(std::istream& src, const bool new_format)
{
try
{
peerlist_storage out{};
if (new_format)
{
boost::archive::portable_binary_iarchive a{src};
a >> out.m_types;
}
else
{
boost::archive::binary_iarchive a{src};
a >> out.m_types;
}
if (src.good())
{
std::sort(out.m_types.white.begin(), out.m_types.white.end(), by_zone{});
std::sort(out.m_types.gray.begin(), out.m_types.gray.end(), by_zone{});
std::sort(out.m_types.anchor.begin(), out.m_types.anchor.end(), by_zone{});
return {std::move(out)};
}
}
catch (const std::exception& e)
{}
return boost::none;
}
boost::optional<peerlist_storage> peerlist_storage::open(const std::string& path)
{
std::ifstream src_file{};
src_file.open( path , std::ios_base::binary | std::ios_base::in);
if(src_file.fail())
return boost::none;
boost::optional<peerlist_storage> out = open(src_file, true);
if (!out)
{
// if failed, try reading in unportable mode
boost::filesystem::copy_file(path, path + ".unportable", boost::filesystem::copy_option::overwrite_if_exists);
src_file.close();
src_file.open( path , std::ios_base::binary | std::ios_base::in);
if(src_file.fail())
return boost::none;
out = open(src_file, false);
if (!out)
{
// This is different from the `return boost::none` cases above. Those
// cases could fail due to bad file permissions, so a shutdown is
// likely more appropriate.
MWARNING("Failed to load p2p config file, falling back to default config");
out.emplace();
}
}
return out;
}
peerlist_storage::~peerlist_storage() noexcept
{}
bool peerlist_storage::store(std::ostream& dest, const peerlist_types& other) const
{
try
{
boost::archive::portable_binary_oarchive a{dest};
const peerlist_join pj{std::cref(m_types), std::cref(other)};
a << pj;
return dest.good();
}
catch (const boost::archive::archive_exception& e)
{}
return false;
}
bool peerlist_storage::store(const std::string& path, const peerlist_types& other) const
{
std::ofstream dest_file{};
dest_file.open( path , std::ios_base::binary | std::ios_base::out| std::ios::trunc);
if(dest_file.fail())
return false;
return store(dest_file, other);
}
peerlist_types peerlist_storage::take_zone(epee::net_utils::zone zone)
{
peerlist_types out{};
out.white = do_take_zone(m_types.white, zone);
out.gray = do_take_zone(m_types.gray, zone);
out.anchor = do_take_zone(m_types.anchor, zone);
return out;
}
bool peerlist_manager::init(peerlist_types&& peers, bool allow_local_ip)
{
CRITICAL_REGION_LOCAL(m_peerlist_lock);
if (!m_peers_white.empty() || !m_peers_gray.empty() || !m_peers_anchor.empty())
return false;
add_peers(m_peers_white.get<by_addr>(), std::move(peers.white));
add_peers(m_peers_gray.get<by_addr>(), std::move(peers.gray));
add_peers(m_peers_anchor.get<by_addr>(), std::move(peers.anchor));
m_allow_local_ip = allow_local_ip;
return true;
}
void peerlist_manager::get_peerlist(std::vector<peerlist_entry>& pl_gray, std::vector<peerlist_entry>& pl_white)
{
CRITICAL_REGION_LOCAL(m_peerlist_lock);
copy_peers(pl_gray, m_peers_gray.get<by_addr>());
copy_peers(pl_white, m_peers_white.get<by_addr>());
}
void peerlist_manager::get_peerlist(peerlist_types& peers)
{
CRITICAL_REGION_LOCAL(m_peerlist_lock);
peers.white.reserve(peers.white.size() + m_peers_white.size());
peers.gray.reserve(peers.gray.size() + m_peers_gray.size());
peers.anchor.reserve(peers.anchor.size() + m_peers_anchor.size());
copy_peers(peers.white, m_peers_white.get<by_addr>());
copy_peers(peers.gray, m_peers_gray.get<by_addr>());
copy_peers(peers.anchor, m_peers_anchor.get<by_addr>());
}
}
BOOST_CLASS_VERSION(nodetool::peerlist_types, nodetool::CURRENT_PEERLIST_STORAGE_ARCHIVE_VER);
BOOST_CLASS_VERSION(nodetool::peerlist_join, nodetool::CURRENT_PEERLIST_STORAGE_ARCHIVE_VER);

View file

@ -30,33 +30,67 @@
#pragma once
#include <iosfwd>
#include <list>
#include <set>
#include <map>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/portable_binary_oarchive.hpp>
#include <boost/archive/portable_binary_iarchive.hpp>
#include <boost/serialization/version.hpp>
#include <string>
#include <vector>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/optional/optional.hpp>
#include <boost/range/adaptor/reversed.hpp>
#include "syncobj.h"
#include "cryptonote_config.h"
#include "net/enums.h"
#include "net/local_ip.h"
#include "p2p_protocol_defs.h"
#include "cryptonote_config.h"
#include "net_peerlist_boost_serialization.h"
#define CURRENT_PEERLIST_STORAGE_ARCHIVE_VER 6
#include "syncobj.h"
namespace nodetool
{
struct peerlist_types
{
std::vector<peerlist_entry> white;
std::vector<peerlist_entry> gray;
std::vector<anchor_peerlist_entry> anchor;
};
class peerlist_storage
{
public:
peerlist_storage()
: m_types{}
{}
//! \return Peers stored in stream `src` in `new_format` (portable archive or older non-portable).
static boost::optional<peerlist_storage> open(std::istream& src, const bool new_format);
//! \return Peers stored in file at `path`
static boost::optional<peerlist_storage> open(const std::string& path);
peerlist_storage(peerlist_storage&&) = default;
peerlist_storage(const peerlist_storage&) = delete;
~peerlist_storage() noexcept;
peerlist_storage& operator=(peerlist_storage&&) = default;
peerlist_storage& operator=(const peerlist_storage&) = delete;
//! Save peers from `this` and `other` in stream `dest`.
bool store(std::ostream& dest, const peerlist_types& other) const;
//! Save peers from `this` and `other` in one file at `path`.
bool store(const std::string& path, const peerlist_types& other) const;
//! \return Peers in `zone` and from remove from `this`.
peerlist_types take_zone(epee::net_utils::zone zone);
private:
peerlist_types m_types;
};
/************************************************************************/
/* */
@ -64,13 +98,13 @@ namespace nodetool
class peerlist_manager
{
public:
bool init(bool allow_local_ip);
bool deinit();
bool init(peerlist_types&& peers, bool allow_local_ip);
size_t get_white_peers_count(){CRITICAL_REGION_LOCAL(m_peerlist_lock); return m_peers_white.size();}
size_t get_gray_peers_count(){CRITICAL_REGION_LOCAL(m_peerlist_lock); return m_peers_gray.size();}
bool merge_peerlist(const std::vector<peerlist_entry>& outer_bs);
bool get_peerlist_head(std::vector<peerlist_entry>& bs_head, uint32_t depth = P2P_DEFAULT_PEERS_IN_HANDSHAKE);
bool get_peerlist_full(std::vector<peerlist_entry>& pl_gray, std::vector<peerlist_entry>& pl_white);
void get_peerlist(std::vector<peerlist_entry>& pl_gray, std::vector<peerlist_entry>& pl_white);
void get_peerlist(peerlist_types& peers);
bool get_white_peer_by_index(peerlist_entry& p, size_t i);
bool get_gray_peer_by_index(peerlist_entry& p, size_t i);
template<typename F> bool foreach(bool white, const F &f);
@ -135,18 +169,6 @@ namespace nodetool
>
> peers_indexed;
typedef boost::multi_index_container<
peerlist_entry,
boost::multi_index::indexed_by<
// access by peerlist_entry::id<
boost::multi_index::ordered_unique<boost::multi_index::tag<by_id>, boost::multi_index::member<peerlist_entry,uint64_t,&peerlist_entry::id> >,
// access by peerlist_entry::net_adress
boost::multi_index::ordered_unique<boost::multi_index::tag<by_addr>, boost::multi_index::member<peerlist_entry,epee::net_utils::network_address,&peerlist_entry::adr> >,
// sort by peerlist_entry::last_seen<
boost::multi_index::ordered_non_unique<boost::multi_index::tag<by_time>, boost::multi_index::member<peerlist_entry,int64_t,&peerlist_entry::last_seen> >
>
> peers_indexed_old;
typedef boost::multi_index_container<
anchor_peerlist_entry,
boost::multi_index::indexed_by<
@ -156,56 +178,8 @@ namespace nodetool
boost::multi_index::ordered_non_unique<boost::multi_index::tag<by_time>, boost::multi_index::member<anchor_peerlist_entry,int64_t,&anchor_peerlist_entry::first_seen> >
>
> anchor_peers_indexed;
public:
template <class Archive, class List, class Element, class t_version_type>
void serialize_peers(Archive &a, List &list, Element ple, const t_version_type ver)
{
if (typename Archive::is_saving())
{
uint64_t size = list.size();
a & size;
for (auto p: list)
{
a & p;
}
}
else
{
uint64_t size;
a & size;
list.clear();
while (size--)
{
a & ple;
list.insert(ple);
}
}
}
template <class Archive, class t_version_type>
void serialize(Archive &a, const t_version_type ver)
{
// at v6, we drop existing peerlists, because annoying change
if (ver < 6)
return;
CRITICAL_REGION_LOCAL(m_peerlist_lock);
#if 0
// trouble loading more than one peer, can't find why
a & m_peers_white;
a & m_peers_gray;
a & m_peers_anchor;
#else
serialize_peers(a, m_peers_white, peerlist_entry(), ver);
serialize_peers(a, m_peers_gray, peerlist_entry(), ver);
serialize_peers(a, m_peers_anchor, anchor_peerlist_entry(), ver);
#endif
}
private:
bool peers_indexed_from_old(const peers_indexed_old& pio, peers_indexed& pi);
void trim_white_peerlist();
void trim_gray_peerlist();
@ -220,34 +194,6 @@ namespace nodetool
anchor_peers_indexed m_peers_anchor;
};
//--------------------------------------------------------------------------------------------------
inline
bool peerlist_manager::init(bool allow_local_ip)
{
m_allow_local_ip = allow_local_ip;
return true;
}
//--------------------------------------------------------------------------------------------------
inline
bool peerlist_manager::deinit()
{
return true;
}
//--------------------------------------------------------------------------------------------------
inline
bool peerlist_manager::peers_indexed_from_old(const peers_indexed_old& pio, peers_indexed& pi)
{
for(auto x: pio)
{
auto by_addr_it = pi.get<by_addr>().find(x.adr);
if(by_addr_it == pi.get<by_addr>().end())
{
pi.insert(x);
}
}
return true;
}
//--------------------------------------------------------------------------------------------------
inline void peerlist_manager::trim_gray_peerlist()
{
while(m_peers_gray.size() > P2P_LOCAL_GRAY_PEERLIST_LIMIT)
@ -337,27 +283,6 @@ namespace nodetool
return true;
}
//--------------------------------------------------------------------------------------------------
inline
bool peerlist_manager::get_peerlist_full(std::vector<peerlist_entry>& pl_gray, std::vector<peerlist_entry>& pl_white)
{
CRITICAL_REGION_LOCAL(m_peerlist_lock);
peers_indexed::index<by_time>::type& by_time_index_gr=m_peers_gray.get<by_time>();
pl_gray.resize(pl_gray.size() + by_time_index_gr.size());
for(const peers_indexed::value_type& vl: boost::adaptors::reverse(by_time_index_gr))
{
pl_gray.push_back(vl);
}
peers_indexed::index<by_time>::type& by_time_index_wt=m_peers_white.get<by_time>();
pl_white.resize(pl_white.size() + by_time_index_wt.size());
for(const peers_indexed::value_type& vl: boost::adaptors::reverse(by_time_index_wt))
{
pl_white.push_back(vl);
}
return true;
}
//--------------------------------------------------------------------------------------------------
template<typename F> inline
bool peerlist_manager::foreach(bool white, const F &f)
{
@ -559,4 +484,3 @@ namespace nodetool
//--------------------------------------------------------------------------------------------------
}
BOOST_CLASS_VERSION(nodetool::peerlist_manager, CURRENT_PEERLIST_STORAGE_ARCHIVE_VER)

View file

@ -1,4 +1,4 @@
// Copyright (c) 2014-2018, The Monero Project
// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
@ -30,7 +30,11 @@
#pragma once
#include <cstring>
#include "common/expect.h"
#include "net/net_utils_base.h"
#include "net/tor_address.h"
#include "p2p/p2p_protocol_defs.h"
#ifdef CRYPTONOTE_PRUNING_DEBUG_SPOOF_SEED
@ -42,24 +46,37 @@ namespace boost
namespace serialization
{
template <class T, class Archive>
inline void do_serialize(Archive &a, epee::net_utils::network_address& na, T local)
inline void do_serialize(boost::mpl::false_, Archive &a, epee::net_utils::network_address& na)
{
if (typename Archive::is_saving()) local = na.as<T>();
a & local;
if (!typename Archive::is_saving()) na = local;
T addr{};
a & addr;
na = std::move(addr);
}
template <class T, class Archive>
inline void do_serialize(boost::mpl::true_, Archive &a, const epee::net_utils::network_address& na)
{
a & na.as<T>();
}
template <class Archive, class ver_type>
inline void serialize(Archive &a, epee::net_utils::network_address& na, const ver_type ver)
{
static constexpr const typename Archive::is_saving is_saving{};
uint8_t type;
if (typename Archive::is_saving())
type = na.get_type_id();
if (is_saving)
type = uint8_t(na.get_type_id());
a & type;
switch (type)
switch (epee::net_utils::address_type(type))
{
case epee::net_utils::ipv4_network_address::ID:
do_serialize(a, na, epee::net_utils::ipv4_network_address{0, 0});
break;
case epee::net_utils::ipv4_network_address::get_type_id():
do_serialize<epee::net_utils::ipv4_network_address>(is_saving, a, na);
break;
case net::tor_address::get_type_id():
do_serialize<net::tor_address>(is_saving, a, na);
break;
case epee::net_utils::address_type::invalid:
default:
throw std::runtime_error("Unsupported network address type");
}
@ -75,6 +92,47 @@ namespace boost
na = epee::net_utils::ipv4_network_address{ip, port};
}
template <class Archive, class ver_type>
inline void save(Archive& a, const net::tor_address& na, const ver_type)
{
const size_t length = std::strlen(na.host_str());
if (length > 255)
MONERO_THROW(net::error::invalid_tor_address, "Tor address too long");
const uint16_t port{na.port()};
const uint8_t len = length;
a & port;
a & len;
a.save_binary(na.host_str(), length);
}
template <class Archive, class ver_type>
inline void load(Archive& a, net::tor_address& na, const ver_type)
{
uint16_t port = 0;
uint8_t length = 0;
a & port;
a & length;
if (length > net::tor_address::buffer_size())
MONERO_THROW(net::error::invalid_tor_address, "Tor address too long");
char host[net::tor_address::buffer_size()] = {0};
a.load_binary(host, length);
host[sizeof(host) - 1] = 0;
if (std::strcmp(host, net::tor_address::unknown_str()) == 0)
na = net::tor_address::unknown();
else
na = MONERO_UNWRAP(net::tor_address::make(host, port));
}
template <class Archive, class ver_type>
inline void serialize(Archive &a, net::tor_address& na, const ver_type ver)
{
boost::serialization::split_free(a, na, ver);
}
template <class Archive, class ver_type>
inline void serialize(Archive &a, nodetool::peerlist_entry& pl, const ver_type ver)
{

View file

@ -34,6 +34,7 @@
#include <boost/serialization/version.hpp>
#include "serialization/keyvalue_serialization.h"
#include "net/net_utils_base.h"
#include "net/tor_address.h" // needed for serialization
#include "misc_language.h"
#include "string_tools.h"
#include "time_helper.h"
@ -204,7 +205,7 @@ namespace nodetool
std::vector<peerlist_entry_base<network_address_old>> local_peerlist;
for (const auto &p: this_ref.local_peerlist_new)
{
if (p.adr.get_type_id() == epee::net_utils::ipv4_network_address::ID)
if (p.adr.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id())
{
const epee::net_utils::network_address &na = p.adr;
const epee::net_utils::ipv4_network_address &ipv4 = na.as<const epee::net_utils::ipv4_network_address>();
@ -263,7 +264,7 @@ namespace nodetool
std::vector<peerlist_entry_base<network_address_old>> local_peerlist;
for (const auto &p: this_ref.local_peerlist_new)
{
if (p.adr.get_type_id() == epee::net_utils::ipv4_network_address::ID)
if (p.adr.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id())
{
const epee::net_utils::network_address &na = p.adr;
const epee::net_utils::ipv4_network_address &ipv4 = na.as<const epee::net_utils::ipv4_network_address>();