mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-17 13:07:54 +02:00
Merge 0ed2c130e7 into merged_master (Bitcoin PR bitcoin/bitcoin#27375)
This commit is contained in:
commit
e893b7e6f9
17 changed files with 401 additions and 142 deletions
15
configure.ac
15
configure.ac
|
|
@ -1211,10 +1211,23 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
|
|||
]], [[
|
||||
getauxval(AT_HWCAP);
|
||||
]])],
|
||||
[ AC_MSG_RESULT([yes]); HAVE_STRONG_GETAUXVAL=1; AC_DEFINE([HAVE_STRONG_GETAUXVAL], [1], [Define this symbol to build code that uses getauxval)]) ],
|
||||
[ AC_MSG_RESULT([yes]); HAVE_STRONG_GETAUXVAL=1; AC_DEFINE([HAVE_STRONG_GETAUXVAL], [1], [Define this symbol to build code that uses getauxval]) ],
|
||||
[ AC_MSG_RESULT([no]); HAVE_STRONG_GETAUXVAL=0 ]
|
||||
)
|
||||
|
||||
# Check for UNIX sockets
|
||||
AC_MSG_CHECKING(for sockaddr_un)
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
]], [[
|
||||
struct sockaddr_un addr;
|
||||
addr.sun_family = AF_UNIX;
|
||||
]])],
|
||||
[ AC_MSG_RESULT([yes]); AC_DEFINE([HAVE_SOCKADDR_UN], [1], [Define this symbol if platform supports unix domain sockets]) ],
|
||||
[ AC_MSG_RESULT([no]); ]
|
||||
)
|
||||
|
||||
have_any_system=no
|
||||
AC_MSG_CHECKING([for std::system])
|
||||
AC_LINK_IFELSE(
|
||||
|
|
|
|||
6
doc/release-notes-27375.md
Normal file
6
doc/release-notes-27375.md
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
P2P
|
||||
---
|
||||
|
||||
UNIX domain sockets can now be used for proxy connections. Set `-onion` or `-proxy`
|
||||
to the local socket path with the prefix `unix:` (e.g. `-onion=unix:/home/me/torsocket`).
|
||||
(#27375)
|
||||
|
|
@ -32,6 +32,13 @@
|
|||
#include <unistd.h> // IWYU pragma: export
|
||||
#endif
|
||||
|
||||
// Windows does not have `sa_family_t` - it defines `sockaddr::sa_family` as `u_short`.
|
||||
// Thus define `sa_family_t` on Windows too so that the rest of the code can use `sa_family_t`.
|
||||
// See https://learn.microsoft.com/en-us/windows/win32/api/winsock/ns-winsock-sockaddr#syntax
|
||||
#ifdef WIN32
|
||||
typedef u_short sa_family_t;
|
||||
#endif
|
||||
|
||||
// We map Linux / BSD error functions and codes, to the equivalent
|
||||
// Windows definitions, and use the WSA* names throughout our code.
|
||||
// Note that glibc defines EWOULDBLOCK as EAGAIN (see errno.h).
|
||||
|
|
|
|||
14
src/i2p.cpp
14
src/i2p.cpp
|
|
@ -115,7 +115,7 @@ static CNetAddr DestB64ToAddr(const std::string& dest)
|
|||
namespace sam {
|
||||
|
||||
Session::Session(const fs::path& private_key_file,
|
||||
const CService& control_host,
|
||||
const Proxy& control_host,
|
||||
CThreadInterrupt* interrupt)
|
||||
: m_private_key_file{private_key_file},
|
||||
m_control_host{control_host},
|
||||
|
|
@ -124,7 +124,7 @@ Session::Session(const fs::path& private_key_file,
|
|||
{
|
||||
}
|
||||
|
||||
Session::Session(const CService& control_host, CThreadInterrupt* interrupt)
|
||||
Session::Session(const Proxy& control_host, CThreadInterrupt* interrupt)
|
||||
: m_control_host{control_host},
|
||||
m_interrupt{interrupt},
|
||||
m_transient{true}
|
||||
|
|
@ -327,14 +327,10 @@ Session::Reply Session::SendRequestAndGetReply(const Sock& sock,
|
|||
|
||||
std::unique_ptr<Sock> Session::Hello() const
|
||||
{
|
||||
auto sock = CreateSock(m_control_host);
|
||||
auto sock = m_control_host.Connect();
|
||||
|
||||
if (!sock) {
|
||||
throw std::runtime_error("Cannot create socket");
|
||||
}
|
||||
|
||||
if (!ConnectSocketDirectly(m_control_host, *sock, nConnectTimeout, true)) {
|
||||
throw std::runtime_error(strprintf("Cannot connect to %s", m_control_host.ToStringAddrPort()));
|
||||
throw std::runtime_error(strprintf("Cannot connect to %s", m_control_host.ToString()));
|
||||
}
|
||||
|
||||
SendRequestAndGetReply(*sock, "HELLO VERSION MIN=3.1 MAX=3.1");
|
||||
|
|
@ -418,7 +414,7 @@ void Session::CreateIfNotCreatedAlready()
|
|||
const auto session_type = m_transient ? "transient" : "persistent";
|
||||
const auto session_id = GetRandHash().GetHex().substr(0, 10); // full is overkill, too verbose in the logs
|
||||
|
||||
Log("Creating %s SAM session %s with %s", session_type, session_id, m_control_host.ToStringAddrPort());
|
||||
Log("Creating %s SAM session %s with %s", session_type, session_id, m_control_host.ToString());
|
||||
|
||||
auto sock = Hello();
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include <compat/compat.h>
|
||||
#include <netaddress.h>
|
||||
#include <netbase.h>
|
||||
#include <sync.h>
|
||||
#include <util/fs.h>
|
||||
#include <util/sock.h>
|
||||
|
|
@ -67,7 +68,7 @@ public:
|
|||
* `Session` object.
|
||||
*/
|
||||
Session(const fs::path& private_key_file,
|
||||
const CService& control_host,
|
||||
const Proxy& control_host,
|
||||
CThreadInterrupt* interrupt);
|
||||
|
||||
/**
|
||||
|
|
@ -81,7 +82,7 @@ public:
|
|||
* `CThreadInterrupt` object is saved, so it must not be destroyed earlier than this
|
||||
* `Session` object.
|
||||
*/
|
||||
Session(const CService& control_host, CThreadInterrupt* interrupt);
|
||||
Session(const Proxy& control_host, CThreadInterrupt* interrupt);
|
||||
|
||||
/**
|
||||
* Destroy the session, closing the internally used sockets. The sockets that have been
|
||||
|
|
@ -235,9 +236,9 @@ private:
|
|||
const fs::path m_private_key_file;
|
||||
|
||||
/**
|
||||
* The host and port of the SAM control service.
|
||||
* The SAM control service proxy.
|
||||
*/
|
||||
const CService m_control_host;
|
||||
const Proxy m_control_host;
|
||||
|
||||
/**
|
||||
* Cease network activity when this is signaled.
|
||||
|
|
|
|||
42
src/init.cpp
42
src/init.cpp
|
|
@ -547,7 +547,11 @@ void SetupServerArgs(ArgsManager& argsman)
|
|||
argsman.AddArg("-maxsendbuffer=<n>", strprintf("Maximum per-connection memory usage for the send buffer, <n>*1000 bytes (default: %u)", DEFAULT_MAXSENDBUFFER), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||
argsman.AddArg("-maxtimeadjustment", strprintf("Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by outbound peers forward or backward by this amount (default: %u seconds).", DEFAULT_MAX_TIME_ADJUSTMENT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||
argsman.AddArg("-maxuploadtarget=<n>", strprintf("Tries to keep outbound traffic under the given target per 24h. Limit does not apply to peers with 'download' permission or blocks created within past week. 0 = no limit (default: %s). Optional suffix units [k|K|m|M|g|G|t|T] (default: M). Lowercase is 1000 base while uppercase is 1024 base", DEFAULT_MAX_UPLOAD_TARGET), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||
#if HAVE_SOCKADDR_UN
|
||||
argsman.AddArg("-onion=<ip:port|path>", "Use separate SOCKS5 proxy to reach peers via Tor onion services, set -noonion to disable (default: -proxy). May be a local file path prefixed with 'unix:'.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||
#else
|
||||
argsman.AddArg("-onion=<ip:port>", "Use separate SOCKS5 proxy to reach peers via Tor onion services, set -noonion to disable (default: -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||
#endif
|
||||
argsman.AddArg("-i2psam=<ip:port>", "I2P SAM proxy to reach I2P peers and accept I2P connections (default: none)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||
argsman.AddArg("-i2pacceptincoming", strprintf("Whether to accept inbound I2P connections (default: %i). Ignored if -i2psam is not set. Listening for inbound I2P connections is done through the SAM proxy, not by binding to a local address and port.", DEFAULT_I2P_ACCEPT_INCOMING), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||
argsman.AddArg("-onlynet=<net>", "Make automatic outbound connections only to network <net> (" + Join(GetNetworkNames(), ", ") + "). Inbound and manual connections are not affected by this option. It can be specified multiple times to allow multiple networks.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||
|
|
@ -558,7 +562,11 @@ void SetupServerArgs(ArgsManager& argsman)
|
|||
// TODO: remove the sentence "Nodes not using ... incoming connections." once the changes from
|
||||
// https://github.com/bitcoin/bitcoin/pull/23542 have become widespread.
|
||||
argsman.AddArg("-port=<port>", strprintf("Listen for connections on <port>. Nodes not using the default ports (default: %u, testnet: %u, signet: %u, regtest: %u) are unlikely to get incoming connections. Not relevant for I2P (see doc/i2p.md).", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort(), signetChainParams->GetDefaultPort(), regtestChainParams->GetDefaultPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
|
||||
#if HAVE_SOCKADDR_UN
|
||||
argsman.AddArg("-proxy=<ip:port|path>", "Connect through SOCKS5 proxy, set -noproxy to disable (default: disabled). May be a local file path prefixed with 'unix:' if the proxy supports it.", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_ELISION, OptionsCategory::CONNECTION);
|
||||
#else
|
||||
argsman.AddArg("-proxy=<ip:port>", "Connect through SOCKS5 proxy, set -noproxy to disable (default: disabled)", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_ELISION, OptionsCategory::CONNECTION);
|
||||
#endif
|
||||
argsman.AddArg("-proxyrandomize", strprintf("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)", DEFAULT_PROXYRANDOMIZE), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||
argsman.AddArg("-seednode=<ip>", "Connect to a node to retrieve peer addresses, and disconnect. This option can be specified multiple times to connect to multiple nodes.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||
argsman.AddArg("-networkactive", "Enable all P2P network activity (default: 1). Can be changed by the setnetworkactive RPC command", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||
|
|
@ -1469,7 +1477,14 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|||
std::string host_out;
|
||||
uint16_t port_out{0};
|
||||
if (!SplitHostPort(socket_addr, port_out, host_out)) {
|
||||
#if HAVE_SOCKADDR_UN
|
||||
// Allow unix domain sockets for -proxy and -onion e.g. unix:/some/file/path
|
||||
if ((port_option != "-proxy" && port_option != "-onion") || socket_addr.find(ADDR_PREFIX_UNIX) != 0) {
|
||||
return InitError(InvalidPortErrMsg(port_option, socket_addr));
|
||||
}
|
||||
#else
|
||||
return InitError(InvalidPortErrMsg(port_option, socket_addr));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1536,12 +1551,18 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|||
// -noproxy (or -proxy=0) as well as the empty string can be used to not set a proxy, this is the default
|
||||
std::string proxyArg = args.GetArg("-proxy", "");
|
||||
if (proxyArg != "" && proxyArg != "0") {
|
||||
const std::optional<CService> proxyAddr{Lookup(proxyArg, 9050, fNameLookup)};
|
||||
if (!proxyAddr.has_value()) {
|
||||
return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'"), proxyArg));
|
||||
Proxy addrProxy;
|
||||
if (IsUnixSocketPath(proxyArg)) {
|
||||
addrProxy = Proxy(proxyArg, proxyRandomize);
|
||||
} else {
|
||||
const std::optional<CService> proxyAddr{Lookup(proxyArg, 9050, fNameLookup)};
|
||||
if (!proxyAddr.has_value()) {
|
||||
return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'"), proxyArg));
|
||||
}
|
||||
|
||||
addrProxy = Proxy(proxyAddr.value(), proxyRandomize);
|
||||
}
|
||||
|
||||
Proxy addrProxy = Proxy(proxyAddr.value(), proxyRandomize);
|
||||
if (!addrProxy.IsValid())
|
||||
return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'"), proxyArg));
|
||||
|
||||
|
|
@ -1567,11 +1588,16 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|||
"reaching the Tor network is explicitly forbidden: -onion=0"));
|
||||
}
|
||||
} else {
|
||||
const std::optional<CService> addr{Lookup(onionArg, 9050, fNameLookup)};
|
||||
if (!addr.has_value() || !addr->IsValid()) {
|
||||
return InitError(strprintf(_("Invalid -onion address or hostname: '%s'"), onionArg));
|
||||
if (IsUnixSocketPath(onionArg)) {
|
||||
onion_proxy = Proxy(onionArg, proxyRandomize);
|
||||
} else {
|
||||
const std::optional<CService> addr{Lookup(onionArg, 9050, fNameLookup)};
|
||||
if (!addr.has_value() || !addr->IsValid()) {
|
||||
return InitError(strprintf(_("Invalid -onion address or hostname: '%s'"), onionArg));
|
||||
}
|
||||
|
||||
onion_proxy = Proxy(addr.value(), proxyRandomize);
|
||||
}
|
||||
onion_proxy = Proxy{addr.value(), proxyRandomize};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
32
src/net.cpp
32
src/net.cpp
|
|
@ -437,7 +437,6 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
|||
}
|
||||
|
||||
// Connect
|
||||
bool connected = false;
|
||||
std::unique_ptr<Sock> sock;
|
||||
Proxy proxy;
|
||||
CAddress addr_bind;
|
||||
|
|
@ -450,6 +449,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
|||
|
||||
if (addrConnect.IsI2P() && use_proxy) {
|
||||
i2p::Connection conn;
|
||||
bool connected{false};
|
||||
|
||||
if (m_i2p_sam_session) {
|
||||
connected = m_i2p_sam_session->Connect(addrConnect, conn, proxyConnectionFailed);
|
||||
|
|
@ -458,7 +458,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
|||
LOCK(m_unused_i2p_sessions_mutex);
|
||||
if (m_unused_i2p_sessions.empty()) {
|
||||
i2p_transient_session =
|
||||
std::make_unique<i2p::sam::Session>(proxy.proxy, &interruptNet);
|
||||
std::make_unique<i2p::sam::Session>(proxy, &interruptNet);
|
||||
} else {
|
||||
i2p_transient_session.swap(m_unused_i2p_sessions.front());
|
||||
m_unused_i2p_sessions.pop();
|
||||
|
|
@ -478,20 +478,11 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
|||
addr_bind = CAddress{conn.me, NODE_NONE};
|
||||
}
|
||||
} else if (use_proxy) {
|
||||
sock = CreateSock(proxy.proxy);
|
||||
if (!sock) {
|
||||
return nullptr;
|
||||
}
|
||||
connected = ConnectThroughProxy(proxy, addrConnect.ToStringAddr(), addrConnect.GetPort(),
|
||||
*sock, nConnectTimeout, proxyConnectionFailed);
|
||||
LogPrintLevel(BCLog::PROXY, BCLog::Level::Debug, "Using proxy: %s to connect to %s:%s\n", proxy.ToString(), addrConnect.ToStringAddr(), addrConnect.GetPort());
|
||||
sock = ConnectThroughProxy(proxy, addrConnect.ToStringAddr(), addrConnect.GetPort(), proxyConnectionFailed);
|
||||
} else {
|
||||
// no proxy needed (none set for target network)
|
||||
sock = CreateSock(addrConnect);
|
||||
if (!sock) {
|
||||
return nullptr;
|
||||
}
|
||||
connected = ConnectSocketDirectly(addrConnect, *sock, nConnectTimeout,
|
||||
conn_type == ConnectionType::MANUAL);
|
||||
sock = ConnectDirectly(addrConnect, conn_type == ConnectionType::MANUAL);
|
||||
}
|
||||
if (!proxyConnectionFailed) {
|
||||
// If a connection to the node was attempted, and failure (if any) is not caused by a problem connecting to
|
||||
|
|
@ -499,18 +490,13 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
|||
addrman.Attempt(addrConnect, fCountFailure);
|
||||
}
|
||||
} else if (pszDest && GetNameProxy(proxy)) {
|
||||
sock = CreateSock(proxy.proxy);
|
||||
if (!sock) {
|
||||
return nullptr;
|
||||
}
|
||||
std::string host;
|
||||
uint16_t port{default_port};
|
||||
SplitHostPort(std::string(pszDest), port, host);
|
||||
bool proxyConnectionFailed;
|
||||
connected = ConnectThroughProxy(proxy, host, port, *sock, nConnectTimeout,
|
||||
proxyConnectionFailed);
|
||||
sock = ConnectThroughProxy(proxy, host, port, proxyConnectionFailed);
|
||||
}
|
||||
if (!connected) {
|
||||
if (!sock) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
@ -2990,7 +2976,7 @@ bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError,
|
|||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<Sock> sock = CreateSock(addrBind);
|
||||
std::unique_ptr<Sock> sock = CreateSock(addrBind.GetSAFamily());
|
||||
if (!sock) {
|
||||
strError = strprintf(Untranslated("Couldn't open socket for incoming connections (socket returned error %s)"), NetworkErrorString(WSAGetLastError()));
|
||||
LogPrintLevel(BCLog::NET, BCLog::Level::Error, "%s\n", strError.original);
|
||||
|
|
@ -3197,7 +3183,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
|
|||
Proxy i2p_sam;
|
||||
if (GetProxy(NET_I2P, i2p_sam) && connOptions.m_i2p_accept_incoming) {
|
||||
m_i2p_sam_session = std::make_unique<i2p::sam::Session>(gArgs.GetDataDirNet() / "i2p_private_key",
|
||||
i2p_sam.proxy, &interruptNet);
|
||||
i2p_sam, &interruptNet);
|
||||
}
|
||||
|
||||
for (const auto& strDest : connOptions.vSeedNodes) {
|
||||
|
|
|
|||
|
|
@ -818,6 +818,19 @@ bool CService::SetSockAddr(const struct sockaddr *paddr)
|
|||
}
|
||||
}
|
||||
|
||||
sa_family_t CService::GetSAFamily() const
|
||||
{
|
||||
switch (m_net) {
|
||||
case NET_IPV4:
|
||||
return AF_INET;
|
||||
case NET_IPV6:
|
||||
case NET_CJDNS:
|
||||
return AF_INET6;
|
||||
default:
|
||||
return AF_UNSPEC;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t CService::GetPort() const
|
||||
{
|
||||
return port;
|
||||
|
|
|
|||
|
|
@ -540,6 +540,11 @@ public:
|
|||
uint16_t GetPort() const;
|
||||
bool GetSockAddr(struct sockaddr* paddr, socklen_t* addrlen) const;
|
||||
bool SetSockAddr(const struct sockaddr* paddr);
|
||||
/**
|
||||
* Get the address family
|
||||
* @returns AF_UNSPEC if unspecified
|
||||
*/
|
||||
[[nodiscard]] sa_family_t GetSAFamily() const;
|
||||
friend bool operator==(const CService& a, const CService& b);
|
||||
friend bool operator!=(const CService& a, const CService& b) { return !(a == b); }
|
||||
friend bool operator<(const CService& a, const CService& b);
|
||||
|
|
|
|||
175
src/netbase.cpp
175
src/netbase.cpp
|
|
@ -3,6 +3,10 @@
|
|||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#if defined(HAVE_CONFIG_H)
|
||||
#include <config/bitcoin-config.h>
|
||||
#endif
|
||||
|
||||
#include <netbase.h>
|
||||
|
||||
#include <compat/compat.h>
|
||||
|
|
@ -21,6 +25,10 @@
|
|||
#include <limits>
|
||||
#include <memory>
|
||||
|
||||
#if HAVE_SOCKADDR_UN
|
||||
#include <sys/un.h>
|
||||
#endif
|
||||
|
||||
// Settings
|
||||
static GlobalMutex g_proxyinfo_mutex;
|
||||
static Proxy proxyInfo[NET_MAX] GUARDED_BY(g_proxyinfo_mutex);
|
||||
|
|
@ -208,6 +216,24 @@ CService LookupNumeric(const std::string& name, uint16_t portDefault, DNSLookupF
|
|||
return Lookup(name, portDefault, /*fAllowLookup=*/false, dns_lookup_function).value_or(CService{});
|
||||
}
|
||||
|
||||
bool IsUnixSocketPath(const std::string& name)
|
||||
{
|
||||
#if HAVE_SOCKADDR_UN
|
||||
if (name.find(ADDR_PREFIX_UNIX) != 0) return false;
|
||||
|
||||
// Split off "unix:" prefix
|
||||
std::string str{name.substr(ADDR_PREFIX_UNIX.length())};
|
||||
|
||||
// Path size limit is platform-dependent
|
||||
// see https://manpages.ubuntu.com/manpages/xenial/en/man7/unix.7.html
|
||||
if (str.size() + 1 > sizeof(((sockaddr_un*)nullptr)->sun_path)) return false;
|
||||
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
/** SOCKS version */
|
||||
enum SOCKSVersion: uint8_t {
|
||||
SOCKS4 = 0x04,
|
||||
|
|
@ -461,18 +487,18 @@ bool Socks5(const std::string& strDest, uint16_t port, const ProxyCredentials* a
|
|||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Sock> CreateSockTCP(const CService& address_family)
|
||||
std::unique_ptr<Sock> CreateSockOS(sa_family_t address_family)
|
||||
{
|
||||
// Create a sockaddr from the specified service.
|
||||
struct sockaddr_storage sockaddr;
|
||||
socklen_t len = sizeof(sockaddr);
|
||||
if (!address_family.GetSockAddr((struct sockaddr*)&sockaddr, &len)) {
|
||||
LogPrintf("Cannot create socket for %s: unsupported network\n", address_family.ToStringAddrPort());
|
||||
return nullptr;
|
||||
}
|
||||
// Not IPv4, IPv6 or UNIX
|
||||
if (address_family == AF_UNSPEC) return nullptr;
|
||||
|
||||
// Create a TCP socket in the address family of the specified service.
|
||||
SOCKET hSocket = socket(((struct sockaddr*)&sockaddr)->sa_family, SOCK_STREAM, IPPROTO_TCP);
|
||||
int protocol{IPPROTO_TCP};
|
||||
#if HAVE_SOCKADDR_UN
|
||||
if (address_family == AF_UNIX) protocol = 0;
|
||||
#endif
|
||||
|
||||
// Create a socket in the specified address family.
|
||||
SOCKET hSocket = socket(address_family, SOCK_STREAM, protocol);
|
||||
if (hSocket == INVALID_SOCKET) {
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -496,21 +522,25 @@ std::unique_ptr<Sock> CreateSockTCP(const CService& address_family)
|
|||
}
|
||||
#endif
|
||||
|
||||
// Set the no-delay option (disable Nagle's algorithm) on the TCP socket.
|
||||
const int on{1};
|
||||
if (sock->SetSockOpt(IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) == SOCKET_ERROR) {
|
||||
LogPrint(BCLog::NET, "Unable to set TCP_NODELAY on a newly created socket, continuing anyway\n");
|
||||
}
|
||||
|
||||
// Set the non-blocking option on the socket.
|
||||
if (!sock->SetNonBlocking()) {
|
||||
LogPrintf("Error setting socket to non-blocking: %s\n", NetworkErrorString(WSAGetLastError()));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#if HAVE_SOCKADDR_UN
|
||||
if (address_family == AF_UNIX) return sock;
|
||||
#endif
|
||||
|
||||
// Set the no-delay option (disable Nagle's algorithm) on the TCP socket.
|
||||
const int on{1};
|
||||
if (sock->SetSockOpt(IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) == SOCKET_ERROR) {
|
||||
LogPrint(BCLog::NET, "Unable to set TCP_NODELAY on a newly created socket, continuing anyway\n");
|
||||
}
|
||||
return sock;
|
||||
}
|
||||
|
||||
std::function<std::unique_ptr<Sock>(const CService&)> CreateSock = CreateSockTCP;
|
||||
std::function<std::unique_ptr<Sock>(const sa_family_t&)> CreateSock = CreateSockOS;
|
||||
|
||||
template<typename... Args>
|
||||
static void LogConnectFailure(bool manual_connection, const char* fmt, const Args&... args) {
|
||||
|
|
@ -522,18 +552,10 @@ static void LogConnectFailure(bool manual_connection, const char* fmt, const Arg
|
|||
}
|
||||
}
|
||||
|
||||
bool ConnectSocketDirectly(const CService &addrConnect, const Sock& sock, int nTimeout, bool manual_connection)
|
||||
static bool ConnectToSocket(const Sock& sock, struct sockaddr* sockaddr, socklen_t len, const std::string& dest_str, bool manual_connection)
|
||||
{
|
||||
// Create a sockaddr from the specified service.
|
||||
struct sockaddr_storage sockaddr;
|
||||
socklen_t len = sizeof(sockaddr);
|
||||
if (!addrConnect.GetSockAddr((struct sockaddr*)&sockaddr, &len)) {
|
||||
LogPrintf("Cannot connect to %s: unsupported network\n", addrConnect.ToStringAddrPort());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Connect to the addrConnect service on the hSocket socket.
|
||||
if (sock.Connect(reinterpret_cast<struct sockaddr*>(&sockaddr), len) == SOCKET_ERROR) {
|
||||
// Connect to `sockaddr` using `sock`.
|
||||
if (sock.Connect(sockaddr, len) == SOCKET_ERROR) {
|
||||
int nErr = WSAGetLastError();
|
||||
// WSAEINVAL is here because some legacy version of winsock uses it
|
||||
if (nErr == WSAEINPROGRESS || nErr == WSAEWOULDBLOCK || nErr == WSAEINVAL)
|
||||
|
|
@ -543,13 +565,13 @@ bool ConnectSocketDirectly(const CService &addrConnect, const Sock& sock, int nT
|
|||
// synchronously to check for successful connection with a timeout.
|
||||
const Sock::Event requested = Sock::RECV | Sock::SEND;
|
||||
Sock::Event occurred;
|
||||
if (!sock.Wait(std::chrono::milliseconds{nTimeout}, requested, &occurred)) {
|
||||
if (!sock.Wait(std::chrono::milliseconds{nConnectTimeout}, requested, &occurred)) {
|
||||
LogPrintf("wait for connect to %s failed: %s\n",
|
||||
addrConnect.ToStringAddrPort(),
|
||||
dest_str,
|
||||
NetworkErrorString(WSAGetLastError()));
|
||||
return false;
|
||||
} else if (occurred == 0) {
|
||||
LogPrint(BCLog::NET, "connection attempt to %s timed out\n", addrConnect.ToStringAddrPort());
|
||||
LogPrint(BCLog::NET, "connection attempt to %s timed out\n", dest_str);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -561,13 +583,13 @@ bool ConnectSocketDirectly(const CService &addrConnect, const Sock& sock, int nT
|
|||
socklen_t sockerr_len = sizeof(sockerr);
|
||||
if (sock.GetSockOpt(SOL_SOCKET, SO_ERROR, (sockopt_arg_type)&sockerr, &sockerr_len) ==
|
||||
SOCKET_ERROR) {
|
||||
LogPrintf("getsockopt() for %s failed: %s\n", addrConnect.ToStringAddrPort(), NetworkErrorString(WSAGetLastError()));
|
||||
LogPrintf("getsockopt() for %s failed: %s\n", dest_str, NetworkErrorString(WSAGetLastError()));
|
||||
return false;
|
||||
}
|
||||
if (sockerr != 0) {
|
||||
LogConnectFailure(manual_connection,
|
||||
"connect() to %s failed after wait: %s",
|
||||
addrConnect.ToStringAddrPort(),
|
||||
dest_str,
|
||||
NetworkErrorString(sockerr));
|
||||
return false;
|
||||
}
|
||||
|
|
@ -578,13 +600,73 @@ bool ConnectSocketDirectly(const CService &addrConnect, const Sock& sock, int nT
|
|||
else
|
||||
#endif
|
||||
{
|
||||
LogConnectFailure(manual_connection, "connect() to %s failed: %s", addrConnect.ToStringAddrPort(), NetworkErrorString(WSAGetLastError()));
|
||||
LogConnectFailure(manual_connection, "connect() to %s failed: %s", dest_str, NetworkErrorString(WSAGetLastError()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<Sock> ConnectDirectly(const CService& dest, bool manual_connection)
|
||||
{
|
||||
auto sock = CreateSock(dest.GetSAFamily());
|
||||
if (!sock) {
|
||||
LogPrintLevel(BCLog::NET, BCLog::Level::Error, "Cannot create a socket for connecting to %s\n", dest.ToStringAddrPort());
|
||||
return {};
|
||||
}
|
||||
|
||||
// Create a sockaddr from the specified service.
|
||||
struct sockaddr_storage sockaddr;
|
||||
socklen_t len = sizeof(sockaddr);
|
||||
if (!dest.GetSockAddr((struct sockaddr*)&sockaddr, &len)) {
|
||||
LogPrintf("Cannot get sockaddr for %s: unsupported network\n", dest.ToStringAddrPort());
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!ConnectToSocket(*sock, (struct sockaddr*)&sockaddr, len, dest.ToStringAddrPort(), manual_connection)) {
|
||||
LogPrintf("Cannot connect to socket for %s\n", dest.ToStringAddrPort());
|
||||
return {};
|
||||
}
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
std::unique_ptr<Sock> Proxy::Connect() const
|
||||
{
|
||||
if (!IsValid()) {
|
||||
LogPrintf("Cannot connect to invalid Proxy\n");
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!m_is_unix_socket) return ConnectDirectly(proxy, /*manual_connection=*/true);
|
||||
|
||||
#if HAVE_SOCKADDR_UN
|
||||
auto sock = CreateSock(AF_UNIX);
|
||||
if (!sock) {
|
||||
LogPrintLevel(BCLog::NET, BCLog::Level::Error, "Cannot create a socket for connecting to %s\n", m_unix_socket_path);
|
||||
return {};
|
||||
}
|
||||
|
||||
const std::string path{m_unix_socket_path.substr(ADDR_PREFIX_UNIX.length())};
|
||||
|
||||
struct sockaddr_un addrun;
|
||||
memset(&addrun, 0, sizeof(addrun));
|
||||
addrun.sun_family = AF_UNIX;
|
||||
// leave the last char in addrun.sun_path[] to be always '\0'
|
||||
memcpy(addrun.sun_path, path.c_str(), std::min(sizeof(addrun.sun_path) - 1, path.length()));
|
||||
socklen_t len = sizeof(addrun);
|
||||
|
||||
if(!ConnectToSocket(*sock, (struct sockaddr*)&addrun, len, path, /*manual_connection=*/true)) {
|
||||
LogPrintf("Cannot connect to socket for %s\n", path);
|
||||
return {};
|
||||
}
|
||||
|
||||
return sock;
|
||||
#else
|
||||
return {};
|
||||
#endif
|
||||
}
|
||||
|
||||
bool SetProxy(enum Network net, const Proxy &addrProxy) {
|
||||
assert(net >= 0 && net < NET_MAX);
|
||||
if (!addrProxy.IsValid())
|
||||
|
|
@ -633,27 +715,32 @@ bool IsProxy(const CNetAddr &addr) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool ConnectThroughProxy(const Proxy& proxy, const std::string& strDest, uint16_t port, const Sock& sock, int nTimeout, bool& outProxyConnectionFailed)
|
||||
std::unique_ptr<Sock> ConnectThroughProxy(const Proxy& proxy,
|
||||
const std::string& dest,
|
||||
uint16_t port,
|
||||
bool& proxy_connection_failed)
|
||||
{
|
||||
// first connect to proxy server
|
||||
if (!ConnectSocketDirectly(proxy.proxy, sock, nTimeout, true)) {
|
||||
outProxyConnectionFailed = true;
|
||||
return false;
|
||||
auto sock = proxy.Connect();
|
||||
if (!sock) {
|
||||
proxy_connection_failed = true;
|
||||
return {};
|
||||
}
|
||||
|
||||
// do socks negotiation
|
||||
if (proxy.randomize_credentials) {
|
||||
if (proxy.m_randomize_credentials) {
|
||||
ProxyCredentials random_auth;
|
||||
static std::atomic_int counter(0);
|
||||
random_auth.username = random_auth.password = strprintf("%i", counter++);
|
||||
if (!Socks5(strDest, port, &random_auth, sock)) {
|
||||
return false;
|
||||
if (!Socks5(dest, port, &random_auth, *sock)) {
|
||||
return {};
|
||||
}
|
||||
} else {
|
||||
if (!Socks5(strDest, port, nullptr, sock)) {
|
||||
return false;
|
||||
if (!Socks5(dest, port, nullptr, *sock)) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return sock;
|
||||
}
|
||||
|
||||
CSubNet LookupSubNet(const std::string& subnet_str)
|
||||
|
|
|
|||
|
|
@ -27,6 +27,9 @@ static const int DEFAULT_CONNECT_TIMEOUT = 5000;
|
|||
//! -dns default
|
||||
static const int DEFAULT_NAME_LOOKUP = true;
|
||||
|
||||
/** Prefix for unix domain socket addresses (which are local filesystem paths) */
|
||||
const std::string ADDR_PREFIX_UNIX = "unix:";
|
||||
|
||||
enum class ConnectionDirection {
|
||||
None = 0,
|
||||
In = (1U << 0),
|
||||
|
|
@ -43,16 +46,46 @@ static inline bool operator&(ConnectionDirection a, ConnectionDirection b) {
|
|||
return (underlying(a) & underlying(b));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a string is a valid UNIX domain socket path
|
||||
*
|
||||
* @param name The string provided by the user representing a local path
|
||||
*
|
||||
* @returns Whether the string has proper format, length, and points to an existing file path
|
||||
*/
|
||||
bool IsUnixSocketPath(const std::string& name);
|
||||
|
||||
class Proxy
|
||||
{
|
||||
public:
|
||||
Proxy(): randomize_credentials(false) {}
|
||||
explicit Proxy(const CService &_proxy, bool _randomize_credentials=false): proxy(_proxy), randomize_credentials(_randomize_credentials) {}
|
||||
|
||||
bool IsValid() const { return proxy.IsValid(); }
|
||||
Proxy() : m_is_unix_socket(false), m_randomize_credentials(false) {}
|
||||
explicit Proxy(const CService& _proxy, bool _randomize_credentials = false) : proxy(_proxy), m_is_unix_socket(false), m_randomize_credentials(_randomize_credentials) {}
|
||||
explicit Proxy(const std::string path, bool _randomize_credentials = false) : m_unix_socket_path(path), m_is_unix_socket(true), m_randomize_credentials(_randomize_credentials) {}
|
||||
|
||||
CService proxy;
|
||||
bool randomize_credentials;
|
||||
std::string m_unix_socket_path;
|
||||
bool m_is_unix_socket;
|
||||
bool m_randomize_credentials;
|
||||
|
||||
bool IsValid() const
|
||||
{
|
||||
if (m_is_unix_socket) return IsUnixSocketPath(m_unix_socket_path);
|
||||
return proxy.IsValid();
|
||||
}
|
||||
|
||||
sa_family_t GetFamily() const
|
||||
{
|
||||
if (m_is_unix_socket) return AF_UNIX;
|
||||
return proxy.GetSAFamily();
|
||||
}
|
||||
|
||||
std::string ToString() const
|
||||
{
|
||||
if (m_is_unix_socket) return m_unix_socket_path;
|
||||
return proxy.ToStringAddrPort();
|
||||
}
|
||||
|
||||
std::unique_ptr<Sock> Connect() const;
|
||||
};
|
||||
|
||||
/** Credentials for proxy authentication */
|
||||
|
|
@ -229,47 +262,42 @@ CService LookupNumeric(const std::string& name, uint16_t portDefault = 0, DNSLoo
|
|||
CSubNet LookupSubNet(const std::string& subnet_str);
|
||||
|
||||
/**
|
||||
* Create a TCP socket in the given address family.
|
||||
* @param[in] address_family The socket is created in the same address family as this address.
|
||||
* Create a TCP or UNIX socket in the given address family.
|
||||
* @param[in] address_family to use for the socket.
|
||||
* @return pointer to the created Sock object or unique_ptr that owns nothing in case of failure
|
||||
*/
|
||||
std::unique_ptr<Sock> CreateSockTCP(const CService& address_family);
|
||||
std::unique_ptr<Sock> CreateSockOS(sa_family_t address_family);
|
||||
|
||||
/**
|
||||
* Socket factory. Defaults to `CreateSockTCP()`, but can be overridden by unit tests.
|
||||
* Socket factory. Defaults to `CreateSockOS()`, but can be overridden by unit tests.
|
||||
*/
|
||||
extern std::function<std::unique_ptr<Sock>(const CService&)> CreateSock;
|
||||
extern std::function<std::unique_ptr<Sock>(const sa_family_t&)> CreateSock;
|
||||
|
||||
/**
|
||||
* Try to connect to the specified service on the specified socket.
|
||||
* Create a socket and try to connect to the specified service.
|
||||
*
|
||||
* @param addrConnect The service to which to connect.
|
||||
* @param sock The socket on which to connect.
|
||||
* @param nTimeout Wait this many milliseconds for the connection to be
|
||||
* established.
|
||||
* @param manual_connection Whether or not the connection was manually requested
|
||||
* (e.g. through the addnode RPC)
|
||||
* @param[in] dest The service to which to connect.
|
||||
* @param[in] manual_connection Whether or not the connection was manually requested (e.g. through the addnode RPC)
|
||||
*
|
||||
* @returns Whether or not a connection was successfully made.
|
||||
* @returns the connected socket if the operation succeeded, empty unique_ptr otherwise
|
||||
*/
|
||||
bool ConnectSocketDirectly(const CService &addrConnect, const Sock& sock, int nTimeout, bool manual_connection);
|
||||
std::unique_ptr<Sock> ConnectDirectly(const CService& dest, bool manual_connection);
|
||||
|
||||
/**
|
||||
* Connect to a specified destination service through a SOCKS5 proxy by first
|
||||
* connecting to the SOCKS5 proxy.
|
||||
*
|
||||
* @param proxy The SOCKS5 proxy.
|
||||
* @param strDest The destination service to which to connect.
|
||||
* @param port The destination port.
|
||||
* @param sock The socket on which to connect to the SOCKS5 proxy.
|
||||
* @param nTimeout Wait this many milliseconds for the connection to the SOCKS5
|
||||
* proxy to be established.
|
||||
* @param[out] outProxyConnectionFailed Whether or not the connection to the
|
||||
* SOCKS5 proxy failed.
|
||||
* @param[in] proxy The SOCKS5 proxy.
|
||||
* @param[in] dest The destination service to which to connect.
|
||||
* @param[in] port The destination port.
|
||||
* @param[out] proxy_connection_failed Whether or not the connection to the SOCKS5 proxy failed.
|
||||
*
|
||||
* @returns Whether or not the operation succeeded.
|
||||
* @returns the connected socket if the operation succeeded. Otherwise an empty unique_ptr.
|
||||
*/
|
||||
bool ConnectThroughProxy(const Proxy& proxy, const std::string& strDest, uint16_t port, const Sock& sock, int nTimeout, bool& outProxyConnectionFailed);
|
||||
std::unique_ptr<Sock> ConnectThroughProxy(const Proxy& proxy,
|
||||
const std::string& dest,
|
||||
uint16_t port,
|
||||
bool& proxy_connection_failed);
|
||||
|
||||
/**
|
||||
* Interrupt SOCKS5 reads or writes.
|
||||
|
|
|
|||
|
|
@ -456,20 +456,24 @@ void OptionsDialog::updateProxyValidationState()
|
|||
|
||||
void OptionsDialog::updateDefaultProxyNets()
|
||||
{
|
||||
const std::optional<CNetAddr> ui_proxy_netaddr{LookupHost(ui->proxyIp->text().toStdString(), /*fAllowLookup=*/false)};
|
||||
const CService ui_proxy{ui_proxy_netaddr.value_or(CNetAddr{}), ui->proxyPort->text().toUShort()};
|
||||
std::string proxyIpText{ui->proxyIp->text().toStdString()};
|
||||
if (!IsUnixSocketPath(proxyIpText)) {
|
||||
const std::optional<CNetAddr> ui_proxy_netaddr{LookupHost(proxyIpText, /*fAllowLookup=*/false)};
|
||||
const CService ui_proxy{ui_proxy_netaddr.value_or(CNetAddr{}), ui->proxyPort->text().toUShort()};
|
||||
proxyIpText = ui_proxy.ToStringAddrPort();
|
||||
}
|
||||
|
||||
Proxy proxy;
|
||||
bool has_proxy;
|
||||
|
||||
has_proxy = model->node().getProxy(NET_IPV4, proxy);
|
||||
ui->proxyReachIPv4->setChecked(has_proxy && proxy.proxy == ui_proxy);
|
||||
ui->proxyReachIPv4->setChecked(has_proxy && proxy.ToString() == proxyIpText);
|
||||
|
||||
has_proxy = model->node().getProxy(NET_IPV6, proxy);
|
||||
ui->proxyReachIPv6->setChecked(has_proxy && proxy.proxy == ui_proxy);
|
||||
ui->proxyReachIPv6->setChecked(has_proxy && proxy.ToString() == proxyIpText);
|
||||
|
||||
has_proxy = model->node().getProxy(NET_ONION, proxy);
|
||||
ui->proxyReachTor->setChecked(has_proxy && proxy.proxy == ui_proxy);
|
||||
ui->proxyReachTor->setChecked(has_proxy && proxy.ToString() == proxyIpText);
|
||||
}
|
||||
|
||||
ProxyAddressValidator::ProxyAddressValidator(QObject *parent) :
|
||||
|
|
|
|||
|
|
@ -607,8 +607,8 @@ static UniValue GetNetworksInfo()
|
|||
obj.pushKV("name", GetNetworkName(network));
|
||||
obj.pushKV("limited", !g_reachable_nets.Contains(network));
|
||||
obj.pushKV("reachable", g_reachable_nets.Contains(network));
|
||||
obj.pushKV("proxy", proxy.IsValid() ? proxy.proxy.ToStringAddrPort() : std::string());
|
||||
obj.pushKV("proxy_randomize_credentials", proxy.randomize_credentials);
|
||||
obj.pushKV("proxy", proxy.IsValid() ? proxy.ToString() : std::string());
|
||||
obj.pushKV("proxy_randomize_credentials", proxy.m_randomize_credentials);
|
||||
networks.push_back(obj);
|
||||
}
|
||||
return networks;
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ static const TypeTestOneInput* g_test_one_input{nullptr};
|
|||
void initialize()
|
||||
{
|
||||
// Terminate immediately if a fuzzing harness ever tries to create a TCP socket.
|
||||
CreateSock = [](const CService&) -> std::unique_ptr<Sock> { std::terminate(); };
|
||||
CreateSock = [](const sa_family_t&) -> std::unique_ptr<Sock> { std::terminate(); };
|
||||
|
||||
// Terminate immediately if a fuzzing harness ever tries to perform a DNS lookup.
|
||||
g_dns_lookup = [](const std::string& name, bool allow_lookup) {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include <i2p.h>
|
||||
#include <logging.h>
|
||||
#include <netaddress.h>
|
||||
#include <netbase.h>
|
||||
#include <test/util/logging.h>
|
||||
#include <test/util/net.h>
|
||||
#include <test/util/setup_common.h>
|
||||
|
|
@ -38,7 +39,7 @@ public:
|
|||
|
||||
private:
|
||||
const BCLog::Level m_prev_log_level;
|
||||
const std::function<std::unique_ptr<Sock>(const CService&)> m_create_sock_orig;
|
||||
const std::function<std::unique_ptr<Sock>(const sa_family_t&)> m_create_sock_orig;
|
||||
};
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(i2p_tests, EnvTestingSetup)
|
||||
|
|
@ -46,12 +47,14 @@ BOOST_FIXTURE_TEST_SUITE(i2p_tests, EnvTestingSetup)
|
|||
BOOST_AUTO_TEST_CASE(unlimited_recv)
|
||||
{
|
||||
// Mock CreateSock() to create MockSock.
|
||||
CreateSock = [](const CService&) {
|
||||
CreateSock = [](const sa_family_t&) {
|
||||
return std::make_unique<StaticContentsSock>(std::string(i2p::sam::MAX_MSG_SIZE + 1, 'a'));
|
||||
};
|
||||
|
||||
CThreadInterrupt interrupt;
|
||||
i2p::sam::Session session(gArgs.GetDataDirNet() / "test_i2p_private_key", CService{}, &interrupt);
|
||||
const std::optional<CService> addr{Lookup("127.0.0.1", 9000, false)};
|
||||
const Proxy sam_proxy(addr.value(), false);
|
||||
i2p::sam::Session session(gArgs.GetDataDirNet() / "test_i2p_private_key", sam_proxy, &interrupt);
|
||||
|
||||
{
|
||||
ASSERT_DEBUG_LOG("Creating persistent SAM session");
|
||||
|
|
@ -66,7 +69,7 @@ BOOST_AUTO_TEST_CASE(unlimited_recv)
|
|||
BOOST_AUTO_TEST_CASE(listen_ok_accept_fail)
|
||||
{
|
||||
size_t num_sockets{0};
|
||||
CreateSock = [&num_sockets](const CService&) {
|
||||
CreateSock = [&num_sockets](const sa_family_t&) {
|
||||
// clang-format off
|
||||
++num_sockets;
|
||||
// First socket is the control socket for creating the session.
|
||||
|
|
@ -111,8 +114,10 @@ BOOST_AUTO_TEST_CASE(listen_ok_accept_fail)
|
|||
};
|
||||
|
||||
CThreadInterrupt interrupt;
|
||||
const CService addr{in6_addr(IN6ADDR_LOOPBACK_INIT), /*port=*/7656};
|
||||
const Proxy sam_proxy(addr, false);
|
||||
i2p::sam::Session session(gArgs.GetDataDirNet() / "test_i2p_private_key",
|
||||
CService{in6_addr(IN6ADDR_LOOPBACK_INIT), /*port=*/7656},
|
||||
sam_proxy,
|
||||
&interrupt);
|
||||
|
||||
i2p::Connection conn;
|
||||
|
|
@ -130,7 +135,7 @@ BOOST_AUTO_TEST_CASE(damaged_private_key)
|
|||
{
|
||||
const auto CreateSockOrig = CreateSock;
|
||||
|
||||
CreateSock = [](const CService&) {
|
||||
CreateSock = [](const sa_family_t&) {
|
||||
return std::make_unique<StaticContentsSock>("HELLO REPLY RESULT=OK VERSION=3.1\n"
|
||||
"SESSION STATUS RESULT=OK DESTINATION=\n");
|
||||
};
|
||||
|
|
@ -154,7 +159,9 @@ BOOST_AUTO_TEST_CASE(damaged_private_key)
|
|||
BOOST_REQUIRE(WriteBinaryFile(i2p_private_key_file, file_contents));
|
||||
|
||||
CThreadInterrupt interrupt;
|
||||
i2p::sam::Session session(i2p_private_key_file, CService{}, &interrupt);
|
||||
const CService addr{in6_addr(IN6ADDR_LOOPBACK_INIT), /*port=*/7656};
|
||||
const Proxy sam_proxy{addr, false};
|
||||
i2p::sam::Session session(i2p_private_key_file, sam_proxy, &interrupt);
|
||||
|
||||
{
|
||||
ASSERT_DEBUG_LOG("Creating persistent SAM session");
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ Test plan:
|
|||
- support no authentication (other proxy)
|
||||
- support no authentication + user/pass authentication (Tor)
|
||||
- proxy on IPv6
|
||||
- proxy over unix domain sockets
|
||||
|
||||
- Create various proxies (as threads)
|
||||
- Create nodes that connect to them
|
||||
|
|
@ -39,8 +40,9 @@ addnode connect to a CJDNS address
|
|||
- Test passing unknown -onlynet
|
||||
"""
|
||||
|
||||
import socket
|
||||
import os
|
||||
import socket
|
||||
import tempfile
|
||||
|
||||
from test_framework.socks5 import Socks5Configuration, Socks5Command, Socks5Server, AddressType
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
|
|
@ -49,7 +51,7 @@ from test_framework.util import (
|
|||
PORT_RANGE,
|
||||
assert_equal,
|
||||
)
|
||||
from test_framework.netutil import test_ipv6_local
|
||||
from test_framework.netutil import test_ipv6_local, test_unix_socket
|
||||
|
||||
RANGE_BEGIN = PORT_MIN + 2 * PORT_RANGE # Start after p2p and rpc ports
|
||||
|
||||
|
|
@ -64,14 +66,17 @@ NET_CJDNS = "cjdns"
|
|||
# Networks returned by RPC getnetworkinfo, defined in src/rpc/net.cpp::GetNetworksInfo()
|
||||
NETWORKS = frozenset({NET_IPV4, NET_IPV6, NET_ONION, NET_I2P, NET_CJDNS})
|
||||
|
||||
# Use the shortest temp path possible since UNIX sockets may have as little as 92-char limit
|
||||
socket_path = tempfile.NamedTemporaryFile().name
|
||||
|
||||
class ProxyTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.num_nodes = 5
|
||||
self.num_nodes = 7
|
||||
self.setup_clean_chain = True
|
||||
|
||||
def setup_nodes(self):
|
||||
self.have_ipv6 = test_ipv6_local()
|
||||
self.have_unix_sockets = test_unix_socket()
|
||||
# Create two proxies on different ports
|
||||
# ... one unauthenticated
|
||||
self.conf1 = Socks5Configuration()
|
||||
|
|
@ -93,6 +98,15 @@ class ProxyTest(BitcoinTestFramework):
|
|||
else:
|
||||
self.log.warning("Testing without local IPv6 support")
|
||||
|
||||
if self.have_unix_sockets:
|
||||
self.conf4 = Socks5Configuration()
|
||||
self.conf4.af = socket.AF_UNIX
|
||||
self.conf4.addr = socket_path
|
||||
self.conf4.unauth = True
|
||||
self.conf4.auth = True
|
||||
else:
|
||||
self.log.warning("Testing without local unix domain sockets support")
|
||||
|
||||
self.serv1 = Socks5Server(self.conf1)
|
||||
self.serv1.start()
|
||||
self.serv2 = Socks5Server(self.conf2)
|
||||
|
|
@ -100,6 +114,9 @@ class ProxyTest(BitcoinTestFramework):
|
|||
if self.have_ipv6:
|
||||
self.serv3 = Socks5Server(self.conf3)
|
||||
self.serv3.start()
|
||||
if self.have_unix_sockets:
|
||||
self.serv4 = Socks5Server(self.conf4)
|
||||
self.serv4.start()
|
||||
|
||||
# We will not try to connect to this.
|
||||
self.i2p_sam = ('127.0.0.1', 7656)
|
||||
|
|
@ -113,10 +130,15 @@ class ProxyTest(BitcoinTestFramework):
|
|||
['-listen', f'-proxy={self.conf2.addr[0]}:{self.conf2.addr[1]}','-proxyrandomize=1'],
|
||||
[],
|
||||
['-listen', f'-proxy={self.conf1.addr[0]}:{self.conf1.addr[1]}','-proxyrandomize=1',
|
||||
'-cjdnsreachable']
|
||||
'-cjdnsreachable'],
|
||||
[],
|
||||
[]
|
||||
]
|
||||
if self.have_ipv6:
|
||||
args[3] = ['-listen', f'-proxy=[{self.conf3.addr[0]}]:{self.conf3.addr[1]}','-proxyrandomize=0', '-noonion']
|
||||
if self.have_unix_sockets:
|
||||
args[5] = ['-listen', f'-proxy=unix:{socket_path}']
|
||||
args[6] = ['-listen', f'-onion=unix:{socket_path}']
|
||||
self.add_nodes(self.num_nodes, extra_args=args)
|
||||
self.start_nodes()
|
||||
|
||||
|
|
@ -128,7 +150,7 @@ class ProxyTest(BitcoinTestFramework):
|
|||
def node_test(self, node, *, proxies, auth, test_onion, test_cjdns):
|
||||
rv = []
|
||||
addr = "15.61.23.23:1234"
|
||||
self.log.debug(f"Test: outgoing IPv4 connection through node for address {addr}")
|
||||
self.log.debug(f"Test: outgoing IPv4 connection through node {node.index} for address {addr}")
|
||||
node.addnode(addr, "onetry")
|
||||
cmd = proxies[0].queue.get()
|
||||
assert isinstance(cmd, Socks5Command)
|
||||
|
|
@ -144,7 +166,7 @@ class ProxyTest(BitcoinTestFramework):
|
|||
|
||||
if self.have_ipv6:
|
||||
addr = "[1233:3432:2434:2343:3234:2345:6546:4534]:5443"
|
||||
self.log.debug(f"Test: outgoing IPv6 connection through node for address {addr}")
|
||||
self.log.debug(f"Test: outgoing IPv6 connection through node {node.index} for address {addr}")
|
||||
node.addnode(addr, "onetry")
|
||||
cmd = proxies[1].queue.get()
|
||||
assert isinstance(cmd, Socks5Command)
|
||||
|
|
@ -160,7 +182,7 @@ class ProxyTest(BitcoinTestFramework):
|
|||
|
||||
if test_onion:
|
||||
addr = "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion:8333"
|
||||
self.log.debug(f"Test: outgoing onion connection through node for address {addr}")
|
||||
self.log.debug(f"Test: outgoing onion connection through node {node.index} for address {addr}")
|
||||
node.addnode(addr, "onetry")
|
||||
cmd = proxies[2].queue.get()
|
||||
assert isinstance(cmd, Socks5Command)
|
||||
|
|
@ -175,7 +197,7 @@ class ProxyTest(BitcoinTestFramework):
|
|||
|
||||
if test_cjdns:
|
||||
addr = "[fc00:1:2:3:4:5:6:7]:8888"
|
||||
self.log.debug(f"Test: outgoing CJDNS connection through node for address {addr}")
|
||||
self.log.debug(f"Test: outgoing CJDNS connection through node {node.index} for address {addr}")
|
||||
node.addnode(addr, "onetry")
|
||||
cmd = proxies[1].queue.get()
|
||||
assert isinstance(cmd, Socks5Command)
|
||||
|
|
@ -189,7 +211,7 @@ class ProxyTest(BitcoinTestFramework):
|
|||
self.network_test(node, addr, network=NET_CJDNS)
|
||||
|
||||
addr = "node.noumenon:8333"
|
||||
self.log.debug(f"Test: outgoing DNS name connection through node for address {addr}")
|
||||
self.log.debug(f"Test: outgoing DNS name connection through node {node.index} for address {addr}")
|
||||
node.addnode(addr, "onetry")
|
||||
cmd = proxies[3].queue.get()
|
||||
assert isinstance(cmd, Socks5Command)
|
||||
|
|
@ -234,6 +256,12 @@ class ProxyTest(BitcoinTestFramework):
|
|||
proxies=[self.serv1, self.serv1, self.serv1, self.serv1],
|
||||
auth=False, test_onion=True, test_cjdns=True)
|
||||
|
||||
if self.have_unix_sockets:
|
||||
self.node_test(self.nodes[5],
|
||||
proxies=[self.serv4, self.serv4, self.serv4, self.serv4],
|
||||
auth=True, test_onion=True, test_cjdns=False)
|
||||
|
||||
|
||||
def networks_dict(d):
|
||||
r = {}
|
||||
for x in d['networks']:
|
||||
|
|
@ -319,6 +347,37 @@ class ProxyTest(BitcoinTestFramework):
|
|||
assert_equal(n4['i2p']['reachable'], False)
|
||||
assert_equal(n4['cjdns']['reachable'], True)
|
||||
|
||||
if self.have_unix_sockets:
|
||||
n5 = networks_dict(nodes_network_info[5])
|
||||
assert_equal(NETWORKS, n5.keys())
|
||||
for net in NETWORKS:
|
||||
if net == NET_I2P:
|
||||
expected_proxy = ''
|
||||
expected_randomize = False
|
||||
else:
|
||||
expected_proxy = 'unix:' + self.conf4.addr # no port number
|
||||
expected_randomize = True
|
||||
assert_equal(n5[net]['proxy'], expected_proxy)
|
||||
assert_equal(n5[net]['proxy_randomize_credentials'], expected_randomize)
|
||||
assert_equal(n5['onion']['reachable'], True)
|
||||
assert_equal(n5['i2p']['reachable'], False)
|
||||
assert_equal(n5['cjdns']['reachable'], False)
|
||||
|
||||
n6 = networks_dict(nodes_network_info[6])
|
||||
assert_equal(NETWORKS, n6.keys())
|
||||
for net in NETWORKS:
|
||||
if net != NET_ONION:
|
||||
expected_proxy = ''
|
||||
expected_randomize = False
|
||||
else:
|
||||
expected_proxy = 'unix:' + self.conf4.addr # no port number
|
||||
expected_randomize = True
|
||||
assert_equal(n6[net]['proxy'], expected_proxy)
|
||||
assert_equal(n6[net]['proxy_randomize_credentials'], expected_randomize)
|
||||
assert_equal(n6['onion']['reachable'], True)
|
||||
assert_equal(n6['i2p']['reachable'], False)
|
||||
assert_equal(n6['cjdns']['reachable'], False)
|
||||
|
||||
self.stop_node(1)
|
||||
|
||||
self.log.info("Test passing invalid -proxy hostname raises expected init error")
|
||||
|
|
@ -387,6 +446,18 @@ class ProxyTest(BitcoinTestFramework):
|
|||
msg = "Error: Unknown network specified in -onlynet: 'abc'"
|
||||
self.nodes[1].assert_start_raises_init_error(expected_msg=msg)
|
||||
|
||||
self.log.info("Test passing too-long unix path to -proxy raises init error")
|
||||
self.nodes[1].extra_args = [f"-proxy=unix:{'x' * 1000}"]
|
||||
if self.have_unix_sockets:
|
||||
msg = f"Error: Invalid -proxy address or hostname: 'unix:{'x' * 1000}'"
|
||||
else:
|
||||
# If unix sockets are not supported, the file path is incorrectly interpreted as host:port
|
||||
msg = f"Error: Invalid port specified in -proxy: 'unix:{'x' * 1000}'"
|
||||
self.nodes[1].assert_start_raises_init_error(expected_msg=msg)
|
||||
|
||||
# Cleanup socket path we established outside the individual test directory.
|
||||
if self.have_unix_sockets:
|
||||
os.unlink(socket_path)
|
||||
|
||||
if __name__ == '__main__':
|
||||
ProxyTest().main()
|
||||
|
|
|
|||
|
|
@ -158,3 +158,12 @@ def test_ipv6_local():
|
|||
except socket.error:
|
||||
have_ipv6 = False
|
||||
return have_ipv6
|
||||
|
||||
def test_unix_socket():
|
||||
'''Return True if UNIX sockets are available on this platform.'''
|
||||
try:
|
||||
socket.AF_UNIX
|
||||
except AttributeError:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue