mirror of
https://github.com/monero-project/monero.git
synced 2025-06-21 06:04:11 -04:00
Merge pull request #4988
973403bc
Adding initial support for broadcasting transactions over Tor (Lee Clagett)
This commit is contained in:
commit
31bdf7bd11
39 changed files with 4298 additions and 831 deletions
|
@ -110,6 +110,7 @@ add_subdirectory(checkpoints)
|
|||
add_subdirectory(cryptonote_basic)
|
||||
add_subdirectory(cryptonote_core)
|
||||
add_subdirectory(multisig)
|
||||
add_subdirectory(net)
|
||||
if(NOT IOS)
|
||||
add_subdirectory(blockchain_db)
|
||||
endif()
|
||||
|
|
|
@ -108,6 +108,7 @@
|
|||
#define P2P_DEFAULT_PACKET_MAX_SIZE 50000000 //50000000 bytes maximum packet size
|
||||
#define P2P_DEFAULT_PEERS_IN_HANDSHAKE 250
|
||||
#define P2P_DEFAULT_CONNECTION_TIMEOUT 5000 //5 seconds
|
||||
#define P2P_DEFAULT_TOR_CONNECT_TIMEOUT 20 // seconds
|
||||
#define P2P_DEFAULT_PING_CONNECTION_TIMEOUT 2000 //2 seconds
|
||||
#define P2P_DEFAULT_INVOKE_TIMEOUT 60*2*1000 //2 minutes
|
||||
#define P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT 5000 //5 seconds
|
||||
|
|
|
@ -173,15 +173,6 @@ namespace cryptonote
|
|||
//handler_response_blocks_now(blob.size()); // XXX
|
||||
return m_p2p->invoke_notify_to_peer(t_parameter::ID, epee::strspan<uint8_t>(blob), context);
|
||||
}
|
||||
|
||||
template<class t_parameter>
|
||||
bool relay_post_notify(typename t_parameter::request& arg, cryptonote_connection_context& exclude_context)
|
||||
{
|
||||
LOG_PRINT_L2("[" << epee::net_utils::print_connection_context_short(exclude_context) << "] post relay " << typeid(t_parameter).name() << " -->");
|
||||
std::string arg_buff;
|
||||
epee::serialization::store_t_to_binary(arg, arg_buff);
|
||||
return m_p2p->relay_notify_to_all(t_parameter::ID, epee::strspan<uint8_t>(arg_buff), exclude_context);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -226,7 +226,7 @@ namespace cryptonote
|
|||
cnx.host = cntxt.m_remote_address.host_str();
|
||||
cnx.ip = "";
|
||||
cnx.port = "";
|
||||
if (cntxt.m_remote_address.get_type_id() == epee::net_utils::ipv4_network_address::ID)
|
||||
if (cntxt.m_remote_address.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id())
|
||||
{
|
||||
cnx.ip = cnx.host;
|
||||
cnx.port = std::to_string(cntxt.m_remote_address.as<epee::net_utils::ipv4_network_address>().port());
|
||||
|
@ -333,6 +333,13 @@ namespace cryptonote
|
|||
return true;
|
||||
}
|
||||
|
||||
// No chain synchronization over hidden networks (tor, i2p, etc.)
|
||||
if(context.m_remote_address.get_zone() != epee::net_utils::zone::public_)
|
||||
{
|
||||
context.m_state = cryptonote_connection_context::state_normal;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hshd.current_height > target)
|
||||
{
|
||||
/* As I don't know if accessing hshd from core could be a good practice,
|
||||
|
@ -2058,20 +2065,20 @@ skip:
|
|||
fluffy_arg.b.txs = fluffy_txs;
|
||||
|
||||
// sort peers between fluffy ones and others
|
||||
std::list<boost::uuids::uuid> fullConnections, fluffyConnections;
|
||||
std::vector<std::pair<epee::net_utils::zone, boost::uuids::uuid>> fullConnections, fluffyConnections;
|
||||
m_p2p->for_each_connection([this, &exclude_context, &fullConnections, &fluffyConnections](connection_context& context, nodetool::peerid_type peer_id, uint32_t support_flags)
|
||||
{
|
||||
if (peer_id && exclude_context.m_connection_id != context.m_connection_id)
|
||||
if (peer_id && exclude_context.m_connection_id != context.m_connection_id && context.m_remote_address.get_zone() == epee::net_utils::zone::public_)
|
||||
{
|
||||
if(m_core.fluffy_blocks_enabled() && (support_flags & P2P_SUPPORT_FLAG_FLUFFY_BLOCKS))
|
||||
{
|
||||
LOG_DEBUG_CC(context, "PEER SUPPORTS FLUFFY BLOCKS - RELAYING THIN/COMPACT WHATEVER BLOCK");
|
||||
fluffyConnections.push_back(context.m_connection_id);
|
||||
fluffyConnections.push_back({context.m_remote_address.get_zone(), context.m_connection_id});
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_DEBUG_CC(context, "PEER DOESN'T SUPPORT FLUFFY BLOCKS - RELAYING FULL BLOCK");
|
||||
fullConnections.push_back(context.m_connection_id);
|
||||
fullConnections.push_back({context.m_remote_address.get_zone(), context.m_connection_id});
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
@ -2082,13 +2089,13 @@ skip:
|
|||
{
|
||||
std::string fluffyBlob;
|
||||
epee::serialization::store_t_to_binary(fluffy_arg, fluffyBlob);
|
||||
m_p2p->relay_notify_to_list(NOTIFY_NEW_FLUFFY_BLOCK::ID, epee::strspan<uint8_t>(fluffyBlob), fluffyConnections);
|
||||
m_p2p->relay_notify_to_list(NOTIFY_NEW_FLUFFY_BLOCK::ID, epee::strspan<uint8_t>(fluffyBlob), std::move(fluffyConnections));
|
||||
}
|
||||
if (!fullConnections.empty())
|
||||
{
|
||||
std::string fullBlob;
|
||||
epee::serialization::store_t_to_binary(arg, fullBlob);
|
||||
m_p2p->relay_notify_to_list(NOTIFY_NEW_BLOCK::ID, epee::strspan<uint8_t>(fullBlob), fullConnections);
|
||||
m_p2p->relay_notify_to_list(NOTIFY_NEW_BLOCK::ID, epee::strspan<uint8_t>(fullBlob), std::move(fullConnections));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -2097,6 +2104,12 @@ skip:
|
|||
template<class t_core>
|
||||
bool t_cryptonote_protocol_handler<t_core>::relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context)
|
||||
{
|
||||
const bool hide_tx_broadcast =
|
||||
1 < m_p2p->get_zone_count() && exclude_context.m_remote_address.get_zone() == epee::net_utils::zone::invalid;
|
||||
|
||||
if (hide_tx_broadcast)
|
||||
MDEBUG("Attempting to conceal origin of tx via anonymity network connection(s)");
|
||||
|
||||
// no check for success, so tell core they're relayed unconditionally
|
||||
const bool pad_transactions = m_core.pad_transactions();
|
||||
size_t bytes = pad_transactions ? 9 /* header */ + 4 /* 1 + 'txs' */ + tools::get_varint_data(arg.txs.size()).size() : 0;
|
||||
|
@ -2131,7 +2144,30 @@ skip:
|
|||
// if the size of _ moved enough, we might lose byte in size encoding, we don't care
|
||||
}
|
||||
|
||||
return relay_post_notify<NOTIFY_NEW_TRANSACTIONS>(arg, exclude_context);
|
||||
std::vector<std::pair<epee::net_utils::zone, boost::uuids::uuid>> connections;
|
||||
m_p2p->for_each_connection([hide_tx_broadcast, &exclude_context, &connections](connection_context& context, nodetool::peerid_type peer_id, uint32_t support_flags)
|
||||
{
|
||||
const epee::net_utils::zone current_zone = context.m_remote_address.get_zone();
|
||||
const bool broadcast_to_peer =
|
||||
peer_id &&
|
||||
(hide_tx_broadcast != bool(current_zone == epee::net_utils::zone::public_)) &&
|
||||
exclude_context.m_connection_id != context.m_connection_id;
|
||||
|
||||
if (broadcast_to_peer)
|
||||
connections.push_back({current_zone, context.m_connection_id});
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
if (connections.empty())
|
||||
MERROR("Transaction not relayed - no" << (hide_tx_broadcast ? " privacy": "") << " peers available");
|
||||
else
|
||||
{
|
||||
std::string fullBlob;
|
||||
epee::serialization::store_t_to_binary(arg, fullBlob);
|
||||
m_p2p->relay_notify_to_list(NOTIFY_NEW_TRANSACTIONS::ID, epee::strspan<uint8_t>(fullBlob), std::move(connections));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------
|
||||
template<class t_core>
|
||||
|
|
34
src/net/CMakeLists.txt
Normal file
34
src/net/CMakeLists.txt
Normal file
|
@ -0,0 +1,34 @@
|
|||
# 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.
|
||||
|
||||
set(net_sources error.cpp parse.cpp socks.cpp tor_address.cpp)
|
||||
set(net_headers error.h parse.h socks.h tor_address.h)
|
||||
|
||||
monero_add_library(net ${net_sources} ${net_headers})
|
||||
target_link_libraries(net epee ${Boost_ASIO_LIBRARY})
|
||||
|
92
src/net/error.cpp
Normal file
92
src/net/error.cpp
Normal file
|
@ -0,0 +1,92 @@
|
|||
// 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 "error.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace
|
||||
{
|
||||
struct net_category : std::error_category
|
||||
{
|
||||
net_category() noexcept
|
||||
: std::error_category()
|
||||
{}
|
||||
|
||||
const char* name() const noexcept override
|
||||
{
|
||||
return "net::error_category";
|
||||
}
|
||||
|
||||
std::string message(int value) const override
|
||||
{
|
||||
switch (net::error(value))
|
||||
{
|
||||
case net::error::expected_tld:
|
||||
return "Expected top-level domain";
|
||||
case net::error::invalid_host:
|
||||
return "Host value is not valid";
|
||||
case net::error::invalid_i2p_address:
|
||||
return "Invalid I2P address";
|
||||
case net::error::invalid_port:
|
||||
return "Invalid port value (expected 0-65535)";
|
||||
case net::error::invalid_tor_address:
|
||||
return "Invalid Tor address";
|
||||
case net::error::unsupported_address:
|
||||
return "Network address not supported";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return "Unknown net::error";
|
||||
}
|
||||
|
||||
std::error_condition default_error_condition(int value) const noexcept override
|
||||
{
|
||||
switch (net::error(value))
|
||||
{
|
||||
case net::error::invalid_port:
|
||||
return std::errc::result_out_of_range;
|
||||
case net::error::expected_tld:
|
||||
case net::error::invalid_tor_address:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return std::error_condition{value, *this};
|
||||
}
|
||||
};
|
||||
} // anonymous
|
||||
|
||||
namespace net
|
||||
{
|
||||
std::error_category const& error_category() noexcept
|
||||
{
|
||||
static const net_category instance{};
|
||||
return instance;
|
||||
}
|
||||
}
|
64
src/net/error.h
Normal file
64
src/net/error.h
Normal file
|
@ -0,0 +1,64 @@
|
|||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <system_error>
|
||||
#include <type_traits>
|
||||
|
||||
namespace net
|
||||
{
|
||||
//! General net errors
|
||||
enum class error : int
|
||||
{
|
||||
// 0 reserved for success (as per expect<T>)
|
||||
expected_tld = 1, //!< Expected a tld
|
||||
invalid_host, //!< Hostname is not valid
|
||||
invalid_i2p_address,
|
||||
invalid_port, //!< Outside of 0-65535 range
|
||||
invalid_tor_address,//!< Invalid base32 or length
|
||||
unsupported_address //!< Type not supported by `get_network_address`
|
||||
};
|
||||
|
||||
//! \return `std::error_category` for `net` namespace.
|
||||
std::error_category const& error_category() noexcept;
|
||||
|
||||
//! \return `net::error` as a `std::error_code` value.
|
||||
inline std::error_code make_error_code(error value) noexcept
|
||||
{
|
||||
return std::error_code{int(value), error_category()};
|
||||
}
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<>
|
||||
struct is_error_code_enum<::net::error>
|
||||
: true_type
|
||||
{};
|
||||
}
|
45
src/net/fwd.h
Normal file
45
src/net/fwd.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace net
|
||||
{
|
||||
enum class error : int;
|
||||
class tor_address;
|
||||
|
||||
namespace socks
|
||||
{
|
||||
class client;
|
||||
template<typename> class connect_handler;
|
||||
enum class error : int;
|
||||
enum class version : std::uint8_t;
|
||||
}
|
||||
}
|
60
src/net/parse.cpp
Normal file
60
src/net/parse.cpp
Normal file
|
@ -0,0 +1,60 @@
|
|||
// 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 "parse.h"
|
||||
|
||||
#include "net/tor_address.h"
|
||||
#include "string_tools.h"
|
||||
|
||||
namespace net
|
||||
{
|
||||
expect<epee::net_utils::network_address>
|
||||
get_network_address(const boost::string_ref address, const std::uint16_t default_port)
|
||||
{
|
||||
const boost::string_ref host = address.substr(0, address.rfind(':'));
|
||||
|
||||
if (host.empty())
|
||||
return make_error_code(net::error::invalid_host);
|
||||
if (host.ends_with(".onion"))
|
||||
return tor_address::make(address, default_port);
|
||||
if (host.ends_with(".i2p"))
|
||||
return make_error_code(net::error::invalid_i2p_address); // not yet implemented (prevent public DNS lookup)
|
||||
|
||||
std::uint16_t port = default_port;
|
||||
if (host.size() < address.size())
|
||||
{
|
||||
if (!epee::string_tools::get_xtype_from_string(port, std::string{address.substr(host.size() + 1)}))
|
||||
return make_error_code(net::error::invalid_port);
|
||||
}
|
||||
|
||||
std::uint32_t ip = 0;
|
||||
if (epee::string_tools::get_ip_int32_from_string(ip, std::string{host}))
|
||||
return {epee::net_utils::ipv4_network_address{ip, port}};
|
||||
return make_error_code(net::error::unsupported_address);
|
||||
}
|
||||
}
|
54
src/net/parse.h
Normal file
54
src/net/parse.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <cstdint>
|
||||
|
||||
#include "common/expect.h"
|
||||
#include "net/net_utils_base.h"
|
||||
|
||||
namespace net
|
||||
{
|
||||
/*!
|
||||
Identifies onion and IPv4 addresses and returns them as a generic
|
||||
`network_address`. If the type is unsupported, it might be a hostname,
|
||||
and `error() == net::error::kUnsupportedAddress` is returned.
|
||||
|
||||
\param address An onion address, ipv4 address or hostname. Hostname
|
||||
will return an error.
|
||||
\param default_port If `address` does not specify a port, this value
|
||||
will be used.
|
||||
|
||||
\return A tor or IPv4 address, else error.
|
||||
*/
|
||||
expect<epee::net_utils::network_address>
|
||||
get_network_address(boost::string_ref address, std::uint16_t default_port);
|
||||
}
|
||||
|
308
src/net/socks.cpp
Normal file
308
src/net/socks.cpp
Normal file
|
@ -0,0 +1,308 @@
|
|||
// 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 "socks.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/asio/read.hpp>
|
||||
#include <boost/asio/write.hpp>
|
||||
#include <boost/endian/arithmetic.hpp>
|
||||
#include <boost/endian/conversion.hpp>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
#include "net/net_utils_base.h"
|
||||
#include "net/tor_address.h"
|
||||
|
||||
namespace net
|
||||
{
|
||||
namespace socks
|
||||
{
|
||||
namespace
|
||||
{
|
||||
constexpr const unsigned v4_reply_size = 8;
|
||||
constexpr const std::uint8_t v4_connect_command = 1;
|
||||
constexpr const std::uint8_t v4tor_resolve_command = 0xf0;
|
||||
constexpr const std::uint8_t v4_request_granted = 90;
|
||||
|
||||
struct v4_header
|
||||
{
|
||||
std::uint8_t version;
|
||||
std::uint8_t command_code;
|
||||
boost::endian::big_uint16_t port;
|
||||
boost::endian::big_uint32_t ip;
|
||||
};
|
||||
|
||||
std::size_t write_domain_header(epee::span<std::uint8_t> out, const std::uint8_t command, const std::uint16_t port, const boost::string_ref domain)
|
||||
{
|
||||
if (std::numeric_limits<std::size_t>::max() - sizeof(v4_header) - 2 < domain.size())
|
||||
return 0;
|
||||
|
||||
const std::size_t buf_size = sizeof(v4_header) + domain.size() + 2;
|
||||
if (out.size() < buf_size)
|
||||
return 0;
|
||||
|
||||
// version 4, 1 indicates invalid ip for domain extension
|
||||
const v4_header temp{4, command, port, std::uint32_t(1)};
|
||||
std::memcpy(out.data(), std::addressof(temp), sizeof(temp));
|
||||
out.remove_prefix(sizeof(temp));
|
||||
|
||||
*(out.data()) = 0;
|
||||
out.remove_prefix(1);
|
||||
|
||||
std::memcpy(out.data(), domain.data(), domain.size());
|
||||
out.remove_prefix(domain.size());
|
||||
|
||||
*(out.data()) = 0;
|
||||
return buf_size;
|
||||
}
|
||||
|
||||
struct socks_category : boost::system::error_category
|
||||
{
|
||||
explicit socks_category() noexcept
|
||||
: boost::system::error_category()
|
||||
{}
|
||||
|
||||
const char* name() const noexcept override
|
||||
{
|
||||
return "net::socks::error_category";
|
||||
}
|
||||
|
||||
virtual std::string message(int value) const override
|
||||
{
|
||||
switch (socks::error(value))
|
||||
{
|
||||
case socks::error::rejected:
|
||||
return "Socks request rejected or failed";
|
||||
case socks::error::identd_connection:
|
||||
return "Socks request rejected because server cannot connect to identd on the client";
|
||||
case socks::error::identd_user:
|
||||
return "Socks request rejected because the client program and identd report different user-ids";
|
||||
|
||||
case socks::error::bad_read:
|
||||
return "Socks boost::async_read read fewer bytes than expected";
|
||||
case socks::error::bad_write:
|
||||
return "Socks boost::async_write wrote fewer bytes than expected";
|
||||
case socks::error::unexpected_version:
|
||||
return "Socks server returned unexpected version in reply";
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "Unknown net::socks::error";
|
||||
}
|
||||
|
||||
boost::system::error_condition default_error_condition(int value) const noexcept override
|
||||
{
|
||||
switch (socks::error(value))
|
||||
{
|
||||
case socks::error::bad_read:
|
||||
case socks::error::bad_write:
|
||||
return boost::system::errc::io_error;
|
||||
case socks::error::unexpected_version:
|
||||
return boost::system::errc::protocol_error;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
if (1 <= value && value <= 256)
|
||||
return boost::system::errc::protocol_error;
|
||||
|
||||
return boost::system::error_condition{value, *this};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const boost::system::error_category& error_category() noexcept
|
||||
{
|
||||
static const socks_category instance{};
|
||||
return instance;
|
||||
}
|
||||
|
||||
struct client::completed
|
||||
{
|
||||
std::shared_ptr<client> self_;
|
||||
|
||||
void operator()(const boost::system::error_code error, const std::size_t bytes) const
|
||||
{
|
||||
static_assert(1 < sizeof(self_->buffer_), "buffer too small for v4 response");
|
||||
|
||||
if (self_)
|
||||
{
|
||||
client& self = *self_;
|
||||
self.buffer_size_ = std::min(bytes, sizeof(self.buffer_));
|
||||
|
||||
if (error)
|
||||
self.done(error, std::move(self_));
|
||||
else if (self.buffer().size() < sizeof(v4_header))
|
||||
self.done(socks::error::bad_read, std::move(self_));
|
||||
else if (self.buffer_[0] != 0) // response version
|
||||
self.done(socks::error::unexpected_version, std::move(self_));
|
||||
else if (self.buffer_[1] != v4_request_granted)
|
||||
self.done(socks::error(int(self.buffer_[1]) + 1), std::move(self_));
|
||||
else
|
||||
self.done(boost::system::error_code{}, std::move(self_));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct client::read
|
||||
{
|
||||
std::shared_ptr<client> self_;
|
||||
|
||||
static boost::asio::mutable_buffers_1 get_buffer(client& self) noexcept
|
||||
{
|
||||
static_assert(sizeof(v4_header) <= sizeof(self.buffer_), "buffer too small for v4 response");
|
||||
return boost::asio::buffer(self.buffer_, sizeof(v4_header));
|
||||
}
|
||||
|
||||
void operator()(const boost::system::error_code error, const std::size_t bytes)
|
||||
{
|
||||
if (self_)
|
||||
{
|
||||
client& self = *self_;
|
||||
if (error)
|
||||
self.done(error, std::move(self_));
|
||||
else if (bytes < self.buffer().size())
|
||||
self.done(socks::error::bad_write, std::move(self_));
|
||||
else
|
||||
boost::asio::async_read(self.proxy_, get_buffer(self), completed{std::move(self_)});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct client::write
|
||||
{
|
||||
std::shared_ptr<client> self_;
|
||||
|
||||
static boost::asio::const_buffers_1 get_buffer(client const& self) noexcept
|
||||
{
|
||||
return boost::asio::buffer(self.buffer_, self.buffer_size_);
|
||||
}
|
||||
|
||||
void operator()(const boost::system::error_code error)
|
||||
{
|
||||
if (self_)
|
||||
{
|
||||
client& self = *self_;
|
||||
if (error)
|
||||
self.done(error, std::move(self_));
|
||||
else
|
||||
boost::asio::async_write(self.proxy_, get_buffer(self), read{std::move(self_)});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
client::client(stream_type::socket&& proxy, socks::version ver)
|
||||
: proxy_(std::move(proxy)), buffer_size_(0), buffer_(), ver_(ver)
|
||||
{}
|
||||
|
||||
client::~client() {}
|
||||
|
||||
bool client::set_connect_command(const epee::net_utils::ipv4_network_address& address)
|
||||
{
|
||||
switch (socks_version())
|
||||
{
|
||||
case version::v4:
|
||||
case version::v4a:
|
||||
case version::v4a_tor:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
static_assert(sizeof(v4_header) < sizeof(buffer_), "buffer size too small for request");
|
||||
static_assert(0 < sizeof(buffer_), "buffer size too small for null termination");
|
||||
|
||||
// version 4
|
||||
const v4_header temp{4, v4_connect_command, address.port(), boost::endian::big_to_native(address.ip())};
|
||||
std::memcpy(std::addressof(buffer_), std::addressof(temp), sizeof(temp));
|
||||
buffer_[sizeof(temp)] = 0;
|
||||
buffer_size_ = sizeof(temp) + 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool client::set_connect_command(const boost::string_ref domain, std::uint16_t port)
|
||||
{
|
||||
switch (socks_version())
|
||||
{
|
||||
case version::v4a:
|
||||
case version::v4a_tor:
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::size_t buf_used = write_domain_header(buffer_, v4_connect_command, port, domain);
|
||||
buffer_size_ = buf_used;
|
||||
return buf_used != 0;
|
||||
}
|
||||
|
||||
bool client::set_connect_command(const net::tor_address& address)
|
||||
{
|
||||
if (!address.is_unknown())
|
||||
return set_connect_command(address.host_str(), address.port());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool client::set_resolve_command(boost::string_ref domain)
|
||||
{
|
||||
if (socks_version() != version::v4a_tor)
|
||||
return false;
|
||||
|
||||
const std::size_t buf_used = write_domain_header(buffer_, v4tor_resolve_command, 0, domain);
|
||||
buffer_size_ = buf_used;
|
||||
return buf_used != 0;
|
||||
}
|
||||
|
||||
bool client::connect_and_send(std::shared_ptr<client> self, const stream_type::endpoint& proxy_address)
|
||||
{
|
||||
if (self && !self->buffer().empty())
|
||||
{
|
||||
client& alias = *self;
|
||||
alias.proxy_.async_connect(proxy_address, write{std::move(self)});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool client::send(std::shared_ptr<client> self)
|
||||
{
|
||||
if (self && !self->buffer().empty())
|
||||
{
|
||||
client& alias = *self;
|
||||
boost::asio::async_write(alias.proxy_, write::get_buffer(alias), read{std::move(self)});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} // socks
|
||||
} // net
|
225
src/net/socks.h
Normal file
225
src/net/socks.h
Normal file
|
@ -0,0 +1,225 @@
|
|||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/asio/io_service.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
#include <boost/type_traits/integral_constant.hpp>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "net/fwd.h"
|
||||
#include "span.h"
|
||||
|
||||
namespace epee
|
||||
{
|
||||
namespace net_utils
|
||||
{
|
||||
class ipv4_network_address;
|
||||
}
|
||||
}
|
||||
|
||||
namespace net
|
||||
{
|
||||
namespace socks
|
||||
{
|
||||
//! Supported socks variants.
|
||||
enum class version : std::uint8_t
|
||||
{
|
||||
v4 = 0,
|
||||
v4a,
|
||||
v4a_tor //!< Extensions defined in Tor codebase
|
||||
};
|
||||
|
||||
//! Possible errors with socks communication. Defined in https://www.openssh.com/txt/socks4.protocol
|
||||
enum class error : int
|
||||
{
|
||||
// 0 is reserved for success value
|
||||
// 1-256 -> reserved for error values from socks server (+1 from wire value).
|
||||
rejected = 92,
|
||||
identd_connection,
|
||||
identd_user,
|
||||
// Specific to application
|
||||
bad_read = 257,
|
||||
bad_write,
|
||||
unexpected_version
|
||||
};
|
||||
|
||||
/* boost::system::error_code is extended for easier compatibility with
|
||||
boost::asio errors. If std::error_code is needed (with expect<T> for
|
||||
instance), then upgrade to boost 1.65+ or use conversion code in
|
||||
develop branch at boost/system/detail/std_interoperability.hpp */
|
||||
|
||||
//! \return boost::system::error_category for net::socks namespace
|
||||
const boost::system::error_category& error_category() noexcept;
|
||||
|
||||
//! \return net::socks::error as a boost::system::error_code.
|
||||
inline boost::system::error_code make_error_code(error value) noexcept
|
||||
{
|
||||
return boost::system::error_code{int(value), socks::error_category()};
|
||||
}
|
||||
|
||||
//! Client support for socks connect and resolve commands.
|
||||
class client
|
||||
{
|
||||
boost::asio::ip::tcp::socket proxy_;
|
||||
std::uint16_t buffer_size_;
|
||||
std::uint8_t buffer_[1024];
|
||||
socks::version ver_;
|
||||
|
||||
/*!
|
||||
Only invoked after `*send(...)` function completes or fails.
|
||||
`bool(error) == false` indicates success; `self.get()` is always
|
||||
`this` and allows implementations to skip
|
||||
`std::enable_shared_from_this<T>` (ASIO callbacks need shared_ptr).
|
||||
The design saves space and reduces cycles (everything uses moves,
|
||||
so no atomic operations are ever necessary).
|
||||
|
||||
\param error when processing last command (if any).
|
||||
\param self `shared_ptr<client>` handle to `this`.
|
||||
*/
|
||||
virtual void done(boost::system::error_code error, std::shared_ptr<client> self) = 0;
|
||||
|
||||
public:
|
||||
using stream_type = boost::asio::ip::tcp;
|
||||
|
||||
// defined in cpp
|
||||
struct write;
|
||||
struct read;
|
||||
struct completed;
|
||||
|
||||
/*!
|
||||
\param proxy ownership is passed into `this`. Does not have to be
|
||||
in connected state.
|
||||
\param ver socks version for the connection.
|
||||
*/
|
||||
explicit client(stream_type::socket&& proxy, socks::version ver);
|
||||
|
||||
client(const client&) = delete;
|
||||
virtual ~client();
|
||||
client& operator=(const client&) = delete;
|
||||
|
||||
//! \return Ownership of socks client socket object.
|
||||
stream_type::socket take_socket()
|
||||
{
|
||||
return stream_type::socket{std::move(proxy_)};
|
||||
}
|
||||
|
||||
//! \return Socks version.
|
||||
socks::version socks_version() const noexcept { return ver_; }
|
||||
|
||||
//! \return Contents of internal buffer.
|
||||
epee::span<const std::uint8_t> buffer() const noexcept
|
||||
{
|
||||
return {buffer_, buffer_size_};
|
||||
}
|
||||
|
||||
//! \post `buffer.empty()`.
|
||||
void clear_command() noexcept { buffer_size_ = 0; }
|
||||
|
||||
//! Try to set `address` as remote connection request.
|
||||
bool set_connect_command(const epee::net_utils::ipv4_network_address& address);
|
||||
|
||||
//! Try to set `domain` + `port` as remote connection request.
|
||||
bool set_connect_command(boost::string_ref domain, std::uint16_t port);
|
||||
|
||||
//! Try to set `address` as remote Tor hidden service connection request.
|
||||
bool set_connect_command(const net::tor_address& address);
|
||||
|
||||
//! Try to set `domain` as remote DNS A record lookup request.
|
||||
bool set_resolve_command(boost::string_ref domain);
|
||||
|
||||
/*!
|
||||
Asynchronously connect to `proxy_address` then issue command in
|
||||
`buffer()`. The `done(...)` method will be invoked upon completion
|
||||
with `self` and potential `error`s.
|
||||
|
||||
\note Must use one of the `self->set_*_command` calls before using
|
||||
this function.
|
||||
|
||||
\param self ownership of object is given to function.
|
||||
\param proxy_address of the socks server.
|
||||
\return False if `self->buffer().empty()` (no command set).
|
||||
*/
|
||||
static bool connect_and_send(std::shared_ptr<client> self, const stream_type::endpoint& proxy_address);
|
||||
|
||||
/*!
|
||||
Assume existing connection to proxy server; asynchronously issue
|
||||
command in `buffer()`. The `done(...)` method will be invoked
|
||||
upon completion with `self` and potential `error`s.
|
||||
|
||||
\note Must use one of the `self->set_*_command` calls before using
|
||||
the function.
|
||||
|
||||
\param self ownership of object is given to function.
|
||||
\return False if `self->buffer().empty()` (no command set).
|
||||
*/
|
||||
static bool send(std::shared_ptr<client> self);
|
||||
};
|
||||
|
||||
template<typename Handler>
|
||||
class connect_client : public client
|
||||
{
|
||||
Handler handler_;
|
||||
|
||||
virtual void done(boost::system::error_code error, std::shared_ptr<client>) override
|
||||
{
|
||||
handler_(error, take_socket());
|
||||
}
|
||||
|
||||
public:
|
||||
explicit connect_client(stream_type::socket&& proxy, socks::version ver, Handler&& handler)
|
||||
: client(std::move(proxy), ver), handler_(std::move(handler))
|
||||
{}
|
||||
|
||||
virtual ~connect_client() override {}
|
||||
};
|
||||
|
||||
template<typename Handler>
|
||||
inline std::shared_ptr<client>
|
||||
make_connect_client(client::stream_type::socket&& proxy, socks::version ver, Handler handler)
|
||||
{
|
||||
return std::make_shared<connect_client<Handler>>(std::move(proxy), ver, std::move(handler));
|
||||
}
|
||||
} // socks
|
||||
} // net
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace system
|
||||
{
|
||||
template<>
|
||||
struct is_error_code_enum<net::socks::error>
|
||||
: true_type
|
||||
{};
|
||||
} // system
|
||||
} // boost
|
203
src/net/tor_address.cpp
Normal file
203
src/net/tor_address.cpp
Normal file
|
@ -0,0 +1,203 @@
|
|||
// 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 "tor_address.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/spirit/include/karma_generate.hpp>
|
||||
#include <boost/spirit/include/karma_uint.hpp>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
|
||||
#include "net/error.h"
|
||||
#include "serialization/keyvalue_serialization.h"
|
||||
#include "storages/portable_storage.h"
|
||||
#include "string_tools.h"
|
||||
|
||||
namespace net
|
||||
{
|
||||
namespace
|
||||
{
|
||||
constexpr const char tld[] = u8".onion";
|
||||
constexpr const char unknown_host[] = "<unknown tor host>";
|
||||
|
||||
constexpr const unsigned v2_length = 16;
|
||||
constexpr const unsigned v3_length = 56;
|
||||
|
||||
constexpr const char base32_alphabet[] =
|
||||
u8"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz234567";
|
||||
|
||||
expect<void> host_check(boost::string_ref host) noexcept
|
||||
{
|
||||
if (!host.ends_with(tld))
|
||||
return {net::error::expected_tld};
|
||||
|
||||
host.remove_suffix(sizeof(tld) - 1);
|
||||
|
||||
//! \TODO v3 has checksum, base32 decoding is required to verify it
|
||||
if (host.size() != v2_length && host.size() != v3_length)
|
||||
return {net::error::invalid_tor_address};
|
||||
if (host.find_first_not_of(base32_alphabet) != boost::string_ref::npos)
|
||||
return {net::error::invalid_tor_address};
|
||||
|
||||
return success();
|
||||
}
|
||||
|
||||
struct tor_serialized
|
||||
{
|
||||
std::string host;
|
||||
std::uint16_t port;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(host)
|
||||
KV_SERIALIZE(port)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
}
|
||||
|
||||
tor_address::tor_address(const boost::string_ref host, const std::uint16_t port) noexcept
|
||||
: port_(port)
|
||||
{
|
||||
// this is a private constructor, throw if moved to public
|
||||
assert(host.size() < sizeof(host_));
|
||||
|
||||
const std::size_t length = std::min(sizeof(host_) - 1, host.size());
|
||||
std::memcpy(host_, host.data(), length);
|
||||
std::memset(host_ + length, 0, sizeof(host_) - length);
|
||||
}
|
||||
|
||||
const char* tor_address::unknown_str() noexcept
|
||||
{
|
||||
return unknown_host;
|
||||
}
|
||||
|
||||
tor_address::tor_address() noexcept
|
||||
: port_(0)
|
||||
{
|
||||
static_assert(sizeof(unknown_host) <= sizeof(host_), "bad buffer size");
|
||||
std::memcpy(host_, unknown_host, sizeof(unknown_host));
|
||||
std::memset(host_ + sizeof(unknown_host), 0, sizeof(host_) - sizeof(unknown_host));
|
||||
}
|
||||
|
||||
expect<tor_address> tor_address::make(const boost::string_ref address, const std::uint16_t default_port)
|
||||
{
|
||||
boost::string_ref host = address.substr(0, address.rfind(':'));
|
||||
const boost::string_ref port =
|
||||
address.substr(host.size() + (host.size() == address.size() ? 0 : 1));
|
||||
|
||||
MONERO_CHECK(host_check(host));
|
||||
|
||||
std::uint16_t porti = default_port;
|
||||
if (!port.empty() && !epee::string_tools::get_xtype_from_string(porti, std::string{port}))
|
||||
return {net::error::invalid_port};
|
||||
|
||||
static_assert(v2_length <= v3_length, "bad internal host size");
|
||||
static_assert(v3_length + sizeof(tld) == sizeof(tor_address::host_), "bad internal host size");
|
||||
return tor_address{host, porti};
|
||||
}
|
||||
|
||||
bool tor_address::_load(epee::serialization::portable_storage& src, epee::serialization::section* hparent)
|
||||
{
|
||||
tor_serialized in{};
|
||||
if (in._load(src, hparent) && in.host.size() < sizeof(host_) && (in.host == unknown_host || !host_check(in.host).has_error()))
|
||||
{
|
||||
std::memcpy(host_, in.host.data(), in.host.size());
|
||||
std::memset(host_ + in.host.size(), 0, sizeof(host_) - in.host.size());
|
||||
port_ = in.port;
|
||||
return true;
|
||||
}
|
||||
static_assert(sizeof(unknown_host) <= sizeof(host_), "bad buffer size");
|
||||
std::memcpy(host_, unknown_host, sizeof(unknown_host)); // include null terminator
|
||||
port_ = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tor_address::store(epee::serialization::portable_storage& dest, epee::serialization::section* hparent) const
|
||||
{
|
||||
const tor_serialized out{std::string{host_}, port_};
|
||||
return out.store(dest, hparent);
|
||||
}
|
||||
|
||||
tor_address::tor_address(const tor_address& rhs) noexcept
|
||||
: port_(rhs.port_)
|
||||
{
|
||||
std::memcpy(host_, rhs.host_, sizeof(host_));
|
||||
}
|
||||
|
||||
tor_address& tor_address::operator=(const tor_address& rhs) noexcept
|
||||
{
|
||||
if (this != std::addressof(rhs))
|
||||
{
|
||||
port_ = rhs.port_;
|
||||
std::memcpy(host_, rhs.host_, sizeof(host_));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool tor_address::is_unknown() const noexcept
|
||||
{
|
||||
static_assert(1 <= sizeof(host_), "host size too small");
|
||||
return host_[0] == '<'; // character is not allowed otherwise
|
||||
}
|
||||
|
||||
bool tor_address::equal(const tor_address& rhs) const noexcept
|
||||
{
|
||||
return port_ == rhs.port_ && is_same_host(rhs);
|
||||
}
|
||||
|
||||
bool tor_address::less(const tor_address& rhs) const noexcept
|
||||
{
|
||||
return std::strcmp(host_str(), rhs.host_str()) < 0 || port() < rhs.port();
|
||||
}
|
||||
|
||||
bool tor_address::is_same_host(const tor_address& rhs) const noexcept
|
||||
{
|
||||
//! \TODO v2 and v3 should be comparable - requires base32
|
||||
return std::strcmp(host_str(), rhs.host_str()) == 0;
|
||||
}
|
||||
|
||||
std::string tor_address::str() const
|
||||
{
|
||||
const std::size_t host_length = std::strlen(host_str());
|
||||
const std::size_t port_length =
|
||||
port_ == 0 ? 0 : std::numeric_limits<std::uint16_t>::digits10 + 2;
|
||||
|
||||
std::string out{};
|
||||
out.reserve(host_length + port_length);
|
||||
out.assign(host_str(), host_length);
|
||||
|
||||
if (port_ != 0)
|
||||
{
|
||||
out.push_back(':');
|
||||
namespace karma = boost::spirit::karma;
|
||||
karma::generate(std::back_inserter(out), karma::ushort_, port());
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
140
src/net/tor_address.h
Normal file
140
src/net/tor_address.h
Normal file
|
@ -0,0 +1,140 @@
|
|||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "common/expect.h"
|
||||
#include "net/enums.h"
|
||||
#include "net/error.h"
|
||||
|
||||
namespace epee
|
||||
{
|
||||
namespace serialization
|
||||
{
|
||||
class portable_storage;
|
||||
struct section;
|
||||
}
|
||||
}
|
||||
|
||||
namespace net
|
||||
{
|
||||
//! Tor onion address; internal format not condensed/decoded.
|
||||
class tor_address
|
||||
{
|
||||
std::uint16_t port_;
|
||||
char host_[63]; // null-terminated
|
||||
|
||||
//! Keep in private, `host.size()` has no runtime check
|
||||
tor_address(boost::string_ref host, std::uint16_t port) noexcept;
|
||||
|
||||
public:
|
||||
//! \return Size of internal buffer for host.
|
||||
static constexpr std::size_t buffer_size() noexcept { return sizeof(host_); }
|
||||
|
||||
//! \return `<unknown tor host>`.
|
||||
static const char* unknown_str() noexcept;
|
||||
|
||||
//! An object with `port() == 0` and `host_str() == unknown_str()`.
|
||||
tor_address() noexcept;
|
||||
|
||||
//! \return A default constructed `tor_address` object.
|
||||
static tor_address unknown() noexcept { return tor_address{}; }
|
||||
|
||||
/*!
|
||||
Parse `address` in onion v2 or v3 format with (i.e. x.onion:80)
|
||||
with `default_port` being used iff port is not specified in
|
||||
`address`.
|
||||
*/
|
||||
static expect<tor_address> make(boost::string_ref address, std::uint16_t default_port = 0);
|
||||
|
||||
//! Load from epee p2p format, and \return false if not valid tor address
|
||||
bool _load(epee::serialization::portable_storage& src, epee::serialization::section* hparent);
|
||||
|
||||
//! Store in epee p2p format
|
||||
bool store(epee::serialization::portable_storage& dest, epee::serialization::section* hparent) const;
|
||||
|
||||
// Moves and copies are currently identical
|
||||
|
||||
tor_address(const tor_address& rhs) noexcept;
|
||||
~tor_address() = default;
|
||||
tor_address& operator=(const tor_address& rhs) noexcept;
|
||||
|
||||
//! \return True if default constructed or via `unknown()`.
|
||||
bool is_unknown() const noexcept;
|
||||
|
||||
bool equal(const tor_address& rhs) const noexcept;
|
||||
bool less(const tor_address& rhs) const noexcept;
|
||||
|
||||
//! \return True if onion addresses are identical.
|
||||
bool is_same_host(const tor_address& rhs) const noexcept;
|
||||
|
||||
//! \return `x.onion` or `x.onion:z` if `port() != 0`.
|
||||
std::string str() const;
|
||||
|
||||
//! \return Null-terminated `x.onion` value or `unknown_str()`.
|
||||
const char* host_str() const noexcept { return host_; }
|
||||
|
||||
//! \return Port value or `0` if unspecified.
|
||||
std::uint16_t port() const noexcept { return port_; }
|
||||
|
||||
static constexpr bool is_loopback() noexcept { return false; }
|
||||
static constexpr bool is_local() noexcept { return false; }
|
||||
|
||||
static constexpr epee::net_utils::address_type get_type_id() noexcept
|
||||
{
|
||||
return epee::net_utils::address_type::tor;
|
||||
}
|
||||
|
||||
static constexpr epee::net_utils::zone get_zone() noexcept
|
||||
{
|
||||
return epee::net_utils::zone::tor;
|
||||
}
|
||||
|
||||
//! \return `!is_unknown()`.
|
||||
bool is_blockable() const noexcept { return !is_unknown(); }
|
||||
};
|
||||
|
||||
inline bool operator==(const tor_address& lhs, const tor_address& rhs) noexcept
|
||||
{
|
||||
return lhs.equal(rhs);
|
||||
}
|
||||
|
||||
inline bool operator!=(const tor_address& lhs, const tor_address& rhs) noexcept
|
||||
{
|
||||
return !lhs.equal(rhs);
|
||||
}
|
||||
|
||||
inline bool operator<(const tor_address& lhs, const tor_address& rhs) noexcept
|
||||
{
|
||||
return lhs.less(rhs);
|
||||
}
|
||||
} // net
|
|
@ -40,6 +40,7 @@ target_link_libraries(p2p
|
|||
PUBLIC
|
||||
version
|
||||
cryptonote_core
|
||||
net
|
||||
${UPNP_LIBRARIES}
|
||||
${Boost_CHRONO_LIBRARY}
|
||||
${Boost_PROGRAM_OPTIONS_LIBRARY}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
1129
src/p2p/net_node.inl
1129
src/p2p/net_node.inl
File diff suppressed because it is too large
Load diff
|
@ -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
295
src/p2p/net_peerlist.cpp
Normal 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);
|
||||
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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>();
|
||||
|
|
|
@ -112,6 +112,7 @@ target_link_libraries(rpc
|
|||
common
|
||||
cryptonote_core
|
||||
cryptonote_protocol
|
||||
net
|
||||
version
|
||||
${Boost_REGEX_LIBRARY}
|
||||
${Boost_THREAD_LIBRARY}
|
||||
|
|
|
@ -42,6 +42,7 @@ using namespace epee;
|
|||
#include "cryptonote_basic/account.h"
|
||||
#include "cryptonote_basic/cryptonote_basic_impl.h"
|
||||
#include "misc_language.h"
|
||||
#include "net/parse.h"
|
||||
#include "storages/http_abstract_invoke.h"
|
||||
#include "crypto/hash.h"
|
||||
#include "rpc/rpc_args.h"
|
||||
|
@ -185,12 +186,12 @@ namespace cryptonote
|
|||
res.tx_count = m_core.get_blockchain_storage().get_total_transactions() - res.height; //without coinbase
|
||||
res.tx_pool_size = m_core.get_pool_transactions_count();
|
||||
res.alt_blocks_count = restricted ? 0 : m_core.get_blockchain_storage().get_alternative_blocks_count();
|
||||
uint64_t total_conn = restricted ? 0 : m_p2p.get_connections_count();
|
||||
res.outgoing_connections_count = restricted ? 0 : m_p2p.get_outgoing_connections_count();
|
||||
uint64_t total_conn = restricted ? 0 : m_p2p.get_public_connections_count();
|
||||
res.outgoing_connections_count = restricted ? 0 : m_p2p.get_public_outgoing_connections_count();
|
||||
res.incoming_connections_count = restricted ? 0 : (total_conn - res.outgoing_connections_count);
|
||||
res.rpc_connections_count = restricted ? 0 : get_connections_count();
|
||||
res.white_peerlist_size = restricted ? 0 : m_p2p.get_peerlist_manager().get_white_peers_count();
|
||||
res.grey_peerlist_size = restricted ? 0 : m_p2p.get_peerlist_manager().get_gray_peers_count();
|
||||
res.white_peerlist_size = restricted ? 0 : m_p2p.get_public_white_peers_count();
|
||||
res.grey_peerlist_size = restricted ? 0 : m_p2p.get_public_gray_peers_count();
|
||||
|
||||
cryptonote::network_type net_type = nettype();
|
||||
res.mainnet = net_type == MAINNET;
|
||||
|
@ -902,12 +903,12 @@ namespace cryptonote
|
|||
PERF_TIMER(on_get_peer_list);
|
||||
std::vector<nodetool::peerlist_entry> white_list;
|
||||
std::vector<nodetool::peerlist_entry> gray_list;
|
||||
m_p2p.get_peerlist_manager().get_peerlist_full(gray_list, white_list);
|
||||
m_p2p.get_public_peerlist(gray_list, white_list);
|
||||
|
||||
res.white_list.reserve(white_list.size());
|
||||
for (auto & entry : white_list)
|
||||
{
|
||||
if (entry.adr.get_type_id() == epee::net_utils::ipv4_network_address::ID)
|
||||
if (entry.adr.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id())
|
||||
res.white_list.emplace_back(entry.id, entry.adr.as<epee::net_utils::ipv4_network_address>().ip(),
|
||||
entry.adr.as<epee::net_utils::ipv4_network_address>().port(), entry.last_seen, entry.pruning_seed);
|
||||
else
|
||||
|
@ -917,7 +918,7 @@ namespace cryptonote
|
|||
res.gray_list.reserve(gray_list.size());
|
||||
for (auto & entry : gray_list)
|
||||
{
|
||||
if (entry.adr.get_type_id() == epee::net_utils::ipv4_network_address::ID)
|
||||
if (entry.adr.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id())
|
||||
res.gray_list.emplace_back(entry.id, entry.adr.as<epee::net_utils::ipv4_network_address>().ip(),
|
||||
entry.adr.as<epee::net_utils::ipv4_network_address>().port(), entry.last_seen, entry.pruning_seed);
|
||||
else
|
||||
|
@ -1646,12 +1647,12 @@ namespace cryptonote
|
|||
res.tx_count = m_core.get_blockchain_storage().get_total_transactions() - res.height; //without coinbase
|
||||
res.tx_pool_size = m_core.get_pool_transactions_count();
|
||||
res.alt_blocks_count = restricted ? 0 : m_core.get_blockchain_storage().get_alternative_blocks_count();
|
||||
uint64_t total_conn = restricted ? 0 : m_p2p.get_connections_count();
|
||||
res.outgoing_connections_count = restricted ? 0 : m_p2p.get_outgoing_connections_count();
|
||||
uint64_t total_conn = restricted ? 0 : m_p2p.get_public_connections_count();
|
||||
res.outgoing_connections_count = restricted ? 0 : m_p2p.get_public_outgoing_connections_count();
|
||||
res.incoming_connections_count = restricted ? 0 : (total_conn - res.outgoing_connections_count);
|
||||
res.rpc_connections_count = restricted ? 0 : get_connections_count();
|
||||
res.white_peerlist_size = restricted ? 0 : m_p2p.get_peerlist_manager().get_white_peers_count();
|
||||
res.grey_peerlist_size = restricted ? 0 : m_p2p.get_peerlist_manager().get_gray_peers_count();
|
||||
res.white_peerlist_size = restricted ? 0 : m_p2p.get_public_white_peers_count();
|
||||
res.grey_peerlist_size = restricted ? 0 : m_p2p.get_public_gray_peers_count();
|
||||
|
||||
cryptonote::network_type net_type = nettype();
|
||||
res.mainnet = net_type == MAINNET;
|
||||
|
@ -1730,12 +1731,14 @@ namespace cryptonote
|
|||
epee::net_utils::network_address na;
|
||||
if (!i->host.empty())
|
||||
{
|
||||
if (!epee::net_utils::create_network_address(na, i->host))
|
||||
auto na_parsed = net::get_network_address(i->host, 0);
|
||||
if (!na_parsed)
|
||||
{
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
|
||||
error_resp.message = "Unsupported host type";
|
||||
return false;
|
||||
}
|
||||
na = std::move(*na_parsed);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1958,11 +1961,7 @@ namespace cryptonote
|
|||
bool core_rpc_server::on_out_peers(const COMMAND_RPC_OUT_PEERS::request& req, COMMAND_RPC_OUT_PEERS::response& res, const connection_context *ctx)
|
||||
{
|
||||
PERF_TIMER(on_out_peers);
|
||||
size_t n_connections = m_p2p.get_outgoing_connections_count();
|
||||
size_t n_delete = (n_connections > req.out_peers) ? n_connections - req.out_peers : 0;
|
||||
m_p2p.m_config.m_net_config.max_out_connection_count = req.out_peers;
|
||||
if (n_delete)
|
||||
m_p2p.delete_out_connections(n_delete);
|
||||
m_p2p.change_max_out_public_peers(req.out_peers);
|
||||
res.status = CORE_RPC_STATUS_OK;
|
||||
return true;
|
||||
}
|
||||
|
@ -1970,11 +1969,7 @@ namespace cryptonote
|
|||
bool core_rpc_server::on_in_peers(const COMMAND_RPC_IN_PEERS::request& req, COMMAND_RPC_IN_PEERS::response& res, const connection_context *ctx)
|
||||
{
|
||||
PERF_TIMER(on_in_peers);
|
||||
size_t n_connections = m_p2p.get_incoming_connections_count();
|
||||
size_t n_delete = (n_connections > req.in_peers) ? n_connections - req.in_peers : 0;
|
||||
m_p2p.m_config.m_net_config.max_in_connection_count = req.in_peers;
|
||||
if (n_delete)
|
||||
m_p2p.delete_in_connections(n_delete);
|
||||
m_p2p.change_max_in_public_peers(req.in_peers);
|
||||
res.status = CORE_RPC_STATUS_OK;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -423,13 +423,13 @@ namespace rpc
|
|||
|
||||
res.info.alt_blocks_count = chain.get_alternative_blocks_count();
|
||||
|
||||
uint64_t total_conn = m_p2p.get_connections_count();
|
||||
res.info.outgoing_connections_count = m_p2p.get_outgoing_connections_count();
|
||||
uint64_t total_conn = m_p2p.get_public_connections_count();
|
||||
res.info.outgoing_connections_count = m_p2p.get_public_outgoing_connections_count();
|
||||
res.info.incoming_connections_count = total_conn - res.info.outgoing_connections_count;
|
||||
|
||||
res.info.white_peerlist_size = m_p2p.get_peerlist_manager().get_white_peers_count();
|
||||
res.info.white_peerlist_size = m_p2p.get_public_white_peers_count();
|
||||
|
||||
res.info.grey_peerlist_size = m_p2p.get_peerlist_manager().get_gray_peers_count();
|
||||
res.info.grey_peerlist_size = m_p2p.get_public_gray_peers_count();
|
||||
|
||||
res.info.mainnet = m_core.get_nettype() == MAINNET;
|
||||
res.info.testnet = m_core.get_nettype() == TESTNET;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue