aboutsummaryrefslogtreecommitdiff
path: root/contrib/epee
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 /contrib/epee
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 'contrib/epee')
-rw-r--r--contrib/epee/include/net/abstract_tcp_server2.h20
-rw-r--r--contrib/epee/include/net/abstract_tcp_server2.inl323
-rw-r--r--contrib/epee/include/net/http_server_impl_base.h9
-rw-r--r--contrib/epee/include/net/local_ip.h28
-rw-r--r--contrib/epee/include/net/net_parse_helpers.h44
-rw-r--r--contrib/epee/include/net/net_utils_base.h56
-rw-r--r--contrib/epee/src/net_helper.cpp35
-rw-r--r--contrib/epee/src/net_utils_base.cpp13
8 files changed, 461 insertions, 67 deletions
diff --git a/contrib/epee/include/net/abstract_tcp_server2.h b/contrib/epee/include/net/abstract_tcp_server2.h
index c1aa0fe5f..b38ab5399 100644
--- a/contrib/epee/include/net/abstract_tcp_server2.h
+++ b/contrib/epee/include/net/abstract_tcp_server2.h
@@ -227,8 +227,12 @@ namespace net_utils
std::map<std::string, t_connection_type> server_type_map;
void create_server_type_map();
- bool init_server(uint32_t port, const std::string address = "0.0.0.0", ssl_options_t ssl_options = ssl_support_t::e_ssl_support_autodetect);
- bool init_server(const std::string port, const std::string& address = "0.0.0.0", ssl_options_t ssl_options = ssl_support_t::e_ssl_support_autodetect);
+ bool init_server(uint32_t port, const std::string& address = "0.0.0.0",
+ uint32_t port_ipv6 = 0, const std::string& address_ipv6 = "::", bool use_ipv6 = false, bool require_ipv4 = true,
+ ssl_options_t ssl_options = ssl_support_t::e_ssl_support_autodetect);
+ bool init_server(const std::string port, const std::string& address = "0.0.0.0",
+ const std::string port_ipv6 = "", const std::string address_ipv6 = "::", bool use_ipv6 = false, bool require_ipv4 = true,
+ ssl_options_t ssl_options = ssl_support_t::e_ssl_support_autodetect);
/// Run the server's io_service loop.
bool run_server(size_t threads_count, bool wait = true, const boost::thread::attributes& attrs = boost::thread::attributes());
@@ -269,6 +273,7 @@ namespace net_utils
}
int get_binded_port(){return m_port;}
+ int get_binded_port_ipv6(){return m_port_ipv6;}
long get_connections_count() const
{
@@ -339,7 +344,9 @@ namespace net_utils
/// Run the server's io_service loop.
bool worker_thread();
/// Handle completion of an asynchronous accept operation.
- void handle_accept(const boost::system::error_code& e);
+ void handle_accept_ipv4(const boost::system::error_code& e);
+ void handle_accept_ipv6(const boost::system::error_code& e);
+ void handle_accept(const boost::system::error_code& e, bool ipv6 = false);
bool is_thread_worker();
@@ -360,11 +367,16 @@ namespace net_utils
/// Acceptor used to listen for incoming connections.
boost::asio::ip::tcp::acceptor acceptor_;
+ boost::asio::ip::tcp::acceptor acceptor_ipv6;
epee::net_utils::network_address default_remote;
std::atomic<bool> m_stop_signal_sent;
uint32_t m_port;
+ uint32_t m_port_ipv6;
std::string m_address;
+ std::string m_address_ipv6;
+ bool m_use_ipv6;
+ bool m_require_ipv4;
std::string m_thread_name_prefix; //TODO: change to enum server_type, now used
size_t m_threads_count;
std::vector<boost::shared_ptr<boost::thread> > m_threads;
@@ -376,6 +388,8 @@ namespace net_utils
/// The next connection to be accepted
connection_ptr new_connection_;
+ connection_ptr new_connection_ipv6;
+
boost::mutex connections_mutex;
std::set<connection_ptr> connections_;
diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl
index 0721366aa..19e9c9af9 100644
--- a/contrib/epee/include/net/abstract_tcp_server2.inl
+++ b/contrib/epee/include/net/abstract_tcp_server2.inl
@@ -145,10 +145,18 @@ PRAGMA_WARNING_DISABLE_VS(4355)
boost::system::error_code ec;
auto remote_ep = socket().remote_endpoint(ec);
CHECK_AND_NO_ASSERT_MES(!ec, false, "Failed to get remote endpoint: " << ec.message() << ':' << ec.value());
- CHECK_AND_NO_ASSERT_MES(remote_ep.address().is_v4(), false, "IPv6 not supported here");
+ CHECK_AND_NO_ASSERT_MES(remote_ep.address().is_v4() || remote_ep.address().is_v6(), false, "only IPv4 and IPv6 supported here");
- const unsigned long ip_{boost::asio::detail::socket_ops::host_to_network_long(remote_ep.address().to_v4().to_ulong())};
- return start(is_income, is_multithreaded, ipv4_network_address{uint32_t(ip_), remote_ep.port()});
+ if (remote_ep.address().is_v4())
+ {
+ const unsigned long ip_ = boost::asio::detail::socket_ops::host_to_network_long(remote_ep.address().to_v4().to_ulong());
+ return start(is_income, is_multithreaded, ipv4_network_address{uint32_t(ip_), remote_ep.port()});
+ }
+ else
+ {
+ const auto ip_{remote_ep.address().to_v6()};
+ return start(is_income, is_multithreaded, ipv6_network_address{ip_, remote_ep.port()});
+ }
CATCH_ENTRY_L0("connection<t_protocol_handler>::start()", false);
}
//---------------------------------------------------------------------------------
@@ -904,12 +912,14 @@ PRAGMA_WARNING_DISABLE_VS(4355)
m_io_service_local_instance(new worker()),
io_service_(m_io_service_local_instance->io_service),
acceptor_(io_service_),
+ acceptor_ipv6(io_service_),
default_remote(),
m_stop_signal_sent(false), m_port(0),
m_threads_count(0),
m_thread_index(0),
m_connection_type( connection_type ),
- new_connection_()
+ new_connection_(),
+ new_connection_ipv6()
{
create_server_type_map();
m_thread_name_prefix = "NET";
@@ -920,12 +930,14 @@ PRAGMA_WARNING_DISABLE_VS(4355)
m_state(boost::make_shared<typename connection<t_protocol_handler>::shared_state>()),
io_service_(extarnal_io_service),
acceptor_(io_service_),
+ acceptor_ipv6(io_service_),
default_remote(),
m_stop_signal_sent(false), m_port(0),
m_threads_count(0),
m_thread_index(0),
m_connection_type(connection_type),
- new_connection_()
+ new_connection_(),
+ new_connection_ipv6()
{
create_server_type_map();
m_thread_name_prefix = "NET";
@@ -947,29 +959,92 @@ PRAGMA_WARNING_DISABLE_VS(4355)
}
//---------------------------------------------------------------------------------
template<class t_protocol_handler>
- bool boosted_tcp_server<t_protocol_handler>::init_server(uint32_t port, const std::string address, ssl_options_t ssl_options)
+ bool boosted_tcp_server<t_protocol_handler>::init_server(uint32_t port, const std::string& address,
+ uint32_t port_ipv6, const std::string& address_ipv6, bool use_ipv6, bool require_ipv4,
+ ssl_options_t ssl_options)
{
TRY_ENTRY();
m_stop_signal_sent = false;
m_port = port;
+ m_port_ipv6 = port_ipv6;
m_address = address;
+ m_address_ipv6 = address_ipv6;
+ m_use_ipv6 = use_ipv6;
+ m_require_ipv4 = require_ipv4;
+
if (ssl_options)
m_state->configure_ssl(std::move(ssl_options));
- // Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR).
- boost::asio::ip::tcp::resolver resolver(io_service_);
- boost::asio::ip::tcp::resolver::query query(address, boost::lexical_cast<std::string>(port), boost::asio::ip::tcp::resolver::query::canonical_name);
- boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
- acceptor_.open(endpoint.protocol());
- acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
- acceptor_.bind(endpoint);
- acceptor_.listen();
- boost::asio::ip::tcp::endpoint binded_endpoint = acceptor_.local_endpoint();
- m_port = binded_endpoint.port();
- MDEBUG("start accept");
- new_connection_.reset(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, m_state->ssl_options().support));
- acceptor_.async_accept(new_connection_->socket(),
- boost::bind(&boosted_tcp_server<t_protocol_handler>::handle_accept, this,
- boost::asio::placeholders::error));
+
+ std::string ipv4_failed = "";
+ std::string ipv6_failed = "";
+ try
+ {
+ boost::asio::ip::tcp::resolver resolver(io_service_);
+ boost::asio::ip::tcp::resolver::query query(address, boost::lexical_cast<std::string>(port), boost::asio::ip::tcp::resolver::query::canonical_name);
+ boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
+ acceptor_.open(endpoint.protocol());
+ // Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR).
+ acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
+ acceptor_.bind(endpoint);
+ acceptor_.listen();
+ boost::asio::ip::tcp::endpoint binded_endpoint = acceptor_.local_endpoint();
+ m_port = binded_endpoint.port();
+ MDEBUG("start accept (IPv4)");
+ new_connection_.reset(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, m_state->ssl_options().support));
+ acceptor_.async_accept(new_connection_->socket(),
+ boost::bind(&boosted_tcp_server<t_protocol_handler>::handle_accept_ipv4, this,
+ boost::asio::placeholders::error));
+ }
+ catch (const std::exception &e)
+ {
+ ipv4_failed = e.what();
+ }
+
+ if (ipv4_failed != "")
+ {
+ MERROR("Failed to bind IPv4: " << ipv4_failed);
+ if (require_ipv4)
+ {
+ throw std::runtime_error("Failed to bind IPv4 (set to required)");
+ }
+ }
+
+ if (use_ipv6)
+ {
+ try
+ {
+ if (port_ipv6 == 0) port_ipv6 = port; // default arg means bind to same port as ipv4
+ boost::asio::ip::tcp::resolver resolver(io_service_);
+ boost::asio::ip::tcp::resolver::query query(address_ipv6, boost::lexical_cast<std::string>(port_ipv6), boost::asio::ip::tcp::resolver::query::canonical_name);
+ boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
+ acceptor_ipv6.open(endpoint.protocol());
+ // Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR).
+ acceptor_ipv6.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
+ acceptor_ipv6.set_option(boost::asio::ip::v6_only(true));
+ acceptor_ipv6.bind(endpoint);
+ acceptor_ipv6.listen();
+ boost::asio::ip::tcp::endpoint binded_endpoint = acceptor_ipv6.local_endpoint();
+ m_port_ipv6 = binded_endpoint.port();
+ MDEBUG("start accept (IPv6)");
+ new_connection_ipv6.reset(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, m_state->ssl_options().support));
+ acceptor_ipv6.async_accept(new_connection_ipv6->socket(),
+ boost::bind(&boosted_tcp_server<t_protocol_handler>::handle_accept_ipv6, this,
+ boost::asio::placeholders::error));
+ }
+ catch (const std::exception &e)
+ {
+ ipv6_failed = e.what();
+ }
+ }
+
+ if (use_ipv6 && ipv6_failed != "")
+ {
+ MERROR("Failed to bind IPv6: " << ipv6_failed);
+ if (ipv4_failed != "")
+ {
+ throw std::runtime_error("Failed to bind IPv4 and IPv6");
+ }
+ }
return true;
}
@@ -988,15 +1063,23 @@ PRAGMA_WARNING_DISABLE_VS(4355)
PUSH_WARNINGS
DISABLE_GCC_WARNING(maybe-uninitialized)
template<class t_protocol_handler>
- bool boosted_tcp_server<t_protocol_handler>::init_server(const std::string port, const std::string& address, ssl_options_t ssl_options)
+ bool boosted_tcp_server<t_protocol_handler>::init_server(const std::string port, const std::string& address,
+ const std::string port_ipv6, const std::string address_ipv6, bool use_ipv6, bool require_ipv4,
+ ssl_options_t ssl_options)
{
uint32_t p = 0;
+ uint32_t p_ipv6 = 0;
if (port.size() && !string_tools::get_xtype_from_string(p, port)) {
MERROR("Failed to convert port no = " << port);
return false;
}
- return this->init_server(p, address, std::move(ssl_options));
+
+ if (port_ipv6.size() && !string_tools::get_xtype_from_string(p_ipv6, port_ipv6)) {
+ MERROR("Failed to convert port no = " << port_ipv6);
+ return false;
+ }
+ return this->init_server(p, address, p_ipv6, address_ipv6, use_ipv6, require_ipv4, std::move(ssl_options));
}
POP_WARNINGS
//---------------------------------------------------------------------------------
@@ -1088,7 +1171,7 @@ POP_WARNINGS
{
//some problems with the listening socket ?..
_dbg1("Net service stopped without stop request, restarting...");
- if(!this->init_server(m_port, m_address))
+ if(!this->init_server(m_port, m_address, m_port_ipv6, m_address_ipv6, m_use_ipv6, m_require_ipv4))
{
_dbg1("Reiniting service failed, exit.");
return false;
@@ -1154,29 +1237,52 @@ POP_WARNINGS
}
//---------------------------------------------------------------------------------
template<class t_protocol_handler>
- void boosted_tcp_server<t_protocol_handler>::handle_accept(const boost::system::error_code& e)
+ void boosted_tcp_server<t_protocol_handler>::handle_accept_ipv4(const boost::system::error_code& e)
+ {
+ this->handle_accept(e, false);
+ }
+ //---------------------------------------------------------------------------------
+ template<class t_protocol_handler>
+ void boosted_tcp_server<t_protocol_handler>::handle_accept_ipv6(const boost::system::error_code& e)
+ {
+ this->handle_accept(e, true);
+ }
+ //---------------------------------------------------------------------------------
+ template<class t_protocol_handler>
+ void boosted_tcp_server<t_protocol_handler>::handle_accept(const boost::system::error_code& e, bool ipv6)
{
MDEBUG("handle_accept");
+
+ boost::asio::ip::tcp::acceptor* current_acceptor = &acceptor_;
+ connection_ptr* current_new_connection = &new_connection_;
+ auto accept_function_pointer = &boosted_tcp_server<t_protocol_handler>::handle_accept_ipv4;
+ if (ipv6)
+ {
+ current_acceptor = &acceptor_ipv6;
+ current_new_connection = &new_connection_ipv6;
+ accept_function_pointer = &boosted_tcp_server<t_protocol_handler>::handle_accept_ipv6;
+ }
+
try
{
if (!e)
{
- if (m_connection_type == e_connection_type_RPC) {
- const char *ssl_message = "unknown";
- switch (new_connection_->get_ssl_support())
- {
- case epee::net_utils::ssl_support_t::e_ssl_support_disabled: ssl_message = "disabled"; break;
- case epee::net_utils::ssl_support_t::e_ssl_support_enabled: ssl_message = "enabled"; break;
- case epee::net_utils::ssl_support_t::e_ssl_support_autodetect: ssl_message = "autodetection"; break;
- }
- MDEBUG("New server for RPC connections, SSL " << ssl_message);
- new_connection_->setRpcStation(); // hopefully this is not needed actually
- }
- connection_ptr conn(std::move(new_connection_));
- new_connection_.reset(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, conn->get_ssl_support()));
- acceptor_.async_accept(new_connection_->socket(),
- boost::bind(&boosted_tcp_server<t_protocol_handler>::handle_accept, this,
- boost::asio::placeholders::error));
+ if (m_connection_type == e_connection_type_RPC) {
+ const char *ssl_message = "unknown";
+ switch ((*current_new_connection)->get_ssl_support())
+ {
+ case epee::net_utils::ssl_support_t::e_ssl_support_disabled: ssl_message = "disabled"; break;
+ case epee::net_utils::ssl_support_t::e_ssl_support_enabled: ssl_message = "enabled"; break;
+ case epee::net_utils::ssl_support_t::e_ssl_support_autodetect: ssl_message = "autodetection"; break;
+ }
+ MDEBUG("New server for RPC connections, SSL " << ssl_message);
+ (*current_new_connection)->setRpcStation(); // hopefully this is not needed actually
+ }
+ connection_ptr conn(std::move((*current_new_connection)));
+ (*current_new_connection).reset(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, conn->get_ssl_support()));
+ current_acceptor->async_accept((*current_new_connection)->socket(),
+ boost::bind(accept_function_pointer, this,
+ boost::asio::placeholders::error));
boost::asio::socket_base::keep_alive opt(true);
conn->socket().set_option(opt);
@@ -1208,10 +1314,10 @@ POP_WARNINGS
assert(m_state != nullptr); // always set in constructor
_erro("Some problems at accept: " << e.message() << ", connections_count = " << m_state->sock_count);
misc_utils::sleep_no_w(100);
- new_connection_.reset(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, new_connection_->get_ssl_support()));
- acceptor_.async_accept(new_connection_->socket(),
- boost::bind(&boosted_tcp_server<t_protocol_handler>::handle_accept, this,
- boost::asio::placeholders::error));
+ (*current_new_connection).reset(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, (*current_new_connection)->get_ssl_support()));
+ current_acceptor->async_accept((*current_new_connection)->socket(),
+ boost::bind(accept_function_pointer, this,
+ boost::asio::placeholders::error));
}
//---------------------------------------------------------------------------------
template<class t_protocol_handler>
@@ -1345,23 +1451,84 @@ POP_WARNINGS
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ CRITICAL_REGION_LOCAL(connections_mutex); connections_.erase(new_connection_l); });
boost::asio::ip::tcp::socket& sock_ = new_connection_l->socket();
- //////////////////////////////////////////////////////////////////////////
+ bool try_ipv6 = false;
+
boost::asio::ip::tcp::resolver resolver(io_service_);
boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), adr, port, boost::asio::ip::tcp::resolver::query::canonical_name);
- boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
+ boost::system::error_code resolve_error;
+ boost::asio::ip::tcp::resolver::iterator iterator;
+ try
+ {
+ //resolving ipv4 address as ipv6 throws, catch here and move on
+ iterator = resolver.resolve(query, resolve_error);
+ }
+ catch (const boost::system::system_error& e)
+ {
+ if (!m_use_ipv6 || (resolve_error != boost::asio::error::host_not_found &&
+ resolve_error != boost::asio::error::host_not_found_try_again))
+ {
+ throw;
+ }
+ try_ipv6 = true;
+ }
+ catch (...)
+ {
+ throw;
+ }
+
+ std::string bind_ip_to_use;
+
boost::asio::ip::tcp::resolver::iterator end;
if(iterator == end)
{
- _erro("Failed to resolve " << adr);
- return false;
+ if (!m_use_ipv6)
+ {
+ _erro("Failed to resolve " << adr);
+ return false;
+ }
+ else
+ {
+ try_ipv6 = true;
+ MINFO("Resolving address as IPv4 failed, trying IPv6");
+ }
+ }
+ else
+ {
+ bind_ip_to_use = bind_ip;
}
- //////////////////////////////////////////////////////////////////////////
+ if (try_ipv6)
+ {
+ boost::asio::ip::tcp::resolver::query query6(boost::asio::ip::tcp::v6(), adr, port, boost::asio::ip::tcp::resolver::query::canonical_name);
+
+ iterator = resolver.resolve(query6, resolve_error);
+
+ if(iterator == end)
+ {
+ _erro("Failed to resolve " << adr);
+ return false;
+ }
+ else
+ {
+ if (bind_ip == "0.0.0.0")
+ {
+ bind_ip_to_use = "::";
+ }
+ else
+ {
+ bind_ip_to_use = "";
+ }
+
+ }
+
+ }
+
+ LOG_ERROR("Trying connect to " << adr << ":" << port << ", bind_ip = " << bind_ip_to_use);
//boost::asio::ip::tcp::endpoint remote_endpoint(boost::asio::ip::address::from_string(addr.c_str()), port);
boost::asio::ip::tcp::endpoint remote_endpoint(*iterator);
- auto try_connect_result = try_connect(new_connection_l, adr, port, sock_, remote_endpoint, bind_ip, conn_timeout, ssl_support);
+ auto try_connect_result = try_connect(new_connection_l, adr, port, sock_, remote_endpoint, bind_ip_to_use, conn_timeout, ssl_support);
if (try_connect_result == CONNECT_FAILURE)
return false;
if (ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect && try_connect_result == CONNECT_NO_SSL)
@@ -1369,7 +1536,7 @@ POP_WARNINGS
// we connected, but could not connect with SSL, try without
MERROR("SSL handshake failed on an autodetect connection, reconnecting without SSL");
new_connection_l->disable_ssl();
- try_connect_result = try_connect(new_connection_l, adr, port, sock_, remote_endpoint, bind_ip, conn_timeout, epee::net_utils::ssl_support_t::e_ssl_support_disabled);
+ try_connect_result = try_connect(new_connection_l, adr, port, sock_, remote_endpoint, bind_ip_to_use, conn_timeout, epee::net_utils::ssl_support_t::e_ssl_support_disabled);
if (try_connect_result != CONNECT_SUCCESS)
return false;
}
@@ -1409,17 +1576,59 @@ POP_WARNINGS
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ CRITICAL_REGION_LOCAL(connections_mutex); connections_.erase(new_connection_l); });
boost::asio::ip::tcp::socket& sock_ = new_connection_l->socket();
- //////////////////////////////////////////////////////////////////////////
+ bool try_ipv6 = false;
+
boost::asio::ip::tcp::resolver resolver(io_service_);
boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), adr, port, boost::asio::ip::tcp::resolver::query::canonical_name);
- boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
+ boost::system::error_code resolve_error;
+ boost::asio::ip::tcp::resolver::iterator iterator;
+ try
+ {
+ //resolving ipv4 address as ipv6 throws, catch here and move on
+ iterator = resolver.resolve(query, resolve_error);
+ }
+ catch (const boost::system::system_error& e)
+ {
+ if (!m_use_ipv6 || (resolve_error != boost::asio::error::host_not_found &&
+ resolve_error != boost::asio::error::host_not_found_try_again))
+ {
+ throw;
+ }
+ try_ipv6 = true;
+ }
+ catch (...)
+ {
+ throw;
+ }
+
boost::asio::ip::tcp::resolver::iterator end;
if(iterator == end)
{
- _erro("Failed to resolve " << adr);
- return false;
+ if (!try_ipv6)
+ {
+ _erro("Failed to resolve " << adr);
+ return false;
+ }
+ else
+ {
+ MINFO("Resolving address as IPv4 failed, trying IPv6");
+ }
}
- //////////////////////////////////////////////////////////////////////////
+
+ if (try_ipv6)
+ {
+ boost::asio::ip::tcp::resolver::query query6(boost::asio::ip::tcp::v6(), adr, port, boost::asio::ip::tcp::resolver::query::canonical_name);
+
+ iterator = resolver.resolve(query6, resolve_error);
+
+ if(iterator == end)
+ {
+ _erro("Failed to resolve " << adr);
+ return false;
+ }
+ }
+
+
boost::asio::ip::tcp::endpoint remote_endpoint(*iterator);
sock_.open(remote_endpoint.protocol());
diff --git a/contrib/epee/include/net/http_server_impl_base.h b/contrib/epee/include/net/http_server_impl_base.h
index fc2dcbf67..6cd19f17b 100644
--- a/contrib/epee/include/net/http_server_impl_base.h
+++ b/contrib/epee/include/net/http_server_impl_base.h
@@ -57,6 +57,7 @@ namespace epee
{}
bool init(std::function<void(size_t, uint8_t*)> rng, const std::string& bind_port = "0", const std::string& bind_ip = "0.0.0.0",
+ const std::string& bind_ipv6_address = "::", bool use_ipv6 = false, bool require_ipv4 = true,
std::vector<std::string> access_control_origins = std::vector<std::string>(),
boost::optional<net_utils::http::login> user = boost::none,
net_utils::ssl_options_t ssl_options = net_utils::ssl_support_t::e_ssl_support_autodetect)
@@ -75,8 +76,12 @@ namespace epee
m_net_server.get_config_object().m_user = std::move(user);
- MGINFO("Binding on " << bind_ip << ":" << bind_port);
- bool res = m_net_server.init_server(bind_port, bind_ip, std::move(ssl_options));
+ MGINFO("Binding on " << bind_ip << " (IPv4):" << bind_port);
+ if (use_ipv6)
+ {
+ MGINFO("Binding on " << bind_ipv6_address << " (IPv6):" << bind_port);
+ }
+ bool res = m_net_server.init_server(bind_port, bind_ip, bind_port, bind_ipv6_address, use_ipv6, require_ipv4, std::move(ssl_options));
if(!res)
{
LOG_ERROR("Failed to bind server");
diff --git a/contrib/epee/include/net/local_ip.h b/contrib/epee/include/net/local_ip.h
index 52c5855b9..7523f9d81 100644
--- a/contrib/epee/include/net/local_ip.h
+++ b/contrib/epee/include/net/local_ip.h
@@ -27,10 +27,38 @@
#pragma once
+#include <string>
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/asio/ip/address_v6.hpp>
+
namespace epee
{
namespace net_utils
{
+
+ inline
+ bool is_ipv6_local(const std::string& ip)
+ {
+ auto addr = boost::asio::ip::make_address_v6(ip);
+
+ // ipv6 link-local unicast addresses are fe80::/10
+ bool is_link_local = addr.is_link_local();
+
+ auto addr_bytes = addr.to_bytes();
+
+ // ipv6 unique local unicast addresses start with fc00::/7 -- (fcXX or fdXX)
+ bool is_unique_local_unicast = (addr_bytes[0] == 0xfc || addr_bytes[0] == 0xfd);
+
+ return is_link_local || is_unique_local_unicast;
+ }
+
+ inline
+ bool is_ipv6_loopback(const std::string& ip)
+ {
+ // ipv6 loopback is ::1
+ return boost::asio::ip::address_v6::from_string(ip).is_loopback();
+ }
+
inline
bool is_ip_local(uint32_t ip)
{
diff --git a/contrib/epee/include/net/net_parse_helpers.h b/contrib/epee/include/net/net_parse_helpers.h
index 708cce0ff..1d156d19c 100644
--- a/contrib/epee/include/net/net_parse_helpers.h
+++ b/contrib/epee/include/net/net_parse_helpers.h
@@ -94,7 +94,7 @@ namespace net_utils
return true;
}
- inline
+ inline
bool parse_uri(const std::string uri, http::uri_content& content)
{
@@ -128,11 +128,51 @@ namespace net_utils
return true;
}
+ inline
+ bool parse_url_ipv6(const std::string url_str, http::url_content& content)
+ {
+ STATIC_REGEXP_EXPR_1(rexp_match_uri, "^((.*?)://)?(\\[(.*)\\](:(\\d+))?)(.*)?", boost::regex::icase | boost::regex::normal);
+ // 12 3 4 5 6 7
- inline
+ content.port = 0;
+ boost::smatch result;
+ if(!(boost::regex_search(url_str, result, rexp_match_uri, boost::match_default) && result[0].matched))
+ {
+ LOG_PRINT_L1("[PARSE URI] regex not matched for uri: " << rexp_match_uri);
+ //content.m_path = uri;
+ return false;
+ }
+ if(result[2].matched)
+ {
+ content.schema = result[2];
+ }
+ if(result[4].matched)
+ {
+ content.host = result[4];
+ }
+ else // if host not matched, matching should be considered failed
+ {
+ return false;
+ }
+ if(result[6].matched)
+ {
+ content.port = boost::lexical_cast<uint64_t>(result[6]);
+ }
+ if(result[7].matched)
+ {
+ content.uri = result[7];
+ return parse_uri(result[7], content.m_uri_content);
+ }
+
+ return true;
+ }
+
+ inline
bool parse_url(const std::string url_str, http::url_content& content)
{
+ if (parse_url_ipv6(url_str, content)) return true;
+
///iframe_test.html?api_url=http://api.vk.com/api.php&api_id=3289090&api_settings=1&viewer_id=562964060&viewer_type=0&sid=0aad8d1c5713130f9ca0076f2b7b47e532877424961367d81e7fa92455f069be7e21bc3193cbd0be11895&secret=368ebbc0ef&access_token=668bc03f43981d883f73876ffff4aa8564254b359cc745dfa1b3cde7bdab2e94105d8f6d8250717569c0a7&user_id=0&group_id=0&is_app_user=1&auth_key=d2f7a895ca5ff3fdb2a2a8ae23fe679a&language=0&parent_language=0&ad_info=ElsdCQBaQlxiAQRdFUVUXiN2AVBzBx5pU1BXIgZUJlIEAWcgAUoLQg==&referrer=unknown&lc_name=9834b6a3&hash=
//STATIC_REGEXP_EXPR_1(rexp_match_uri, "^([^?#]*)(\\?([^#]*))?(#(.*))?", boost::regex::icase | boost::regex::normal);
STATIC_REGEXP_EXPR_1(rexp_match_uri, "^((.*?)://)?(([^/:]*)(:(\\d+))?)(.*)?", boost::regex::icase | boost::regex::normal);
diff --git a/contrib/epee/include/net/net_utils_base.h b/contrib/epee/include/net/net_utils_base.h
index fce01311c..5ae3e53b3 100644
--- a/contrib/epee/include/net/net_utils_base.h
+++ b/contrib/epee/include/net/net_utils_base.h
@@ -31,6 +31,7 @@
#include <boost/uuid/uuid.hpp>
#include <boost/asio/io_service.hpp>
+#include <boost/asio/ip/address_v6.hpp>
#include <typeinfo>
#include <type_traits>
#include "enums.h"
@@ -154,6 +155,59 @@ namespace net_utils
inline bool operator>=(const ipv4_network_subnet& lhs, const ipv4_network_subnet& rhs) noexcept
{ return !lhs.less(rhs); }
+ class ipv6_network_address
+ {
+ protected:
+ boost::asio::ip::address_v6 m_address;
+ uint16_t m_port;
+
+ public:
+ ipv6_network_address()
+ : ipv6_network_address(boost::asio::ip::address_v6::loopback(), 0)
+ {}
+
+ ipv6_network_address(const boost::asio::ip::address_v6& ip, uint16_t port)
+ : m_address(ip), m_port(port)
+ {
+ }
+
+ bool equal(const ipv6_network_address& other) const noexcept;
+ bool less(const ipv6_network_address& other) const noexcept;
+ bool is_same_host(const ipv6_network_address& other) const noexcept
+ { return m_address == other.m_address; }
+
+ boost::asio::ip::address_v6 ip() const noexcept { return m_address; }
+ uint16_t port() const noexcept { return m_port; }
+ std::string str() const;
+ std::string host_str() const;
+ bool is_loopback() const;
+ bool is_local() const;
+ static constexpr address_type get_type_id() noexcept { return address_type::ipv6; }
+ static constexpr zone get_zone() noexcept { return zone::public_; }
+ static constexpr bool is_blockable() noexcept { return true; }
+
+ static const uint8_t ID = 2;
+ BEGIN_KV_SERIALIZE_MAP()
+ boost::asio::ip::address_v6::bytes_type bytes = this_ref.m_address.to_bytes();
+ epee::serialization::selector<is_store>::serialize_t_val_as_blob(bytes, stg, hparent_section, "addr");
+ const_cast<boost::asio::ip::address_v6&>(this_ref.m_address) = boost::asio::ip::address_v6(bytes);
+ KV_SERIALIZE(m_port)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ inline bool operator==(const ipv6_network_address& lhs, const ipv6_network_address& rhs) noexcept
+ { return lhs.equal(rhs); }
+ inline bool operator!=(const ipv6_network_address& lhs, const ipv6_network_address& rhs) noexcept
+ { return !lhs.equal(rhs); }
+ inline bool operator<(const ipv6_network_address& lhs, const ipv6_network_address& rhs) noexcept
+ { return lhs.less(rhs); }
+ inline bool operator<=(const ipv6_network_address& lhs, const ipv6_network_address& rhs) noexcept
+ { return !rhs.less(lhs); }
+ inline bool operator>(const ipv6_network_address& lhs, const ipv6_network_address& rhs) noexcept
+ { return rhs.less(lhs); }
+ inline bool operator>=(const ipv6_network_address& lhs, const ipv6_network_address& rhs) noexcept
+ { return !lhs.less(rhs); }
+
class network_address
{
struct interface
@@ -261,6 +315,8 @@ namespace net_utils
{
case address_type::ipv4:
return this_ref.template serialize_addr<ipv4_network_address>(is_store_, stg, hparent_section);
+ case address_type::ipv6:
+ return this_ref.template serialize_addr<ipv6_network_address>(is_store_, stg, hparent_section);
case address_type::tor:
return this_ref.template serialize_addr<net::tor_address>(is_store_, stg, hparent_section);
case address_type::i2p:
diff --git a/contrib/epee/src/net_helper.cpp b/contrib/epee/src/net_helper.cpp
index 3543f5716..719f1c8e0 100644
--- a/contrib/epee/src/net_helper.cpp
+++ b/contrib/epee/src/net_helper.cpp
@@ -11,10 +11,39 @@ namespace net_utils
//////////////////////////////////////////////////////////////////////////
boost::asio::ip::tcp::resolver resolver(GET_IO_SERVICE(timeout));
boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), addr, port, boost::asio::ip::tcp::resolver::query::canonical_name);
- boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
+
+ bool try_ipv6 = false;
+ boost::asio::ip::tcp::resolver::iterator iterator;
boost::asio::ip::tcp::resolver::iterator end;
- if(iterator == end) // Documentation states that successful call is guaranteed to be non-empty
- throw boost::system::system_error{boost::asio::error::fault, "Failed to resolve " + addr};
+ boost::system::error_code resolve_error;
+ try
+ {
+ iterator = resolver.resolve(query, resolve_error);
+ if(iterator == end) // Documentation states that successful call is guaranteed to be non-empty
+ {
+ // if IPv4 resolution fails, try IPv6. Unintentional outgoing IPv6 connections should only
+ // be possible if for some reason a hostname was given and that hostname fails IPv4 resolution,
+ // so at least for now there should not be a need for a flag "using ipv6 is ok"
+ try_ipv6 = true;
+ }
+
+ }
+ catch (const boost::system::system_error& e)
+ {
+ if (resolve_error != boost::asio::error::host_not_found &&
+ resolve_error != boost::asio::error::host_not_found_try_again)
+ {
+ throw;
+ }
+ try_ipv6 = true;
+ }
+ if (try_ipv6)
+ {
+ boost::asio::ip::tcp::resolver::query query6(boost::asio::ip::tcp::v6(), addr, port, boost::asio::ip::tcp::resolver::query::canonical_name);
+ iterator = resolver.resolve(query6);
+ if (iterator == end)
+ throw boost::system::system_error{boost::asio::error::fault, "Failed to resolve " + addr};
+ }
//////////////////////////////////////////////////////////////////////////
diff --git a/contrib/epee/src/net_utils_base.cpp b/contrib/epee/src/net_utils_base.cpp
index b7f07a23b..5cc49cc71 100644
--- a/contrib/epee/src/net_utils_base.cpp
+++ b/contrib/epee/src/net_utils_base.cpp
@@ -21,6 +21,19 @@ namespace epee { namespace net_utils
bool ipv4_network_address::is_loopback() const { return net_utils::is_ip_loopback(ip()); }
bool ipv4_network_address::is_local() const { return net_utils::is_ip_local(ip()); }
+ bool ipv6_network_address::equal(const ipv6_network_address& other) const noexcept
+ { return is_same_host(other) && port() == other.port(); }
+
+ bool ipv6_network_address::less(const ipv6_network_address& other) const noexcept
+ { return is_same_host(other) ? port() < other.port() : m_address < other.m_address; }
+
+ std::string ipv6_network_address::str() const
+ { return std::string("[") + host_str() + "]:" + std::to_string(port()); }
+
+ std::string ipv6_network_address::host_str() const { return m_address.to_string(); }
+ bool ipv6_network_address::is_loopback() const { return m_address.is_loopback(); }
+ bool ipv6_network_address::is_local() const { return m_address.is_link_local(); }
+
bool ipv4_network_subnet::equal(const ipv4_network_subnet& other) const noexcept
{ return is_same_host(other) && m_mask == other.m_mask; }