Added socks proxy (tor/i2pd/kovri) support to wallet
This commit is contained in:
parent
e4b049da05
commit
7acfa9f3cc
|
@ -19,6 +19,11 @@ network. The transaction will not be broadcast unless an anonymity connection
|
||||||
is made or until `monerod` is shutdown and restarted with only public
|
is made or until `monerod` is shutdown and restarted with only public
|
||||||
connections enabled.
|
connections enabled.
|
||||||
|
|
||||||
|
Anonymity networks can also be used with `monero-wallet-cli` and
|
||||||
|
`monero-wallet-rpc` - the wallets will connect to a daemon through a proxy. The
|
||||||
|
daemon must provide a hidden service for the RPC itself, which is separate from
|
||||||
|
the hidden service for P2P connections.
|
||||||
|
|
||||||
|
|
||||||
## P2P Commands
|
## P2P Commands
|
||||||
|
|
||||||
|
@ -74,6 +79,35 @@ forwarded to `monerod` localhost port 30000.
|
||||||
These addresses will be shared with outgoing peers, over the same network type,
|
These addresses will be shared with outgoing peers, over the same network type,
|
||||||
otherwise the peer will not be notified of the peer address by the proxy.
|
otherwise the peer will not be notified of the peer address by the proxy.
|
||||||
|
|
||||||
|
### Wallet RPC
|
||||||
|
|
||||||
|
An anonymity network can be configured to forward incoming connections to a
|
||||||
|
`monerod` RPC port - which is independent from the configuration for incoming
|
||||||
|
P2P anonymity connections. The anonymity network (Tor/i2p) is
|
||||||
|
[configured in the same manner](#configuration), except the localhost port
|
||||||
|
must be the RPC port (typically 18081 for mainnet) instead of the p2p port:
|
||||||
|
|
||||||
|
> HiddenServiceDir /var/lib/tor/data/monero
|
||||||
|
> HiddenServicePort 18081 127.0.0.1:18081
|
||||||
|
|
||||||
|
Then the wallet will be configured to use a Tor/i2p address:
|
||||||
|
> `--proxy 127.0.0.1:9050`
|
||||||
|
> `--daemon-address rveahdfho7wo4b2m.onion`
|
||||||
|
|
||||||
|
The proxy must match the address type - a Tor proxy will not work properly with
|
||||||
|
i2p addresses, etc.
|
||||||
|
|
||||||
|
i2p and onion addresses provide the information necessary to authenticate and
|
||||||
|
encrypt the connection from end-to-end. If desired, SSL can also be applied to
|
||||||
|
the connection with `--daemon-address https://rveahdfho7wo4b2m.onion` which
|
||||||
|
requires a server certificate that is signed by a "root" certificate on the
|
||||||
|
machine running the wallet. Alternatively, `--daemon-cert-file` can be used to
|
||||||
|
specify a certificate to authenticate the server.
|
||||||
|
|
||||||
|
Proxies can also be used to connect to "clearnet" (ipv4 addresses or ICANN
|
||||||
|
domains), but `--daemon-cert-file` _must_ be used for authentication and
|
||||||
|
encryption.
|
||||||
|
|
||||||
### Network Types
|
### Network Types
|
||||||
|
|
||||||
#### Tor & I2P
|
#### Tor & I2P
|
||||||
|
|
|
@ -58,11 +58,6 @@
|
||||||
#define DEFAULT_TIMEOUT_MS_REMOTE 300000 // 5 minutes
|
#define DEFAULT_TIMEOUT_MS_REMOTE 300000 // 5 minutes
|
||||||
#define TIMEOUT_EXTRA_MS_PER_BYTE 0.2
|
#define TIMEOUT_EXTRA_MS_PER_BYTE 0.2
|
||||||
|
|
||||||
#if BOOST_VERSION >= 107000
|
|
||||||
#define GET_IO_SERVICE(s) ((boost::asio::io_context&)(s).get_executor().context())
|
|
||||||
#else
|
|
||||||
#define GET_IO_SERVICE(s) ((s).get_io_service())
|
|
||||||
#endif
|
|
||||||
|
|
||||||
PRAGMA_WARNING_PUSH
|
PRAGMA_WARNING_PUSH
|
||||||
namespace epee
|
namespace epee
|
||||||
|
|
|
@ -327,10 +327,17 @@ namespace net_utils
|
||||||
m_net_client.set_ssl(m_ssl_support, m_ssl_private_key_and_certificate_path, m_ssl_allowed_certificates, m_ssl_allowed_fingerprints, m_ssl_allow_any_cert);
|
m_net_client.set_ssl(m_ssl_support, m_ssl_private_key_and_certificate_path, m_ssl_allowed_certificates, m_ssl_allowed_fingerprints, m_ssl_allow_any_cert);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
void set_connector(F connector)
|
||||||
|
{
|
||||||
|
CRITICAL_REGION_LOCAL(m_lock);
|
||||||
|
m_net_client.set_connector(std::move(connector));
|
||||||
|
}
|
||||||
|
|
||||||
bool connect(std::chrono::milliseconds timeout)
|
bool connect(std::chrono::milliseconds timeout)
|
||||||
{
|
{
|
||||||
CRITICAL_REGION_LOCAL(m_lock);
|
CRITICAL_REGION_LOCAL(m_lock);
|
||||||
return m_net_client.connect(m_host_buff, m_port, timeout, "0.0.0.0");
|
return m_net_client.connect(m_host_buff, m_port, timeout);
|
||||||
}
|
}
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
bool disconnect()
|
bool disconnect()
|
||||||
|
|
|
@ -33,12 +33,17 @@
|
||||||
//#include <Ws2tcpip.h>
|
//#include <Ws2tcpip.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <boost/version.hpp>
|
#include <boost/version.hpp>
|
||||||
#include <boost/asio.hpp>
|
#include <boost/asio/io_service.hpp>
|
||||||
|
#include <boost/asio/ip/tcp.hpp>
|
||||||
|
#include <boost/asio/read.hpp>
|
||||||
#include <boost/asio/ssl.hpp>
|
#include <boost/asio/ssl.hpp>
|
||||||
#include <boost/asio/steady_timer.hpp>
|
#include <boost/asio/steady_timer.hpp>
|
||||||
|
#include <boost/thread/future.hpp>
|
||||||
#include <boost/lambda/bind.hpp>
|
#include <boost/lambda/bind.hpp>
|
||||||
#include <boost/lambda/lambda.hpp>
|
#include <boost/lambda/lambda.hpp>
|
||||||
#include <boost/interprocess/detail/atomic.hpp>
|
#include <boost/interprocess/detail/atomic.hpp>
|
||||||
|
#include <boost/system/error_code.hpp>
|
||||||
|
#include <functional>
|
||||||
#include "net/net_utils_base.h"
|
#include "net/net_utils_base.h"
|
||||||
#include "net/net_ssl.h"
|
#include "net/net_ssl.h"
|
||||||
#include "misc_language.h"
|
#include "misc_language.h"
|
||||||
|
@ -55,6 +60,12 @@ namespace epee
|
||||||
{
|
{
|
||||||
namespace net_utils
|
namespace net_utils
|
||||||
{
|
{
|
||||||
|
struct direct_connect
|
||||||
|
{
|
||||||
|
boost::unique_future<boost::asio::ip::tcp::socket>
|
||||||
|
operator()(const std::string& addr, const std::string& port, boost::asio::steady_timer&) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class blocked_mode_client
|
class blocked_mode_client
|
||||||
{
|
{
|
||||||
|
@ -88,28 +99,35 @@ namespace net_utils
|
||||||
|
|
||||||
public:
|
public:
|
||||||
inline
|
inline
|
||||||
blocked_mode_client():m_initialized(false),
|
blocked_mode_client() :
|
||||||
|
m_io_service(),
|
||||||
|
m_ctx({boost::asio::ssl::context(boost::asio::ssl::context::tlsv12), {}}),
|
||||||
|
m_connector(direct_connect{}),
|
||||||
|
m_ssl_socket(new boost::asio::ssl::stream<boost::asio::ip::tcp::socket>(m_io_service, m_ctx.context)),
|
||||||
|
m_ssl_support(epee::net_utils::ssl_support_t::e_ssl_support_autodetect),
|
||||||
|
m_initialized(true),
|
||||||
m_connected(false),
|
m_connected(false),
|
||||||
m_deadline(m_io_service),
|
m_deadline(m_io_service),
|
||||||
m_shutdowned(0),
|
m_shutdowned(0)
|
||||||
m_ssl_support(epee::net_utils::ssl_support_t::e_ssl_support_autodetect),
|
|
||||||
m_ctx({boost::asio::ssl::context(boost::asio::ssl::context::tlsv12), {}}),
|
|
||||||
m_ssl_socket(new boost::asio::ssl::stream<boost::asio::ip::tcp::socket>(m_io_service,m_ctx.context))
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
m_initialized = true;
|
|
||||||
|
|
||||||
|
|
||||||
// No deadline is required until the first socket operation is started. We
|
|
||||||
// set the deadline to positive infinity so that the actor takes no action
|
|
||||||
// until a specific deadline is set.
|
|
||||||
m_deadline.expires_at(std::chrono::steady_clock::time_point::max());
|
|
||||||
|
|
||||||
// Start the persistent actor that checks for deadline expiry.
|
|
||||||
check_deadline();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! The first/second parameters are host/port respectively. The third
|
||||||
|
parameter is for setting the timeout callback - the timer is
|
||||||
|
already set by the caller, the callee only needs to set the
|
||||||
|
behavior.
|
||||||
|
|
||||||
|
Additional asynchronous operations should be queued using the
|
||||||
|
`io_service` from the timer. The implementation should assume
|
||||||
|
multi-threaded I/O processing.
|
||||||
|
|
||||||
|
If the callee cannot start an asynchronous operation, an exception
|
||||||
|
should be thrown to signal an immediate failure.
|
||||||
|
|
||||||
|
The return value is a future to a connected socket. Asynchronous
|
||||||
|
failures should use the `set_exception` method. */
|
||||||
|
using connect_func = boost::unique_future<boost::asio::ip::tcp::socket>(const std::string&, const std::string&, boost::asio::steady_timer&);
|
||||||
|
|
||||||
inline
|
inline
|
||||||
~blocked_mode_client()
|
~blocked_mode_client()
|
||||||
{
|
{
|
||||||
|
@ -128,33 +146,28 @@ namespace net_utils
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
bool connect(const std::string& addr, int port, std::chrono::milliseconds timeout, const std::string& bind_ip = "0.0.0.0")
|
bool connect(const std::string& addr, int port, std::chrono::milliseconds timeout)
|
||||||
{
|
{
|
||||||
return connect(addr, std::to_string(port), timeout, bind_ip);
|
return connect(addr, std::to_string(port), timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
try_connect_result_t try_connect(const std::string& addr, const std::string& port, const boost::asio::ip::tcp::endpoint &remote_endpoint, std::chrono::milliseconds timeout, const std::string& bind_ip, epee::net_utils::ssl_support_t ssl_support)
|
try_connect_result_t try_connect(const std::string& addr, const std::string& port, std::chrono::milliseconds timeout, epee::net_utils::ssl_support_t ssl_support)
|
||||||
{
|
{
|
||||||
m_ssl_socket->next_layer().open(remote_endpoint.protocol());
|
|
||||||
if(bind_ip != "0.0.0.0" && bind_ip != "0" && bind_ip != "" )
|
|
||||||
{
|
|
||||||
boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(addr.c_str()), 0);
|
|
||||||
m_ssl_socket->next_layer().bind(local_endpoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
m_deadline.expires_from_now(timeout);
|
m_deadline.expires_from_now(timeout);
|
||||||
|
boost::unique_future<boost::asio::ip::tcp::socket> connection = m_connector(addr, port, m_deadline);
|
||||||
boost::system::error_code ec = boost::asio::error::would_block;
|
for (;;)
|
||||||
|
|
||||||
m_ssl_socket->next_layer().async_connect(remote_endpoint, boost::lambda::var(ec) = boost::lambda::_1);
|
|
||||||
while (ec == boost::asio::error::would_block)
|
|
||||||
{
|
{
|
||||||
|
m_io_service.reset();
|
||||||
m_io_service.run_one();
|
m_io_service.run_one();
|
||||||
|
|
||||||
|
if (connection.is_ready())
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ec && m_ssl_socket->next_layer().is_open())
|
m_ssl_socket->next_layer() = connection.get();
|
||||||
|
m_deadline.cancel();
|
||||||
|
if (m_ssl_socket->next_layer().is_open())
|
||||||
{
|
{
|
||||||
m_connected = true;
|
m_connected = true;
|
||||||
m_deadline.expires_at(std::chrono::steady_clock::time_point::max());
|
m_deadline.expires_at(std::chrono::steady_clock::time_point::max());
|
||||||
|
@ -183,14 +196,14 @@ namespace net_utils
|
||||||
return CONNECT_SUCCESS;
|
return CONNECT_SUCCESS;
|
||||||
}else
|
}else
|
||||||
{
|
{
|
||||||
MWARNING("Some problems at connect, message: " << ec.message());
|
MWARNING("Some problems at connect, expected open socket");
|
||||||
return CONNECT_FAILURE;
|
return CONNECT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
bool connect(const std::string& addr, const std::string& port, std::chrono::milliseconds timeout, const std::string& bind_ip = "0.0.0.0")
|
bool connect(const std::string& addr, const std::string& port, std::chrono::milliseconds timeout)
|
||||||
{
|
{
|
||||||
m_connected = false;
|
m_connected = false;
|
||||||
try
|
try
|
||||||
|
@ -205,25 +218,7 @@ namespace net_utils
|
||||||
|
|
||||||
// Get a list of endpoints corresponding to the server name.
|
// Get a list of endpoints corresponding to the server name.
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
try_connect_result_t try_connect_result = try_connect(addr, port, timeout, m_ssl_support);
|
||||||
|
|
||||||
boost::asio::ip::tcp::resolver resolver(m_io_service);
|
|
||||||
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);
|
|
||||||
boost::asio::ip::tcp::resolver::iterator end;
|
|
||||||
if(iterator == end)
|
|
||||||
{
|
|
||||||
LOG_ERROR("Failed to resolve " << addr);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
|
|
||||||
//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);
|
|
||||||
|
|
||||||
try_connect_result_t try_connect_result = try_connect(addr, port, remote_endpoint, timeout, bind_ip, m_ssl_support);
|
|
||||||
if (try_connect_result == CONNECT_FAILURE)
|
if (try_connect_result == CONNECT_FAILURE)
|
||||||
return false;
|
return false;
|
||||||
if (m_ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect)
|
if (m_ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect)
|
||||||
|
@ -233,7 +228,7 @@ namespace net_utils
|
||||||
{
|
{
|
||||||
MERROR("SSL handshake failed on an autodetect connection, reconnecting without SSL");
|
MERROR("SSL handshake failed on an autodetect connection, reconnecting without SSL");
|
||||||
m_ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_disabled;
|
m_ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_disabled;
|
||||||
if (try_connect(addr, port, remote_endpoint, timeout, bind_ip, m_ssl_support) != CONNECT_SUCCESS)
|
if (try_connect(addr, port, timeout, m_ssl_support) != CONNECT_SUCCESS)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -251,6 +246,11 @@ namespace net_utils
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
//! Change the connection routine (proxy, etc.)
|
||||||
|
void set_connector(std::function<connect_func> connector)
|
||||||
|
{
|
||||||
|
m_connector = std::move(connector);
|
||||||
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
bool disconnect()
|
bool disconnect()
|
||||||
|
@ -265,7 +265,6 @@ namespace net_utils
|
||||||
m_ssl_socket->next_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both);
|
m_ssl_socket->next_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
catch(const boost::system::system_error& /*er*/)
|
catch(const boost::system::system_error& /*er*/)
|
||||||
{
|
{
|
||||||
//LOG_ERROR("Some problems at disconnect, message: " << er.what());
|
//LOG_ERROR("Some problems at disconnect, message: " << er.what());
|
||||||
|
@ -304,6 +303,7 @@ namespace net_utils
|
||||||
// Block until the asynchronous operation has completed.
|
// Block until the asynchronous operation has completed.
|
||||||
while (ec == boost::asio::error::would_block)
|
while (ec == boost::asio::error::would_block)
|
||||||
{
|
{
|
||||||
|
m_io_service.reset();
|
||||||
m_io_service.run_one();
|
m_io_service.run_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -433,6 +433,7 @@ namespace net_utils
|
||||||
// Block until the asynchronous operation has completed.
|
// Block until the asynchronous operation has completed.
|
||||||
while (ec == boost::asio::error::would_block && !boost::interprocess::ipcdetail::atomic_read32(&m_shutdowned))
|
while (ec == boost::asio::error::would_block && !boost::interprocess::ipcdetail::atomic_read32(&m_shutdowned))
|
||||||
{
|
{
|
||||||
|
m_io_service.reset();
|
||||||
m_io_service.run_one();
|
m_io_service.run_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -573,10 +574,6 @@ namespace net_utils
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_connected(bool connected)
|
|
||||||
{
|
|
||||||
m_connected = connected;
|
|
||||||
}
|
|
||||||
boost::asio::io_service& get_io_service()
|
boost::asio::io_service& get_io_service()
|
||||||
{
|
{
|
||||||
return m_io_service;
|
return m_io_service;
|
||||||
|
@ -619,6 +616,7 @@ namespace net_utils
|
||||||
m_ssl_socket->async_shutdown(boost::lambda::var(ec) = boost::lambda::_1);
|
m_ssl_socket->async_shutdown(boost::lambda::var(ec) = boost::lambda::_1);
|
||||||
while (ec == boost::asio::error::would_block)
|
while (ec == boost::asio::error::would_block)
|
||||||
{
|
{
|
||||||
|
m_io_service.reset();
|
||||||
m_io_service.run_one();
|
m_io_service.run_one();
|
||||||
}
|
}
|
||||||
// Ignore "short read" error
|
// Ignore "short read" error
|
||||||
|
@ -665,11 +663,8 @@ namespace net_utils
|
||||||
boost::asio::io_service m_io_service;
|
boost::asio::io_service m_io_service;
|
||||||
epee::net_utils::ssl_context_t m_ctx;
|
epee::net_utils::ssl_context_t m_ctx;
|
||||||
std::shared_ptr<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> m_ssl_socket;
|
std::shared_ptr<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> m_ssl_socket;
|
||||||
|
std::function<connect_func> m_connector;
|
||||||
epee::net_utils::ssl_support_t m_ssl_support;
|
epee::net_utils::ssl_support_t m_ssl_support;
|
||||||
std::string m_ssl_private_key;
|
|
||||||
std::string m_ssl_certificate;
|
|
||||||
std::list<std::string> m_ssl_allowed_certificates;
|
|
||||||
bool m_ssl_allow_any_cerl;
|
|
||||||
bool m_initialized;
|
bool m_initialized;
|
||||||
bool m_connected;
|
bool m_connected;
|
||||||
boost::asio::steady_timer m_deadline;
|
boost::asio::steady_timer m_deadline;
|
||||||
|
@ -790,3 +785,4 @@ namespace net_utils
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,12 @@
|
||||||
#define MAKE_IP( a1, a2, a3, a4 ) (a1|(a2<<8)|(a3<<16)|(a4<<24))
|
#define MAKE_IP( a1, a2, a3, a4 ) (a1|(a2<<8)|(a3<<16)|(a4<<24))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if BOOST_VERSION >= 107000
|
||||||
|
#define GET_IO_SERVICE(s) ((boost::asio::io_context&)(s).get_executor().context())
|
||||||
|
#else
|
||||||
|
#define GET_IO_SERVICE(s) ((s).get_io_service())
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace net
|
namespace net
|
||||||
{
|
{
|
||||||
class tor_address;
|
class tor_address;
|
||||||
|
|
|
@ -26,8 +26,9 @@
|
||||||
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
# 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.
|
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
add_library(epee STATIC hex.cpp http_auth.cpp mlog.cpp net_utils_base.cpp string_tools.cpp wipeable_string.cpp memwipe.c
|
add_library(epee STATIC hex.cpp http_auth.cpp mlog.cpp net_helper.cpp net_utils_base.cpp string_tools.cpp wipeable_string.cpp memwipe.c
|
||||||
connection_basic.cpp network_throttle.cpp network_throttle-detail.cpp mlocker.cpp buffer.cpp net_ssl.cpp)
|
connection_basic.cpp network_throttle.cpp network_throttle-detail.cpp mlocker.cpp buffer.cpp net_ssl.cpp)
|
||||||
|
|
||||||
if (USE_READLINE AND GNU_READLINE_FOUND)
|
if (USE_READLINE AND GNU_READLINE_FOUND)
|
||||||
add_library(epee_readline STATIC readline_buffer.cpp)
|
add_library(epee_readline STATIC readline_buffer.cpp)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
#include "net/net_helper.h"
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace net_utils
|
||||||
|
{
|
||||||
|
boost::unique_future<boost::asio::ip::tcp::socket>
|
||||||
|
direct_connect::operator()(const std::string& addr, const std::string& port, boost::asio::steady_timer& timeout) const
|
||||||
|
{
|
||||||
|
// Get a list of endpoints corresponding to the server name.
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
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);
|
||||||
|
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};
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
struct new_connection
|
||||||
|
{
|
||||||
|
boost::promise<boost::asio::ip::tcp::socket> result_;
|
||||||
|
boost::asio::ip::tcp::socket socket_;
|
||||||
|
|
||||||
|
explicit new_connection(boost::asio::io_service& io_service)
|
||||||
|
: result_(), socket_(io_service)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto shared = std::make_shared<new_connection>(GET_IO_SERVICE(timeout));
|
||||||
|
timeout.async_wait([shared] (boost::system::error_code error)
|
||||||
|
{
|
||||||
|
if (error != boost::system::errc::operation_canceled && shared && shared->socket_.is_open())
|
||||||
|
{
|
||||||
|
shared->socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
|
||||||
|
shared->socket_.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
shared->socket_.async_connect(*iterator, [shared] (boost::system::error_code error)
|
||||||
|
{
|
||||||
|
if (shared)
|
||||||
|
{
|
||||||
|
if (error)
|
||||||
|
shared->result_.set_exception(boost::system::system_error{error});
|
||||||
|
else
|
||||||
|
shared->result_.set_value(std::move(shared->socket_));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return shared->result_.get_future();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -26,8 +26,8 @@
|
||||||
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
# 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.
|
# 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 i2p_address.cpp)
|
set(net_sources error.cpp i2p_address.cpp parse.cpp socks.cpp socks_connect.cpp tor_address.cpp)
|
||||||
set(net_headers error.h parse.h socks.h tor_address.h i2p_address.h)
|
set(net_headers error.h i2p_address.h parse.h socks.h socks_connect.h tor_address.h)
|
||||||
|
|
||||||
monero_add_library(net ${net_sources} ${net_headers})
|
monero_add_library(net ${net_sources} ${net_headers})
|
||||||
target_link_libraries(net common epee ${Boost_ASIO_LIBRARY})
|
target_link_libraries(net common epee ${Boost_ASIO_LIBRARY})
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) 2018, The Monero Project
|
// Copyright (c) 2018-2019, The Monero Project
|
||||||
//
|
//
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
|
@ -193,7 +193,7 @@ namespace socks
|
||||||
else if (bytes < self.buffer().size())
|
else if (bytes < self.buffer().size())
|
||||||
self.done(socks::error::bad_write, std::move(self_));
|
self.done(socks::error::bad_write, std::move(self_));
|
||||||
else
|
else
|
||||||
boost::asio::async_read(self.proxy_, get_buffer(self), completed{std::move(self_)});
|
boost::asio::async_read(self.proxy_, get_buffer(self), self.strand_.wrap(completed{std::move(self_)}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -215,13 +215,13 @@ namespace socks
|
||||||
if (error)
|
if (error)
|
||||||
self.done(error, std::move(self_));
|
self.done(error, std::move(self_));
|
||||||
else
|
else
|
||||||
boost::asio::async_write(self.proxy_, get_buffer(self), read{std::move(self_)});
|
boost::asio::async_write(self.proxy_, get_buffer(self), self.strand_.wrap(read{std::move(self_)}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
client::client(stream_type::socket&& proxy, socks::version ver)
|
client::client(stream_type::socket&& proxy, socks::version ver)
|
||||||
: proxy_(std::move(proxy)), buffer_size_(0), buffer_(), ver_(ver)
|
: proxy_(std::move(proxy)), strand_(proxy_.get_io_service()), buffer_size_(0), buffer_(), ver_(ver)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
client::~client() {}
|
client::~client() {}
|
||||||
|
@ -296,7 +296,7 @@ namespace socks
|
||||||
if (self && !self->buffer().empty())
|
if (self && !self->buffer().empty())
|
||||||
{
|
{
|
||||||
client& alias = *self;
|
client& alias = *self;
|
||||||
alias.proxy_.async_connect(proxy_address, write{std::move(self)});
|
alias.proxy_.async_connect(proxy_address, alias.strand_.wrap(write{std::move(self)}));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -307,10 +307,26 @@ namespace socks
|
||||||
if (self && !self->buffer().empty())
|
if (self && !self->buffer().empty())
|
||||||
{
|
{
|
||||||
client& alias = *self;
|
client& alias = *self;
|
||||||
boost::asio::async_write(alias.proxy_, write::get_buffer(alias), read{std::move(self)});
|
boost::asio::async_write(alias.proxy_, write::get_buffer(alias), alias.strand_.wrap(read{std::move(self)}));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void client::async_close::operator()(boost::system::error_code error)
|
||||||
|
{
|
||||||
|
if (self_ && error != boost::system::errc::operation_canceled)
|
||||||
|
{
|
||||||
|
const std::shared_ptr<client> self = std::move(self_);
|
||||||
|
self->strand_.dispatch([self] ()
|
||||||
|
{
|
||||||
|
if (self && self->proxy_.is_open())
|
||||||
|
{
|
||||||
|
self->proxy_.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
|
||||||
|
self->proxy_.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
} // socks
|
} // socks
|
||||||
} // net
|
} // net
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) 2018, The Monero Project
|
// Copyright (c) 2018-2019, The Monero Project
|
||||||
//
|
//
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
|
@ -31,6 +31,7 @@
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <boost/asio/ip/tcp.hpp>
|
#include <boost/asio/ip/tcp.hpp>
|
||||||
#include <boost/asio/io_service.hpp>
|
#include <boost/asio/io_service.hpp>
|
||||||
|
#include <boost/asio/strand.hpp>
|
||||||
#include <boost/system/error_code.hpp>
|
#include <boost/system/error_code.hpp>
|
||||||
#include <boost/type_traits/integral_constant.hpp>
|
#include <boost/type_traits/integral_constant.hpp>
|
||||||
#include <boost/utility/string_ref.hpp>
|
#include <boost/utility/string_ref.hpp>
|
||||||
|
@ -92,6 +93,7 @@ namespace socks
|
||||||
class client
|
class client
|
||||||
{
|
{
|
||||||
boost::asio::ip::tcp::socket proxy_;
|
boost::asio::ip::tcp::socket proxy_;
|
||||||
|
boost::asio::io_service::strand strand_;
|
||||||
std::uint16_t buffer_size_;
|
std::uint16_t buffer_size_;
|
||||||
std::uint8_t buffer_[1024];
|
std::uint8_t buffer_[1024];
|
||||||
socks::version ver_;
|
socks::version ver_;
|
||||||
|
@ -168,6 +170,8 @@ namespace socks
|
||||||
|
|
||||||
\note Must use one of the `self->set_*_command` calls before using
|
\note Must use one of the `self->set_*_command` calls before using
|
||||||
this function.
|
this function.
|
||||||
|
\note Only `async_close` can be invoked on `self` until the `done`
|
||||||
|
callback is invoked.
|
||||||
|
|
||||||
\param self ownership of object is given to function.
|
\param self ownership of object is given to function.
|
||||||
\param proxy_address of the socks server.
|
\param proxy_address of the socks server.
|
||||||
|
@ -182,11 +186,21 @@ namespace socks
|
||||||
|
|
||||||
\note Must use one of the `self->set_*_command` calls before using
|
\note Must use one of the `self->set_*_command` calls before using
|
||||||
the function.
|
the function.
|
||||||
|
\note Only `async_close` can be invoked on `self` until the `done`
|
||||||
|
callback is invoked.
|
||||||
|
|
||||||
\param self ownership of object is given to function.
|
\param self ownership of object is given to function.
|
||||||
\return False if `self->buffer().empty()` (no command set).
|
\return False if `self->buffer().empty()` (no command set).
|
||||||
*/
|
*/
|
||||||
static bool send(std::shared_ptr<client> self);
|
static bool send(std::shared_ptr<client> self);
|
||||||
|
|
||||||
|
/*! Callback for closing socket. Thread-safe with `*send` functions;
|
||||||
|
never blocks (uses strands). */
|
||||||
|
struct async_close
|
||||||
|
{
|
||||||
|
std::shared_ptr<client> self_;
|
||||||
|
void operator()(boost::system::error_code error = boost::system::error_code{});
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename Handler>
|
template<typename Handler>
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
// Copyright (c) 2019, 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_connect.h"
|
||||||
|
|
||||||
|
#include <boost/system/error_code.hpp>
|
||||||
|
#include <boost/system/system_error.hpp>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
#include <system_error>
|
||||||
|
|
||||||
|
#include "net/error.h"
|
||||||
|
#include "net/net_utils_base.h"
|
||||||
|
#include "net/socks.h"
|
||||||
|
#include "string_tools.h"
|
||||||
|
|
||||||
|
namespace net
|
||||||
|
{
|
||||||
|
namespace socks
|
||||||
|
{
|
||||||
|
boost::unique_future<boost::asio::ip::tcp::socket>
|
||||||
|
connector::operator()(const std::string& remote_host, const std::string& remote_port, boost::asio::steady_timer& timeout) const
|
||||||
|
{
|
||||||
|
struct future_socket
|
||||||
|
{
|
||||||
|
boost::promise<boost::asio::ip::tcp::socket> result_;
|
||||||
|
|
||||||
|
void operator()(boost::system::error_code error, boost::asio::ip::tcp::socket&& socket)
|
||||||
|
{
|
||||||
|
if (error)
|
||||||
|
result_.set_exception(boost::system::system_error{error});
|
||||||
|
else
|
||||||
|
result_.set_value(std::move(socket));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
boost::unique_future<boost::asio::ip::tcp::socket> out{};
|
||||||
|
{
|
||||||
|
std::uint16_t port = 0;
|
||||||
|
if (!epee::string_tools::get_xtype_from_string(port, remote_port))
|
||||||
|
throw std::system_error{net::error::invalid_port, "Remote port for socks proxy"};
|
||||||
|
|
||||||
|
bool is_set = false;
|
||||||
|
std::uint32_t ip_address = 0;
|
||||||
|
boost::promise<boost::asio::ip::tcp::socket> result{};
|
||||||
|
out = result.get_future();
|
||||||
|
const auto proxy = net::socks::make_connect_client(
|
||||||
|
boost::asio::ip::tcp::socket{GET_IO_SERVICE(timeout)}, net::socks::version::v4a, future_socket{std::move(result)}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (epee::string_tools::get_ip_int32_from_string(ip_address, remote_host))
|
||||||
|
is_set = proxy->set_connect_command(epee::net_utils::ipv4_network_address{ip_address, port});
|
||||||
|
else
|
||||||
|
is_set = proxy->set_connect_command(remote_host, port);
|
||||||
|
|
||||||
|
if (!is_set || !net::socks::client::connect_and_send(proxy, proxy_address))
|
||||||
|
throw std::system_error{net::error::invalid_host, "Address for socks proxy"};
|
||||||
|
|
||||||
|
timeout.async_wait(net::socks::client::async_close{std::move(proxy)});
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
} // socks
|
||||||
|
} // net
|
|
@ -0,0 +1,55 @@
|
||||||
|
// Copyright (c) 2019, 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/asio/ip/tcp.hpp>
|
||||||
|
#include <boost/asio/steady_timer.hpp>
|
||||||
|
#include <boost/thread/future.hpp>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace net
|
||||||
|
{
|
||||||
|
namespace socks
|
||||||
|
{
|
||||||
|
//! Primarily for use with `epee::net_utils::http_client`.
|
||||||
|
struct connector
|
||||||
|
{
|
||||||
|
boost::asio::ip::tcp::endpoint proxy_address;
|
||||||
|
|
||||||
|
/*! Creates a new socket, asynchronously connects to `proxy_address`,
|
||||||
|
and requests a connection to `remote_host` on `remote_port`. Sets
|
||||||
|
socket as closed if `timeout` is reached.
|
||||||
|
|
||||||
|
\return The socket if successful, and exception in the future with
|
||||||
|
error otherwise. */
|
||||||
|
boost::unique_future<boost::asio::ip::tcp::socket>
|
||||||
|
operator()(const std::string& remote_host, const std::string& remote_port, boost::asio::steady_timer& timeout) const;
|
||||||
|
};
|
||||||
|
} // socks
|
||||||
|
} // net
|
|
@ -63,6 +63,7 @@ target_link_libraries(wallet
|
||||||
cryptonote_core
|
cryptonote_core
|
||||||
mnemonics
|
mnemonics
|
||||||
device_trezor
|
device_trezor
|
||||||
|
net
|
||||||
${LMDB_LIBRARY}
|
${LMDB_LIBRARY}
|
||||||
${Boost_CHRONO_LIBRARY}
|
${Boost_CHRONO_LIBRARY}
|
||||||
${Boost_SERIALIZATION_LIBRARY}
|
${Boost_SERIALIZATION_LIBRARY}
|
||||||
|
|
|
@ -2173,8 +2173,7 @@ void WalletImpl::pendingTxPostProcess(PendingTransactionImpl * pending)
|
||||||
|
|
||||||
bool WalletImpl::doInit(const string &daemon_address, uint64_t upper_transaction_size_limit, bool ssl)
|
bool WalletImpl::doInit(const string &daemon_address, uint64_t upper_transaction_size_limit, bool ssl)
|
||||||
{
|
{
|
||||||
// claim RPC so there's no in-memory encryption for now
|
if (!m_wallet->init(daemon_address, m_daemon_login, tcp::endpoint{}, upper_transaction_size_limit))
|
||||||
if (!m_wallet->init(daemon_address, m_daemon_login, upper_transaction_size_limit, ssl))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// in case new wallet, this will force fast-refresh (pulling hashes instead of blocks)
|
// in case new wallet, this will force fast-refresh (pulling hashes instead of blocks)
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
#include <boost/algorithm/string/trim.hpp>
|
#include <boost/algorithm/string/trim.hpp>
|
||||||
#include <boost/algorithm/string/split.hpp>
|
#include <boost/algorithm/string/split.hpp>
|
||||||
#include <boost/algorithm/string/join.hpp>
|
#include <boost/algorithm/string/join.hpp>
|
||||||
|
#include <boost/asio/ip/address.hpp>
|
||||||
#include <boost/range/adaptor/transformed.hpp>
|
#include <boost/range/adaptor/transformed.hpp>
|
||||||
#include "include_base_utils.h"
|
#include "include_base_utils.h"
|
||||||
using namespace epee;
|
using namespace epee;
|
||||||
|
@ -75,6 +76,7 @@ using namespace epee;
|
||||||
#include "ringdb.h"
|
#include "ringdb.h"
|
||||||
#include "device/device_cold.hpp"
|
#include "device/device_cold.hpp"
|
||||||
#include "device_trezor/device_trezor.hpp"
|
#include "device_trezor/device_trezor.hpp"
|
||||||
|
#include "net/socks_connect.h"
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
|
@ -231,6 +233,7 @@ namespace
|
||||||
struct options {
|
struct options {
|
||||||
const command_line::arg_descriptor<std::string> daemon_address = {"daemon-address", tools::wallet2::tr("Use daemon instance at <host>:<port>"), ""};
|
const command_line::arg_descriptor<std::string> daemon_address = {"daemon-address", tools::wallet2::tr("Use daemon instance at <host>:<port>"), ""};
|
||||||
const command_line::arg_descriptor<std::string> daemon_host = {"daemon-host", tools::wallet2::tr("Use daemon instance at host <arg> instead of localhost"), ""};
|
const command_line::arg_descriptor<std::string> daemon_host = {"daemon-host", tools::wallet2::tr("Use daemon instance at host <arg> instead of localhost"), ""};
|
||||||
|
const command_line::arg_descriptor<std::string> proxy = {"proxy", tools::wallet2::tr("[<ip>:]<port> socks proxy to use for daemon connections"), {}, true};
|
||||||
const command_line::arg_descriptor<bool> trusted_daemon = {"trusted-daemon", tools::wallet2::tr("Enable commands which rely on a trusted daemon"), false};
|
const command_line::arg_descriptor<bool> trusted_daemon = {"trusted-daemon", tools::wallet2::tr("Enable commands which rely on a trusted daemon"), false};
|
||||||
const command_line::arg_descriptor<bool> untrusted_daemon = {"untrusted-daemon", tools::wallet2::tr("Disable commands which rely on a trusted daemon"), false};
|
const command_line::arg_descriptor<bool> untrusted_daemon = {"untrusted-daemon", tools::wallet2::tr("Disable commands which rely on a trusted daemon"), false};
|
||||||
const command_line::arg_descriptor<std::string> password = {"password", tools::wallet2::tr("Wallet password (escape/quote as needed)"), "", true};
|
const command_line::arg_descriptor<std::string> password = {"password", tools::wallet2::tr("Wallet password (escape/quote as needed)"), "", true};
|
||||||
|
@ -303,6 +306,8 @@ std::string get_weight_string(const cryptonote::transaction &tx, size_t blob_siz
|
||||||
|
|
||||||
std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variables_map& vm, bool unattended, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
|
std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variables_map& vm, bool unattended, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
|
||||||
{
|
{
|
||||||
|
namespace ip = boost::asio::ip;
|
||||||
|
|
||||||
const bool testnet = command_line::get_arg(vm, opts.testnet);
|
const bool testnet = command_line::get_arg(vm, opts.testnet);
|
||||||
const bool stagenet = command_line::get_arg(vm, opts.stagenet);
|
const bool stagenet = command_line::get_arg(vm, opts.stagenet);
|
||||||
const network_type nettype = testnet ? TESTNET : stagenet ? STAGENET : MAINNET;
|
const network_type nettype = testnet ? TESTNET : stagenet ? STAGENET : MAINNET;
|
||||||
|
@ -352,6 +357,44 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
|
||||||
if (daemon_address.empty())
|
if (daemon_address.empty())
|
||||||
daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port);
|
daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port);
|
||||||
|
|
||||||
|
boost::asio::ip::tcp::endpoint proxy{};
|
||||||
|
if (command_line::has_arg(vm, opts.proxy))
|
||||||
|
{
|
||||||
|
namespace ip = boost::asio::ip;
|
||||||
|
const boost::string_ref real_daemon = boost::string_ref{daemon_address}.substr(0, daemon_address.rfind(':'));
|
||||||
|
|
||||||
|
// onion and i2p addresses contain information about the server cert
|
||||||
|
// which both authenticates and encrypts
|
||||||
|
const bool unencrypted_proxy =
|
||||||
|
!real_daemon.ends_with(".onion") && !real_daemon.ends_with(".i2p") &&
|
||||||
|
daemon_ssl_allowed_certificates.empty() && daemon_ssl_allowed_fingerprints.empty();
|
||||||
|
THROW_WALLET_EXCEPTION_IF(
|
||||||
|
unencrypted_proxy,
|
||||||
|
tools::error::wallet_internal_error,
|
||||||
|
std::string{"Use of --"} + opts.proxy.name + " requires --" + opts.daemon_ssl_allowed_certificates.name + " or --" + opts.daemon_ssl_allowed_fingerprints.name + " or use of a .onion/.i2p domain"
|
||||||
|
);
|
||||||
|
|
||||||
|
const auto proxy_address = command_line::get_arg(vm, opts.proxy);
|
||||||
|
|
||||||
|
boost::string_ref proxy_port{proxy_address};
|
||||||
|
boost::string_ref proxy_host = proxy_port.substr(0, proxy_port.rfind(":"));
|
||||||
|
if (proxy_port.size() == proxy_host.size())
|
||||||
|
proxy_host = "127.0.0.1";
|
||||||
|
else
|
||||||
|
proxy_port = proxy_port.substr(proxy_host.size() + 1);
|
||||||
|
|
||||||
|
uint16_t port_value = 0;
|
||||||
|
THROW_WALLET_EXCEPTION_IF(
|
||||||
|
!epee::string_tools::get_xtype_from_string(port_value, std::string{proxy_port}),
|
||||||
|
tools::error::wallet_internal_error,
|
||||||
|
std::string{"Invalid port specified for --"} + opts.proxy.name
|
||||||
|
);
|
||||||
|
|
||||||
|
boost::system::error_code error{};
|
||||||
|
proxy = ip::tcp::endpoint{ip::address::from_string(std::string{proxy_host}, error), port_value};
|
||||||
|
THROW_WALLET_EXCEPTION_IF(bool(error), tools::error::wallet_internal_error, std::string{"Invalid IP address specified for --"} + opts.proxy.name);
|
||||||
|
}
|
||||||
|
|
||||||
boost::optional<bool> trusted_daemon;
|
boost::optional<bool> trusted_daemon;
|
||||||
if (!command_line::is_arg_defaulted(vm, opts.trusted_daemon) || !command_line::is_arg_defaulted(vm, opts.untrusted_daemon))
|
if (!command_line::is_arg_defaulted(vm, opts.trusted_daemon) || !command_line::is_arg_defaulted(vm, opts.untrusted_daemon))
|
||||||
trusted_daemon = command_line::get_arg(vm, opts.trusted_daemon) && !command_line::get_arg(vm, opts.untrusted_daemon);
|
trusted_daemon = command_line::get_arg(vm, opts.trusted_daemon) && !command_line::get_arg(vm, opts.untrusted_daemon);
|
||||||
|
@ -388,8 +431,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
|
||||||
std::transform(daemon_ssl_allowed_fingerprints.begin(), daemon_ssl_allowed_fingerprints.end(), ssl_allowed_fingerprints.begin(), epee::from_hex::vector);
|
std::transform(daemon_ssl_allowed_fingerprints.begin(), daemon_ssl_allowed_fingerprints.end(), ssl_allowed_fingerprints.begin(), epee::from_hex::vector);
|
||||||
|
|
||||||
std::unique_ptr<tools::wallet2> wallet(new tools::wallet2(nettype, kdf_rounds, unattended));
|
std::unique_ptr<tools::wallet2> wallet(new tools::wallet2(nettype, kdf_rounds, unattended));
|
||||||
wallet->init(std::move(daemon_address), std::move(login), 0, *trusted_daemon, ssl_support, std::make_pair(daemon_ssl_private_key, daemon_ssl_certificate), ssl_allowed_certificates, ssl_allowed_fingerprints, daemon_ssl_allow_any_cert);
|
wallet->init(std::move(daemon_address), std::move(login), std::move(proxy), 0, *trusted_daemon, ssl_support, std::make_pair(daemon_ssl_private_key, daemon_ssl_certificate), ssl_allowed_certificates, ssl_allowed_fingerprints, daemon_ssl_allow_any_cert);
|
||||||
|
|
||||||
boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir);
|
boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir);
|
||||||
wallet->set_ring_database(ringdb_path.string());
|
wallet->set_ring_database(ringdb_path.string());
|
||||||
wallet->get_message_store().set_options(vm);
|
wallet->get_message_store().set_options(vm);
|
||||||
|
@ -1046,6 +1088,7 @@ void wallet2::init_options(boost::program_options::options_description& desc_par
|
||||||
const options opts{};
|
const options opts{};
|
||||||
command_line::add_arg(desc_params, opts.daemon_address);
|
command_line::add_arg(desc_params, opts.daemon_address);
|
||||||
command_line::add_arg(desc_params, opts.daemon_host);
|
command_line::add_arg(desc_params, opts.daemon_host);
|
||||||
|
command_line::add_arg(desc_params, opts.proxy);
|
||||||
command_line::add_arg(desc_params, opts.trusted_daemon);
|
command_line::add_arg(desc_params, opts.trusted_daemon);
|
||||||
command_line::add_arg(desc_params, opts.untrusted_daemon);
|
command_line::add_arg(desc_params, opts.untrusted_daemon);
|
||||||
command_line::add_arg(desc_params, opts.password);
|
command_line::add_arg(desc_params, opts.password);
|
||||||
|
@ -1109,7 +1152,7 @@ std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::varia
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, uint64_t upper_transaction_weight_limit, bool trusted_daemon, epee::net_utils::ssl_support_t ssl_support, const std::pair<std::string, std::string> &private_key_and_certificate_path, const std::list<std::string> &allowed_certificates, const std::vector<std::vector<uint8_t>> &allowed_fingerprints, bool allow_any_cert)
|
bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, boost::asio::ip::tcp::endpoint proxy, uint64_t upper_transaction_weight_limit, bool trusted_daemon, epee::net_utils::ssl_support_t ssl_support, const std::pair<std::string, std::string> &private_key_and_certificate_path, const std::list<std::string> &allowed_certificates, const std::vector<std::vector<uint8_t>> &allowed_fingerprints, bool allow_any_cert)
|
||||||
{
|
{
|
||||||
m_checkpoints.init_default_checkpoints(m_nettype);
|
m_checkpoints.init_default_checkpoints(m_nettype);
|
||||||
if(m_http_client.is_connected())
|
if(m_http_client.is_connected())
|
||||||
|
@ -1119,6 +1162,10 @@ bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::
|
||||||
m_daemon_address = std::move(daemon_address);
|
m_daemon_address = std::move(daemon_address);
|
||||||
m_daemon_login = std::move(daemon_login);
|
m_daemon_login = std::move(daemon_login);
|
||||||
m_trusted_daemon = trusted_daemon;
|
m_trusted_daemon = trusted_daemon;
|
||||||
|
if (proxy != boost::asio::ip::tcp::endpoint{})
|
||||||
|
m_http_client.set_connector(net::socks::connector{std::move(proxy)});
|
||||||
|
|
||||||
|
// When switching from light wallet to full wallet, we need to reset the height we got from lw node.
|
||||||
return m_http_client.set_server(get_daemon_address(), get_daemon_login(), ssl_support, private_key_and_certificate_path, allowed_certificates, allowed_fingerprints, allow_any_cert);
|
return m_http_client.set_server(get_daemon_address(), get_daemon_login(), ssl_support, private_key_and_certificate_path, allowed_certificates, allowed_fingerprints, allow_any_cert);
|
||||||
}
|
}
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
|
|
@ -680,7 +680,9 @@ namespace tools
|
||||||
|
|
||||||
bool deinit();
|
bool deinit();
|
||||||
bool init(std::string daemon_address = "http://localhost:8080",
|
bool init(std::string daemon_address = "http://localhost:8080",
|
||||||
boost::optional<epee::net_utils::http::login> daemon_login = boost::none, uint64_t upper_transaction_weight_limit = 0,
|
boost::optional<epee::net_utils::http::login> daemon_login = boost::none,
|
||||||
|
boost::asio::ip::tcp::endpoint proxy = {},
|
||||||
|
uint64_t upper_transaction_weight_limit = 0,
|
||||||
bool trusted_daemon = true,
|
bool trusted_daemon = true,
|
||||||
epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect,
|
epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect,
|
||||||
const std::pair<std::string, std::string> &private_key_and_certificate_path = {},
|
const std::pair<std::string, std::string> &private_key_and_certificate_path = {},
|
||||||
|
|
|
@ -53,7 +53,7 @@ int ColdOutputsFuzzer::init()
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
wallet.init("", boost::none, 0, true, epee::net_utils::ssl_support_t::e_ssl_support_disabled);
|
wallet.init("", boost::none, boost::asio::ip::tcp::endpoint{}, 0, true, epee::net_utils::ssl_support_t::e_ssl_support_disabled);
|
||||||
wallet.set_subaddress_lookahead(1, 1);
|
wallet.set_subaddress_lookahead(1, 1);
|
||||||
wallet.generate("", "", spendkey, true, false);
|
wallet.generate("", "", spendkey, true, false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ int ColdTransactionFuzzer::init()
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
wallet.init("", boost::none, 0, true, epee::net_utils::ssl_support_t::e_ssl_support_disabled);
|
wallet.init("", boost::none, boost::asio::ip::tcp::endpoint{}, 0, true, epee::net_utils::ssl_support_t::e_ssl_support_disabled);
|
||||||
wallet.set_subaddress_lookahead(1, 1);
|
wallet.set_subaddress_lookahead(1, 1);
|
||||||
wallet.generate("", "", spendkey, true, false);
|
wallet.generate("", "", spendkey, true, false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ int SignatureFuzzer::init()
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
wallet.init("", boost::none, 0, true, epee::net_utils::ssl_support_t::e_ssl_support_disabled);
|
wallet.init("", boost::none, boost::asio::ip::tcp::endpoint{}, 0, true, epee::net_utils::ssl_support_t::e_ssl_support_disabled);
|
||||||
wallet.set_subaddress_lookahead(1, 1);
|
wallet.set_subaddress_lookahead(1, 1);
|
||||||
wallet.generate("", "", spendkey, true, false);
|
wallet.generate("", "", spendkey, true, false);
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,7 @@ static void make_wallet(unsigned int idx, tools::wallet2 &wallet)
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
wallet.init("", boost::none, 0, true, epee::net_utils::ssl_support_t::e_ssl_support_disabled);
|
wallet.init("", boost::none, boost::asio::ip::tcp::endpoint{}, 0, true, epee::net_utils::ssl_support_t::e_ssl_support_disabled);
|
||||||
wallet.set_subaddress_lookahead(1, 1);
|
wallet.set_subaddress_lookahead(1, 1);
|
||||||
wallet.generate("", "", spendkey, true, false);
|
wallet.generate("", "", spendkey, true, false);
|
||||||
ASSERT_TRUE(test_addresses[idx].address == wallet.get_account().get_public_address_str(cryptonote::TESTNET));
|
ASSERT_TRUE(test_addresses[idx].address == wallet.get_account().get_public_address_str(cryptonote::TESTNET));
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include <boost/asio/io_service.hpp>
|
#include <boost/asio/io_service.hpp>
|
||||||
#include <boost/asio/ip/tcp.hpp>
|
#include <boost/asio/ip/tcp.hpp>
|
||||||
#include <boost/asio/read.hpp>
|
#include <boost/asio/read.hpp>
|
||||||
|
#include <boost/asio/steady_timer.hpp>
|
||||||
#include <boost/asio/write.hpp>
|
#include <boost/asio/write.hpp>
|
||||||
#include <boost/endian/conversion.hpp>
|
#include <boost/endian/conversion.hpp>
|
||||||
#include <boost/system/error_code.hpp>
|
#include <boost/system/error_code.hpp>
|
||||||
|
@ -45,6 +46,7 @@
|
||||||
#include "net/error.h"
|
#include "net/error.h"
|
||||||
#include "net/net_utils_base.h"
|
#include "net/net_utils_base.h"
|
||||||
#include "net/socks.h"
|
#include "net/socks.h"
|
||||||
|
#include "net/socks_connect.h"
|
||||||
#include "net/parse.h"
|
#include "net/parse.h"
|
||||||
#include "net/tor_address.h"
|
#include "net/tor_address.h"
|
||||||
#include "p2p/net_peerlist_boost_serialization.h"
|
#include "p2p/net_peerlist_boost_serialization.h"
|
||||||
|
@ -742,4 +744,92 @@ TEST(socks_client, resolve_command)
|
||||||
while (test_client->called_ == 1);
|
while (test_client->called_ == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(socks_connector, host)
|
||||||
|
{
|
||||||
|
io_thread io{};
|
||||||
|
boost::asio::steady_timer timeout{io.io_service};
|
||||||
|
timeout.expires_from_now(std::chrono::seconds{5});
|
||||||
|
|
||||||
|
boost::unique_future<boost::asio::ip::tcp::socket> sock =
|
||||||
|
net::socks::connector{io.acceptor.local_endpoint()}("example.com", "8080", timeout);
|
||||||
|
|
||||||
|
while (!io.connected);
|
||||||
|
const std::uint8_t expected_bytes[] = {
|
||||||
|
4, 1, 0x1f, 0x90, 0x00, 0x00, 0x00, 0x01, 0x00,
|
||||||
|
'e', 'x', 'a', 'm', 'p', 'l', 'e', '.', 'c', 'o', 'm', 0x00
|
||||||
|
};
|
||||||
|
|
||||||
|
std::uint8_t actual_bytes[sizeof(expected_bytes)];
|
||||||
|
boost::asio::read(io.server, boost::asio::buffer(actual_bytes));
|
||||||
|
EXPECT_TRUE(std::memcmp(expected_bytes, actual_bytes, sizeof(actual_bytes)) == 0);
|
||||||
|
|
||||||
|
const std::uint8_t reply_bytes[] = {0, 90, 0, 0, 0, 0, 0, 0};
|
||||||
|
boost::asio::write(io.server, boost::asio::buffer(reply_bytes));
|
||||||
|
|
||||||
|
ASSERT_EQ(boost::future_status::ready, sock.wait_for(boost::chrono::seconds{3}));
|
||||||
|
EXPECT_TRUE(sock.get().is_open());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(socks_connector, ipv4)
|
||||||
|
{
|
||||||
|
io_thread io{};
|
||||||
|
boost::asio::steady_timer timeout{io.io_service};
|
||||||
|
timeout.expires_from_now(std::chrono::seconds{5});
|
||||||
|
|
||||||
|
boost::unique_future<boost::asio::ip::tcp::socket> sock =
|
||||||
|
net::socks::connector{io.acceptor.local_endpoint()}("250.88.125.99", "8080", timeout);
|
||||||
|
|
||||||
|
while (!io.connected);
|
||||||
|
const std::uint8_t expected_bytes[] = {
|
||||||
|
4, 1, 0x1f, 0x90, 0xfa, 0x58, 0x7d, 0x63, 0x00
|
||||||
|
};
|
||||||
|
|
||||||
|
std::uint8_t actual_bytes[sizeof(expected_bytes)];
|
||||||
|
boost::asio::read(io.server, boost::asio::buffer(actual_bytes));
|
||||||
|
EXPECT_TRUE(std::memcmp(expected_bytes, actual_bytes, sizeof(actual_bytes)) == 0);
|
||||||
|
|
||||||
|
const std::uint8_t reply_bytes[] = {0, 90, 0, 0, 0, 0, 0, 0};
|
||||||
|
boost::asio::write(io.server, boost::asio::buffer(reply_bytes));
|
||||||
|
|
||||||
|
ASSERT_EQ(boost::future_status::ready, sock.wait_for(boost::chrono::seconds{3}));
|
||||||
|
EXPECT_TRUE(sock.get().is_open());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(socks_connector, error)
|
||||||
|
{
|
||||||
|
io_thread io{};
|
||||||
|
boost::asio::steady_timer timeout{io.io_service};
|
||||||
|
timeout.expires_from_now(std::chrono::seconds{5});
|
||||||
|
|
||||||
|
boost::unique_future<boost::asio::ip::tcp::socket> sock =
|
||||||
|
net::socks::connector{io.acceptor.local_endpoint()}("250.88.125.99", "8080", timeout);
|
||||||
|
|
||||||
|
while (!io.connected);
|
||||||
|
const std::uint8_t expected_bytes[] = {
|
||||||
|
4, 1, 0x1f, 0x90, 0xfa, 0x58, 0x7d, 0x63, 0x00
|
||||||
|
};
|
||||||
|
|
||||||
|
std::uint8_t actual_bytes[sizeof(expected_bytes)];
|
||||||
|
boost::asio::read(io.server, boost::asio::buffer(actual_bytes));
|
||||||
|
EXPECT_TRUE(std::memcmp(expected_bytes, actual_bytes, sizeof(actual_bytes)) == 0);
|
||||||
|
|
||||||
|
const std::uint8_t reply_bytes[] = {0, 91, 0, 0, 0, 0, 0, 0};
|
||||||
|
boost::asio::write(io.server, boost::asio::buffer(reply_bytes));
|
||||||
|
|
||||||
|
ASSERT_EQ(boost::future_status::ready, sock.wait_for(boost::chrono::seconds{3}));
|
||||||
|
EXPECT_THROW(sock.get().is_open(), boost::system::system_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(socks_connector, timeout)
|
||||||
|
{
|
||||||
|
io_thread io{};
|
||||||
|
boost::asio::steady_timer timeout{io.io_service};
|
||||||
|
timeout.expires_from_now(std::chrono::milliseconds{10});
|
||||||
|
|
||||||
|
boost::unique_future<boost::asio::ip::tcp::socket> sock =
|
||||||
|
net::socks::connector{io.acceptor.local_endpoint()}("250.88.125.99", "8080", timeout);
|
||||||
|
|
||||||
|
ASSERT_EQ(boost::future_status::ready, sock.wait_for(boost::chrono::seconds{3}));
|
||||||
|
EXPECT_THROW(sock.get().is_open(), boost::system::system_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue