aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorThomas Winget <tewinget@gmail.com>2019-04-10 18:34:30 -0400
committerThomas Winget <tewinget@gmail.com>2019-07-31 20:04:57 -0400
commit155475d9719694c838297cb3fbc94a2782329d46 (patch)
tree0a8ac9cfc80c123f456fc2416b60752a42ae4f9b /src
parentMerge pull request #5635 (diff)
downloadmonero-155475d9719694c838297cb3fbc94a2782329d46.tar.xz
Add IPv6 support
new cli options (RPC ones also apply to wallet): --p2p-bind-ipv6-address (default = "::") --p2p-bind-port-ipv6 (default same as ipv4 port for given nettype) --rpc-bind-ipv6-address (default = "::1") --p2p-use-ipv6 (default false) --rpc-use-ipv6 (default false) --p2p-require-ipv4 (default true, if ipv4 bind fails and this is true, will not continue even if ipv6 bind successful) --rpc-require-ipv4 (default true, description as above) ipv6 addresses are to be specified as "[xx:xx:xx::xx:xx]:port" except in the cases of the cli args for bind address. For those the square braces can be omitted.
Diffstat (limited to '')
-rw-r--r--src/daemon/rpc_command_executor.cpp2
-rw-r--r--src/net/parse.cpp59
-rw-r--r--src/net/parse.h2
-rw-r--r--src/p2p/net_node.cpp21
-rw-r--r--src/p2p/net_node.h21
-rw-r--r--src/p2p/net_node.inl192
-rw-r--r--src/p2p/net_peerlist_boost_serialization.h31
-rw-r--r--src/rpc/core_rpc_server.cpp12
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h3
-rw-r--r--src/rpc/rpc_args.cpp37
-rw-r--r--src/rpc/rpc_args.h6
-rw-r--r--src/wallet/wallet_rpc_server.cpp4
12 files changed, 344 insertions, 46 deletions
diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp
index ea986a398..ca80e8e98 100644
--- a/src/daemon/rpc_command_executor.cpp
+++ b/src/daemon/rpc_command_executor.cpp
@@ -68,7 +68,7 @@ namespace {
std::string id_str;
std::string port_str;
std::string elapsed = peer.last_seen == 0 ? "never" : epee::misc_utils::get_time_interval_string(now - last_seen);
- std::string ip_str = epee::string_tools::get_ip_string_from_int32(peer.ip);
+ std::string ip_str = peer.ip != 0 ? epee::string_tools::get_ip_string_from_int32(peer.ip) : std::string("[") + peer.host + "]";
std::stringstream peer_id_str;
peer_id_str << std::hex << std::setw(16) << peer.id;
peer_id_str >> id_str;
diff --git a/src/net/parse.cpp b/src/net/parse.cpp
index d93d7d352..7b74f2b75 100644
--- a/src/net/parse.cpp
+++ b/src/net/parse.cpp
@@ -34,28 +34,69 @@
namespace net
{
+ void get_network_address_host_and_port(const std::string& address, std::string& host, std::string& port)
+ {
+ // require ipv6 address format "[addr:addr:addr:...:addr]:port"
+ if (address.find(']') != std::string::npos)
+ {
+ host = address.substr(1, address.rfind(']') - 1);
+ if ((host.size() + 2) < address.size())
+ {
+ port = address.substr(address.rfind(':') + 1);
+ }
+ }
+ else
+ {
+ host = address.substr(0, address.rfind(':'));
+ if (host.size() < address.size())
+ {
+ port = address.substr(host.size() + 1);
+ }
+ }
+ }
+
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(':'));
+ std::string host_str = "";
+ std::string port_str = "";
+
+ bool ipv6 = false;
+
+ get_network_address_host_and_port(std::string(address), host_str, port_str);
- if (host.empty())
+ boost::string_ref host_str_ref(host_str);
+ boost::string_ref port_str_ref(port_str);
+
+ if (host_str.empty())
return make_error_code(net::error::invalid_host);
- if (host.ends_with(".onion"))
+ if (host_str_ref.ends_with(".onion"))
return tor_address::make(address, default_port);
- if (host.ends_with(".i2p"))
+ if (host_str_ref.ends_with(".i2p"))
return i2p_address::make(address, default_port);
+ boost::system::error_code ec;
+ boost::asio::ip::address_v6 v6 = boost::asio::ip::make_address_v6(host_str, ec);
+ ipv6 = !ec;
+
std::uint16_t port = default_port;
- if (host.size() < address.size())
+ if (port_str.size())
{
- if (!epee::string_tools::get_xtype_from_string(port, std::string{address.substr(host.size() + 1)}))
+ if (!epee::string_tools::get_xtype_from_string(port, port_str))
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}};
+ if (ipv6)
+ {
+ return {epee::net_utils::ipv6_network_address{v6, port}};
+ }
+ else
+ {
+ std::uint32_t ip = 0;
+ if (epee::string_tools::get_ip_int32_from_string(ip, host_str))
+ return {epee::net_utils::ipv4_network_address{ip, port}};
+ }
+
return make_error_code(net::error::unsupported_address);
}
diff --git a/src/net/parse.h b/src/net/parse.h
index 9f0d66ea6..0d8fda711 100644
--- a/src/net/parse.h
+++ b/src/net/parse.h
@@ -36,6 +36,8 @@
namespace net
{
+ void get_network_address_host_and_port(const std::string& address, std::string& host, std::string& port);
+
/*!
Identifies onion, i2p and IPv4 addresses and returns them as a generic
`network_address`. If the type is unsupported, it might be a hostname,
diff --git a/src/p2p/net_node.cpp b/src/p2p/net_node.cpp
index a44297c17..bb51be242 100644
--- a/src/p2p/net_node.cpp
+++ b/src/p2p/net_node.cpp
@@ -108,10 +108,11 @@ namespace
namespace nodetool
{
- const command_line::arg_descriptor<std::string> arg_p2p_bind_ip = {"p2p-bind-ip", "Interface for p2p network protocol", "0.0.0.0"};
+ const command_line::arg_descriptor<std::string> arg_p2p_bind_ip = {"p2p-bind-ip", "Interface for p2p network protocol (IPv4)", "0.0.0.0"};
+ const command_line::arg_descriptor<std::string> arg_p2p_bind_ipv6_address = {"p2p-bind-ipv6-address", "Interface for p2p network protocol (IPv6)", "::"};
const command_line::arg_descriptor<std::string, false, true, 2> arg_p2p_bind_port = {
"p2p-bind-port"
- , "Port for p2p network protocol"
+ , "Port for p2p network protocol (IPv4)"
, std::to_string(config::P2P_DEFAULT_PORT)
, {{ &cryptonote::arg_testnet_on, &cryptonote::arg_stagenet_on }}
, [](std::array<bool, 2> testnet_stagenet, bool defaulted, std::string val)->std::string {
@@ -122,6 +123,20 @@ namespace nodetool
return val;
}
};
+ const command_line::arg_descriptor<std::string, false, true, 2> arg_p2p_bind_port_ipv6 = {
+ "p2p-bind-port-ipv6"
+ , "Port for p2p network protocol (IPv6)"
+ , std::to_string(config::P2P_DEFAULT_PORT)
+ , {{ &cryptonote::arg_testnet_on, &cryptonote::arg_stagenet_on }}
+ , [](std::array<bool, 2> testnet_stagenet, bool defaulted, std::string val)->std::string {
+ if (testnet_stagenet[0] && defaulted)
+ return std::to_string(config::testnet::P2P_DEFAULT_PORT);
+ else if (testnet_stagenet[1] && defaulted)
+ return std::to_string(config::stagenet::P2P_DEFAULT_PORT);
+ return val;
+ }
+ };
+
const command_line::arg_descriptor<uint32_t> arg_p2p_external_port = {"p2p-external-port", "External port for p2p network protocol (if port forwarding used with NAT)", 0};
const command_line::arg_descriptor<bool> arg_p2p_allow_local_ip = {"allow-local-ip", "Allow local ip add to peer list, mostly in debug purposes"};
const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_add_peer = {"add-peer", "Manually add peer to local peerlist"};
@@ -136,6 +151,8 @@ namespace nodetool
const command_line::arg_descriptor<bool> arg_no_igd = {"no-igd", "Disable UPnP port mapping"};
const command_line::arg_descriptor<std::string> arg_igd = {"igd", "UPnP port mapping (disabled, enabled, delayed)", "delayed"};
+ const command_line::arg_descriptor<bool> arg_p2p_use_ipv6 = {"p2p-use-ipv6", "Enable IPv6 for p2p", false};
+ const command_line::arg_descriptor<bool> arg_p2p_require_ipv4 = {"p2p-require-ipv4", "Require successful IPv4 bind for p2p", true};
const command_line::arg_descriptor<int64_t> arg_out_peers = {"out-peers", "set max number of out peers", -1};
const command_line::arg_descriptor<int64_t> arg_in_peers = {"in-peers", "set max number of in peers", -1};
const command_line::arg_descriptor<int> arg_tos_flag = {"tos-flag", "set TOS flag", -1};
diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h
index 0d95ceb99..cf6b2c67b 100644
--- a/src/p2p/net_node.h
+++ b/src/p2p/net_node.h
@@ -151,7 +151,9 @@ namespace nodetool
: m_connect(nullptr),
m_net_server(epee::net_utils::e_connection_type_P2P),
m_bind_ip(),
+ m_bind_ipv6_address(),
m_port(),
+ m_port_ipv6(),
m_our_address(),
m_peerlist(),
m_config{},
@@ -167,7 +169,9 @@ namespace nodetool
: m_connect(nullptr),
m_net_server(public_service, epee::net_utils::e_connection_type_P2P),
m_bind_ip(),
+ m_bind_ipv6_address(),
m_port(),
+ m_port_ipv6(),
m_our_address(),
m_peerlist(),
m_config{},
@@ -182,7 +186,9 @@ namespace nodetool
connect_func* m_connect;
net_server m_net_server;
std::string m_bind_ip;
+ std::string m_bind_ipv6_address;
std::string m_port;
+ std::string m_port_ipv6;
epee::net_utils::network_address m_our_address; // in anonymity networks
peerlist_manager m_peerlist;
config m_config;
@@ -357,7 +363,13 @@ namespace nodetool
bool is_peer_used(const peerlist_entry& peer);
bool is_peer_used(const anchor_peerlist_entry& peer);
bool is_addr_connected(const epee::net_utils::network_address& peer);
- void add_upnp_port_mapping(uint32_t port);
+ void add_upnp_port_mapping_impl(uint32_t port, bool ipv6=false);
+ void add_upnp_port_mapping_v4(uint32_t port);
+ void add_upnp_port_mapping_v6(uint32_t port);
+ void add_upnp_port_mapping(uint32_t port, bool ipv4=true, bool ipv6=false);
+ void delete_upnp_port_mapping_impl(uint32_t port, bool ipv6=false);
+ void delete_upnp_port_mapping_v4(uint32_t port);
+ void delete_upnp_port_mapping_v6(uint32_t port);
void delete_upnp_port_mapping(uint32_t port);
template<class t_callback>
bool try_ping(basic_node_data& node_data, p2p_connection_context& context, const t_callback &cb);
@@ -419,12 +431,15 @@ namespace nodetool
bool m_have_address;
bool m_first_connection_maker_call;
uint32_t m_listening_port;
+ uint32_t m_listening_port_ipv6;
uint32_t m_external_port;
uint16_t m_rpc_port;
bool m_allow_local_ip;
bool m_hide_my_port;
igd_t m_igd;
bool m_offline;
+ bool m_use_ipv6;
+ bool m_require_ipv4;
std::atomic<bool> is_closing;
std::unique_ptr<boost::thread> mPeersLoggerThread;
//critical_section m_connections_lock;
@@ -485,7 +500,11 @@ namespace nodetool
const int64_t default_limit_up = P2P_DEFAULT_LIMIT_RATE_UP; // kB/s
const int64_t default_limit_down = P2P_DEFAULT_LIMIT_RATE_DOWN; // kB/s
extern const command_line::arg_descriptor<std::string> arg_p2p_bind_ip;
+ extern const command_line::arg_descriptor<std::string> arg_p2p_bind_ipv6_address;
extern const command_line::arg_descriptor<std::string, false, true, 2> arg_p2p_bind_port;
+ extern const command_line::arg_descriptor<std::string, false, true, 2> arg_p2p_bind_port_ipv6;
+ extern const command_line::arg_descriptor<bool> arg_p2p_use_ipv6;
+ extern const command_line::arg_descriptor<bool> arg_p2p_require_ipv4;
extern const command_line::arg_descriptor<uint32_t> arg_p2p_external_port;
extern const command_line::arg_descriptor<bool> arg_p2p_allow_local_ip;
extern const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_add_peer;
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl
index 98484fe78..426767365 100644
--- a/src/p2p/net_node.inl
+++ b/src/p2p/net_node.inl
@@ -93,7 +93,11 @@ namespace nodetool
void node_server<t_payload_net_handler>::init_options(boost::program_options::options_description& desc)
{
command_line::add_arg(desc, arg_p2p_bind_ip);
+ command_line::add_arg(desc, arg_p2p_bind_ipv6_address);
command_line::add_arg(desc, arg_p2p_bind_port, false);
+ command_line::add_arg(desc, arg_p2p_bind_port_ipv6, false);
+ command_line::add_arg(desc, arg_p2p_use_ipv6);
+ command_line::add_arg(desc, arg_p2p_require_ipv4);
command_line::add_arg(desc, arg_p2p_external_port);
command_line::add_arg(desc, arg_p2p_allow_local_ip);
command_line::add_arg(desc, arg_p2p_add_peer);
@@ -341,7 +345,9 @@ namespace nodetool
network_zone& public_zone = m_network_zones[epee::net_utils::zone::public_];
public_zone.m_connect = &public_connect;
public_zone.m_bind_ip = command_line::get_arg(vm, arg_p2p_bind_ip);
+ public_zone.m_bind_ipv6_address = command_line::get_arg(vm, arg_p2p_bind_ipv6_address);
public_zone.m_port = command_line::get_arg(vm, arg_p2p_bind_port);
+ public_zone.m_port_ipv6 = command_line::get_arg(vm, arg_p2p_bind_port_ipv6);
public_zone.m_can_pingback = true;
m_external_port = command_line::get_arg(vm, arg_p2p_external_port);
m_allow_local_ip = command_line::get_arg(vm, arg_p2p_allow_local_ip);
@@ -375,6 +381,8 @@ namespace nodetool
return false;
}
m_offline = command_line::get_arg(vm, cryptonote::arg_offline);
+ m_use_ipv6 = command_line::get_arg(vm, arg_p2p_use_ipv6);
+ m_require_ipv4 = command_line::get_arg(vm, arg_p2p_require_ipv4);
if (command_line::has_arg(vm, arg_p2p_add_peer))
{
@@ -518,12 +526,17 @@ namespace nodetool
std::string host = addr;
std::string port = std::to_string(default_port);
- size_t pos = addr.find_last_of(':');
- if (std::string::npos != pos)
+ size_t colon_pos = addr.find_last_of(':');
+ size_t dot_pos = addr.find_last_of('.');
+ size_t square_brace_pos = addr.find('[');
+
+ // IPv6 will have colons regardless. IPv6 and IPv4 address:port will have a colon but also either a . or a [
+ // as IPv6 addresses specified as address:port are to be specified as "[addr:addr:...:addr]:port"
+ // One may also specify an IPv6 address as simply "[addr:addr:...:addr]" without the port; in that case
+ // the square braces will be stripped here.
+ if ((std::string::npos != colon_pos && std::string::npos != dot_pos) || std::string::npos != square_brace_pos)
{
- CHECK_AND_ASSERT_MES(addr.length() - 1 != pos && 0 != pos, false, "Failed to parse seed address from string: '" << addr << '\'');
- host = addr.substr(0, pos);
- port = addr.substr(pos + 1);
+ net::get_network_address_host_and_port(addr, host, port);
}
MINFO("Resolving node address: host=" << host << ", port=" << port);
@@ -546,7 +559,9 @@ namespace nodetool
}
else
{
- MWARNING("IPv6 unsupported, skip '" << host << "' -> " << endpoint.address().to_v6().to_string(ec));
+ epee::net_utils::network_address na{epee::net_utils::ipv6_network_address{endpoint.address().to_v6(), endpoint.port()}};
+ seed_nodes.push_back(na);
+ MINFO("Added node: " << na.str());
}
}
return true;
@@ -780,21 +795,40 @@ namespace nodetool
if (!zone.second.m_bind_ip.empty())
{
+ std::string ipv6_addr = "";
+ std::string ipv6_port = "";
zone.second.m_net_server.set_connection_filter(this);
- MINFO("Binding on " << zone.second.m_bind_ip << ":" << zone.second.m_port);
- res = zone.second.m_net_server.init_server(zone.second.m_port, zone.second.m_bind_ip, epee::net_utils::ssl_support_t::e_ssl_support_disabled);
+ MINFO("Binding (IPv4) on " << zone.second.m_bind_ip << ":" << zone.second.m_port);
+ if (!zone.second.m_bind_ipv6_address.empty() && m_use_ipv6)
+ {
+ ipv6_addr = zone.second.m_bind_ipv6_address;
+ ipv6_port = zone.second.m_port_ipv6;
+ MINFO("Binding (IPv6) on " << zone.second.m_bind_ipv6_address << ":" << zone.second.m_port_ipv6);
+ }
+ res = zone.second.m_net_server.init_server(zone.second.m_port, zone.second.m_bind_ip, ipv6_port, ipv6_addr, m_use_ipv6, m_require_ipv4, epee::net_utils::ssl_support_t::e_ssl_support_disabled);
CHECK_AND_ASSERT_MES(res, false, "Failed to bind server");
}
}
m_listening_port = public_zone.m_net_server.get_binded_port();
- MLOG_GREEN(el::Level::Info, "Net service bound to " << public_zone.m_bind_ip << ":" << m_listening_port);
+ MLOG_GREEN(el::Level::Info, "Net service bound (IPv4) to " << public_zone.m_bind_ip << ":" << m_listening_port);
+ if (m_use_ipv6)
+ {
+ m_listening_port_ipv6 = public_zone.m_net_server.get_binded_port_ipv6();
+ MLOG_GREEN(el::Level::Info, "Net service bound (IPv6) to " << public_zone.m_bind_ipv6_address << ":" << m_listening_port_ipv6);
+ }
if(m_external_port)
MDEBUG("External port defined as " << m_external_port);
// add UPnP port mapping
if(m_igd == igd)
- add_upnp_port_mapping(m_listening_port);
+ {
+ add_upnp_port_mapping_v4(m_listening_port);
+ if (m_use_ipv6)
+ {
+ add_upnp_port_mapping_v6(m_listening_port_ipv6);
+ }
+ }
return res;
}
@@ -1997,19 +2031,43 @@ namespace nodetool
if(!node_data.my_port)
return false;
- CHECK_AND_ASSERT_MES(context.m_remote_address.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id(), false,
- "Only IPv4 addresses are supported here");
+ bool address_ok = (context.m_remote_address.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id() || context.m_remote_address.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id());
+ CHECK_AND_ASSERT_MES(address_ok, false,
+ "Only IPv4 or IPv6 addresses are supported here");
const epee::net_utils::network_address na = context.m_remote_address;
- uint32_t actual_ip = na.as<const epee::net_utils::ipv4_network_address>().ip();
+ std::string ip;
+ uint32_t ipv4_addr;
+ boost::asio::ip::address_v6 ipv6_addr;
+ bool is_ipv4;
+ if (na.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id())
+ {
+ ipv4_addr = na.as<const epee::net_utils::ipv4_network_address>().ip();
+ ip = epee::string_tools::get_ip_string_from_int32(ipv4_addr);
+ is_ipv4 = true;
+ }
+ else
+ {
+ ipv6_addr = na.as<const epee::net_utils::ipv6_network_address>().ip();
+ ip = ipv6_addr.to_string();
+ is_ipv4 = false;
+ }
network_zone& zone = m_network_zones.at(na.get_zone());
if(!zone.m_peerlist.is_host_allowed(context.m_remote_address))
return false;
- std::string ip = epee::string_tools::get_ip_string_from_int32(actual_ip);
std::string port = epee::string_tools::num_to_string_fast(node_data.my_port);
- epee::net_utils::network_address address{epee::net_utils::ipv4_network_address(actual_ip, node_data.my_port)};
+
+ epee::net_utils::network_address address;
+ if (is_ipv4)
+ {
+ address = epee::net_utils::network_address{epee::net_utils::ipv4_network_address(ipv4_addr, node_data.my_port)};
+ }
+ else
+ {
+ address = epee::net_utils::network_address{epee::net_utils::ipv6_network_address(ipv6_addr, node_data.my_port)};
+ }
peerid_type pr = node_data.peer_id;
bool r = zone.m_net_server.connect_async(ip, port, zone.m_config.m_net_config.ping_connection_timeout, [cb, /*context,*/ address, pr, this](
const typename net_server::t_connection_context& ping_context,
@@ -2193,12 +2251,19 @@ namespace nodetool
//try ping to be sure that we can add this peer to peer_list
try_ping(arg.node_data, context, [peer_id_l, port_l, context, this]()
{
- CHECK_AND_ASSERT_MES(context.m_remote_address.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id(), void(),
- "Only IPv4 addresses are supported here");
+ CHECK_AND_ASSERT_MES((context.m_remote_address.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id() || context.m_remote_address.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id()), void(),
+ "Only IPv4 or IPv6 addresses are supported here");
//called only(!) if success pinged, update local peerlist
peerlist_entry pe;
const epee::net_utils::network_address na = context.m_remote_address;
- pe.adr = epee::net_utils::ipv4_network_address(na.as<epee::net_utils::ipv4_network_address>().ip(), port_l);
+ if (context.m_remote_address.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id())
+ {
+ pe.adr = epee::net_utils::ipv4_network_address(na.as<epee::net_utils::ipv4_network_address>().ip(), port_l);
+ }
+ else
+ {
+ pe.adr = epee::net_utils::ipv6_network_address(na.as<epee::net_utils::ipv6_network_address>().ip(), port_l);
+ }
time_t last_seen;
time(&last_seen);
pe.last_seen = static_cast<int64_t>(last_seen);
@@ -2565,16 +2630,19 @@ namespace nodetool
}
template<class t_payload_net_handler>
- void node_server<t_payload_net_handler>::add_upnp_port_mapping(uint32_t port)
+ void node_server<t_payload_net_handler>::add_upnp_port_mapping_impl(uint32_t port, bool ipv6) // if ipv6 false, do ipv4
{
- MDEBUG("Attempting to add IGD port mapping.");
+ std::string ipversion = ipv6 ? "(IPv6)" : "(IPv4)";
+ MDEBUG("Attempting to add IGD port mapping " << ipversion << ".");
int result;
+ const int ipv6_arg = ipv6 ? 1 : 0;
+
#if MINIUPNPC_API_VERSION > 13
// default according to miniupnpc.h
unsigned char ttl = 2;
- UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, ttl, &result);
+ UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, ipv6_arg, ttl, &result);
#else
- UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, &result);
+ UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, ipv6_arg, &result);
#endif
UPNPUrls urls;
IGDdatas igdData;
@@ -2611,16 +2679,38 @@ namespace nodetool
}
template<class t_payload_net_handler>
- void node_server<t_payload_net_handler>::delete_upnp_port_mapping(uint32_t port)
+ void node_server<t_payload_net_handler>::add_upnp_port_mapping_v4(uint32_t port)
+ {
+ add_upnp_port_mapping_impl(port, false);
+ }
+
+ template<class t_payload_net_handler>
+ void node_server<t_payload_net_handler>::add_upnp_port_mapping_v6(uint32_t port)
+ {
+ add_upnp_port_mapping_impl(port, true);
+ }
+
+ template<class t_payload_net_handler>
+ void node_server<t_payload_net_handler>::add_upnp_port_mapping(uint32_t port, bool ipv4, bool ipv6)
+ {
+ if (ipv4) add_upnp_port_mapping_v4(port);
+ if (ipv6) add_upnp_port_mapping_v6(port);
+ }
+
+
+ template<class t_payload_net_handler>
+ void node_server<t_payload_net_handler>::delete_upnp_port_mapping_impl(uint32_t port, bool ipv6)
{
- MDEBUG("Attempting to delete IGD port mapping.");
+ std::string ipversion = ipv6 ? "(IPv6)" : "(IPv4)";
+ MDEBUG("Attempting to delete IGD port mapping " << ipversion << ".");
int result;
+ const int ipv6_arg = ipv6 ? 1 : 0;
#if MINIUPNPC_API_VERSION > 13
// default according to miniupnpc.h
unsigned char ttl = 2;
- UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, ttl, &result);
+ UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, ipv6_arg, ttl, &result);
#else
- UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, &result);
+ UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, ipv6_arg, &result);
#endif
UPNPUrls urls;
IGDdatas igdData;
@@ -2653,6 +2743,25 @@ namespace nodetool
}
}
+ template<class t_payload_net_handler>
+ void node_server<t_payload_net_handler>::delete_upnp_port_mapping_v4(uint32_t port)
+ {
+ delete_upnp_port_mapping_impl(port, false);
+ }
+
+ template<class t_payload_net_handler>
+ void node_server<t_payload_net_handler>::delete_upnp_port_mapping_v6(uint32_t port)
+ {
+ delete_upnp_port_mapping_impl(port, true);
+ }
+
+ template<class t_payload_net_handler>
+ void node_server<t_payload_net_handler>::delete_upnp_port_mapping(uint32_t port)
+ {
+ delete_upnp_port_mapping_v4(port);
+ delete_upnp_port_mapping_v6(port);
+ }
+
template<typename t_payload_net_handler>
boost::optional<p2p_connection_context_t<typename t_payload_net_handler::connection_context>>
node_server<t_payload_net_handler>::socks_connect(network_zone& zone, const epee::net_utils::network_address& remote, epee::net_utils::ssl_support_t ssl_support)
@@ -2671,13 +2780,34 @@ namespace nodetool
boost::optional<p2p_connection_context_t<typename t_payload_net_handler::connection_context>>
node_server<t_payload_net_handler>::public_connect(network_zone& zone, epee::net_utils::network_address const& na, epee::net_utils::ssl_support_t ssl_support)
{
- CHECK_AND_ASSERT_MES(na.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id(), boost::none,
- "Only IPv4 addresses are supported here");
- const epee::net_utils::ipv4_network_address &ipv4 = na.as<const epee::net_utils::ipv4_network_address>();
+ bool is_ipv4 = na.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id();
+ bool is_ipv6 = na.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id();
+ CHECK_AND_ASSERT_MES(is_ipv4 || is_ipv6, boost::none,
+ "Only IPv4 or IPv6 addresses are supported here");
+
+ std::string address;
+ std::string port;
+
+ if (is_ipv4)
+ {
+ const epee::net_utils::ipv4_network_address &ipv4 = na.as<const epee::net_utils::ipv4_network_address>();
+ address = epee::string_tools::get_ip_string_from_int32(ipv4.ip());
+ port = epee::string_tools::num_to_string_fast(ipv4.port());
+ }
+ else if (is_ipv6)
+ {
+ const epee::net_utils::ipv6_network_address &ipv6 = na.as<const epee::net_utils::ipv6_network_address>();
+ address = ipv6.ip().to_string();
+ port = epee::string_tools::num_to_string_fast(ipv6.port());
+ }
+ else
+ {
+ LOG_ERROR("Only IPv4 or IPv6 addresses are supported here");
+ return boost::none;
+ }
typename net_server::t_connection_context con{};
- const bool res = zone.m_net_server.connect(epee::string_tools::get_ip_string_from_int32(ipv4.ip()),
- epee::string_tools::num_to_string_fast(ipv4.port()),
+ const bool res = zone.m_net_server.connect(address, port,
zone.m_config.m_net_config.connection_timeout,
con, "0.0.0.0", ssl_support);
diff --git a/src/p2p/net_peerlist_boost_serialization.h b/src/p2p/net_peerlist_boost_serialization.h
index 32f30adca..05eb36e65 100644
--- a/src/p2p/net_peerlist_boost_serialization.h
+++ b/src/p2p/net_peerlist_boost_serialization.h
@@ -76,6 +76,9 @@ namespace boost
case epee::net_utils::ipv4_network_address::get_type_id():
do_serialize<epee::net_utils::ipv4_network_address>(is_saving, a, na);
break;
+ case epee::net_utils::ipv6_network_address::get_type_id():
+ do_serialize<epee::net_utils::ipv6_network_address>(is_saving, a, na);
+ break;
case net::tor_address::get_type_id():
do_serialize<net::tor_address>(is_saving, a, na);
break;
@@ -99,6 +102,34 @@ namespace boost
}
template <class Archive, class ver_type>
+ inline void serialize(Archive &a, boost::asio::ip::address_v6& v6, const ver_type ver)
+ {
+ if (typename Archive::is_saving())
+ {
+ auto bytes = v6.to_bytes();
+ for (auto &e: bytes) a & e;
+ }
+ else
+ {
+ boost::asio::ip::address_v6::bytes_type bytes;
+ for (auto &e: bytes) a & e;
+ v6 = boost::asio::ip::address_v6(bytes);
+ }
+ }
+
+ template <class Archive, class ver_type>
+ inline void serialize(Archive &a, epee::net_utils::ipv6_network_address& na, const ver_type ver)
+ {
+ boost::asio::ip::address_v6 ip{na.ip()};
+ uint16_t port{na.port()};
+ a & ip;
+ a & port;
+ if (!typename Archive::is_saving())
+ na = epee::net_utils::ipv6_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());
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index d80219f1c..dd7201623 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -161,7 +161,9 @@ namespace cryptonote
auto rng = [](size_t len, uint8_t *ptr){ return crypto::rand(len, ptr); };
return epee::http_server_impl_base<core_rpc_server, connection_context>::init(
- rng, std::move(port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login), std::move(rpc_config->ssl_options)
+ rng, std::move(port), std::move(rpc_config->bind_ip),
+ std::move(rpc_config->bind_ipv6_address), std::move(rpc_config->use_ipv6), std::move(rpc_config->require_ipv4),
+ std::move(rpc_config->access_control_origins), std::move(http_login), std::move(rpc_config->ssl_options)
);
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -1026,6 +1028,11 @@ namespace cryptonote
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, entry.rpc_port);
+ else if (entry.adr.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id())
+ {
+ res.white_list.emplace_back(entry.id, entry.adr.as<epee::net_utils::ipv6_network_address>().host_str(),
+ entry.adr.as<epee::net_utils::ipv6_network_address>().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
+ }
else
res.white_list.emplace_back(entry.id, entry.adr.str(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
}
@@ -1036,6 +1043,9 @@ namespace cryptonote
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, entry.rpc_port);
+ else if (entry.adr.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id())
+ res.white_list.emplace_back(entry.id, entry.adr.as<epee::net_utils::ipv6_network_address>().host_str(),
+ entry.adr.as<epee::net_utils::ipv6_network_address>().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
else
res.gray_list.emplace_back(entry.id, entry.adr.str(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
}
diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h
index 571a71207..7f4556c97 100644
--- a/src/rpc/core_rpc_server_commands_defs.h
+++ b/src/rpc/core_rpc_server_commands_defs.h
@@ -1200,6 +1200,9 @@ namespace cryptonote
peer(uint64_t id, const std::string &host, uint64_t last_seen, uint32_t pruning_seed, uint16_t rpc_port)
: id(id), host(host), ip(0), port(0), rpc_port(rpc_port), last_seen(last_seen), pruning_seed(pruning_seed)
{}
+ peer(uint64_t id, const std::string &host, uint16_t port, uint64_t last_seen, uint32_t pruning_seed, uint16_t rpc_port)
+ : id(id), host(host), ip(0), port(port), rpc_port(rpc_port), last_seen(last_seen), pruning_seed(pruning_seed)
+ {}
peer(uint64_t id, uint32_t ip, uint16_t port, uint64_t last_seen, uint32_t pruning_seed, uint16_t rpc_port)
: id(id), host(epee::string_tools::get_ip_string_from_int32(ip)), ip(ip), port(port), rpc_port(rpc_port), last_seen(last_seen), pruning_seed(pruning_seed)
{}
diff --git a/src/rpc/rpc_args.cpp b/src/rpc/rpc_args.cpp
index 4479bd1f1..68b33cb8c 100644
--- a/src/rpc/rpc_args.cpp
+++ b/src/rpc/rpc_args.cpp
@@ -90,6 +90,9 @@ namespace cryptonote
rpc_args::descriptors::descriptors()
: rpc_bind_ip({"rpc-bind-ip", rpc_args::tr("Specify IP to bind RPC server"), "127.0.0.1"})
+ , rpc_bind_ipv6_address({"rpc-bind-ipv6-address", rpc_args::tr("Specify IPv6 address to bind RPC server"), "::1"})
+ , rpc_use_ipv6({"rpc-use-ipv6", rpc_args::tr("Allow IPv6 for RPC"), false})
+ , rpc_require_ipv4({"rpc-require-ipv4", rpc_args::tr("Require successful IPv4 bind for RPC"), true})
, rpc_login({"rpc-login", rpc_args::tr("Specify username[:password] required for RPC server"), "", true})
, confirm_external_bind({"confirm-external-bind", rpc_args::tr("Confirm rpc-bind-ip value is NOT a loopback (local) IP")})
, rpc_access_control_origins({"rpc-access-control-origins", rpc_args::tr("Specify a comma separated list of origins to allow cross origin resource sharing"), ""})
@@ -108,6 +111,9 @@ namespace cryptonote
{
const descriptors arg{};
command_line::add_arg(desc, arg.rpc_bind_ip);
+ command_line::add_arg(desc, arg.rpc_bind_ipv6_address);
+ command_line::add_arg(desc, arg.rpc_use_ipv6);
+ command_line::add_arg(desc, arg.rpc_require_ipv4);
command_line::add_arg(desc, arg.rpc_login);
command_line::add_arg(desc, arg.confirm_external_bind);
command_line::add_arg(desc, arg.rpc_access_control_origins);
@@ -127,6 +133,9 @@ namespace cryptonote
rpc_args config{};
config.bind_ip = command_line::get_arg(vm, arg.rpc_bind_ip);
+ config.bind_ipv6_address = command_line::get_arg(vm, arg.rpc_bind_ipv6_address);
+ config.use_ipv6 = command_line::get_arg(vm, arg.rpc_use_ipv6);
+ config.require_ipv4 = command_line::get_arg(vm, arg.rpc_require_ipv4);
if (!config.bind_ip.empty())
{
// always parse IP here for error consistency
@@ -148,6 +157,34 @@ namespace cryptonote
return boost::none;
}
}
+ if (!config.bind_ipv6_address.empty())
+ {
+ // allow square braces, but remove them here if present
+ if (config.bind_ipv6_address.find('[') != std::string::npos)
+ {
+ config.bind_ipv6_address = config.bind_ipv6_address.substr(1, config.bind_ipv6_address.size() - 2);
+ }
+
+
+ // always parse IP here for error consistency
+ boost::system::error_code ec{};
+ const auto parsed_ip = boost::asio::ip::address::from_string(config.bind_ipv6_address, ec);
+ if (ec)
+ {
+ LOG_ERROR(tr("Invalid IP address given for --") << arg.rpc_bind_ipv6_address.name);
+ return boost::none;
+ }
+
+ if (!parsed_ip.is_loopback() && !command_line::get_arg(vm, arg.confirm_external_bind))
+ {
+ LOG_ERROR(
+ "--" << arg.rpc_bind_ipv6_address.name <<
+ tr(" permits inbound unencrypted external connections. Consider SSH tunnel or SSL proxy instead. Override with --") <<
+ arg.confirm_external_bind.name
+ );
+ return boost::none;
+ }
+ }
const char *env_rpc_login = nullptr;
const bool has_rpc_arg = command_line::has_arg(vm, arg.rpc_login);
diff --git a/src/rpc/rpc_args.h b/src/rpc/rpc_args.h
index 619f02b42..cd154a4d0 100644
--- a/src/rpc/rpc_args.h
+++ b/src/rpc/rpc_args.h
@@ -52,6 +52,9 @@ namespace cryptonote
descriptors& operator=(descriptors&&) = delete;
const command_line::arg_descriptor<std::string> rpc_bind_ip;
+ const command_line::arg_descriptor<std::string> rpc_bind_ipv6_address;
+ const command_line::arg_descriptor<bool> rpc_use_ipv6;
+ const command_line::arg_descriptor<bool> rpc_require_ipv4;
const command_line::arg_descriptor<std::string> rpc_login;
const command_line::arg_descriptor<bool> confirm_external_bind;
const command_line::arg_descriptor<std::string> rpc_access_control_origins;
@@ -76,6 +79,9 @@ namespace cryptonote
static boost::optional<epee::net_utils::ssl_options_t> process_ssl(const boost::program_options::variables_map& vm, const bool any_cert_option = false);
std::string bind_ip;
+ std::string bind_ipv6_address;
+ bool use_ipv6;
+ bool require_ipv4;
std::vector<std::string> access_control_origins;
boost::optional<tools::login> login; // currently `boost::none` if unspecified by user
epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_enabled;
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 9d3605d11..b38c2f7c8 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -247,7 +247,9 @@ namespace tools
m_net_server.set_threads_prefix("RPC");
auto rng = [](size_t len, uint8_t *ptr) { return crypto::rand(len, ptr); };
return epee::http_server_impl_base<wallet_rpc_server, connection_context>::init(
- rng, std::move(bind_port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login),
+ rng, std::move(bind_port), std::move(rpc_config->bind_ip),
+ std::move(rpc_config->bind_ipv6_address), std::move(rpc_config->use_ipv6), std::move(rpc_config->require_ipv4),
+ std::move(rpc_config->access_control_origins), std::move(http_login),
std::move(rpc_config->ssl_options)
);
}