Skip to content

Commit 343bf83

Browse files
committed
merge bitcoin#27375: support unix domain sockets for -proxy and -onion
1 parent 0fd83f3 commit 343bf83

File tree

18 files changed

+402
-149
lines changed

18 files changed

+402
-149
lines changed

configure.ac

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1262,10 +1262,23 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
12621262
]], [[
12631263
getauxval(AT_HWCAP);
12641264
]])],
1265-
[ AC_MSG_RESULT([yes]); HAVE_STRONG_GETAUXVAL=1; AC_DEFINE([HAVE_STRONG_GETAUXVAL], [1], [Define this symbol to build code that uses getauxval)]) ],
1265+
[ AC_MSG_RESULT([yes]); HAVE_STRONG_GETAUXVAL=1; AC_DEFINE([HAVE_STRONG_GETAUXVAL], [1], [Define this symbol to build code that uses getauxval]) ],
12661266
[ AC_MSG_RESULT([no]); HAVE_STRONG_GETAUXVAL=0 ]
12671267
)
12681268

1269+
# Check for UNIX sockets
1270+
AC_MSG_CHECKING(for sockaddr_un)
1271+
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
1272+
#include <sys/socket.h>
1273+
#include <sys/un.h>
1274+
]], [[
1275+
struct sockaddr_un addr;
1276+
addr.sun_family = AF_UNIX;
1277+
]])],
1278+
[ AC_MSG_RESULT([yes]); AC_DEFINE([HAVE_SOCKADDR_UN], [1], [Define this symbol if platform supports unix domain sockets]) ],
1279+
[ AC_MSG_RESULT([no]); ]
1280+
)
1281+
12691282
have_any_system=no
12701283
AC_MSG_CHECKING([for std::system])
12711284
AC_LINK_IFELSE(

doc/release-notes-6634.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,9 @@ Updated settings
22
----------------
33

44
- Ports specified in `-port` and `-rpcport` options are now validated at startup. Values that previously worked and were considered valid can now result in errors.
5+
6+
P2P
7+
---
8+
9+
- 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.
10+
`-onion=unix:/home/me/torsocket`).

src/compat/compat.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@
3636
#include <unistd.h> // IWYU pragma: export
3737
#endif
3838

39+
// Windows does not have `sa_family_t` - it defines `sockaddr::sa_family` as `u_short`.
40+
// Thus define `sa_family_t` on Windows too so that the rest of the code can use `sa_family_t`.
41+
// See https://learn.microsoft.com/en-us/windows/win32/api/winsock/ns-winsock-sockaddr#syntax
42+
#ifdef WIN32
43+
typedef u_short sa_family_t;
44+
#endif
45+
3946
// We map Linux / BSD error functions and codes, to the equivalent
4047
// Windows definitions, and use the WSA* names throughout our code.
4148
// Note that glibc defines EWOULDBLOCK as EAGAIN (see errno.h).

src/i2p.cpp

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ static CNetAddr DestB64ToAddr(const std::string& dest)
114114
namespace sam {
115115

116116
Session::Session(const fs::path& private_key_file,
117-
const CService& control_host,
117+
const Proxy& control_host,
118118
CThreadInterrupt* interrupt)
119119
: m_private_key_file{private_key_file},
120120
m_control_host{control_host},
@@ -123,7 +123,7 @@ Session::Session(const fs::path& private_key_file,
123123
{
124124
}
125125

126-
Session::Session(const CService& control_host, CThreadInterrupt* interrupt)
126+
Session::Session(const Proxy& control_host, CThreadInterrupt* interrupt)
127127
: m_control_host{control_host},
128128
m_interrupt{interrupt},
129129
m_transient{true}
@@ -324,14 +324,10 @@ Session::Reply Session::SendRequestAndGetReply(const Sock& sock,
324324

325325
std::unique_ptr<Sock> Session::Hello() const
326326
{
327-
auto sock = CreateSock(m_control_host);
327+
auto sock = m_control_host.Connect();
328328

329329
if (!sock) {
330-
throw std::runtime_error("Cannot create socket");
331-
}
332-
333-
if (!ConnectSocketDirectly(m_control_host, *sock, nConnectTimeout, true)) {
334-
throw std::runtime_error(strprintf("Cannot connect to %s", m_control_host.ToStringAddrPort()));
330+
throw std::runtime_error(strprintf("Cannot connect to %s", m_control_host.ToString()));
335331
}
336332

337333
SendRequestAndGetReply(*sock, "HELLO VERSION MIN=3.1 MAX=3.1");
@@ -415,7 +411,7 @@ void Session::CreateIfNotCreatedAlready()
415411
const auto session_type = m_transient ? "transient" : "persistent";
416412
const auto session_id = GetRandHash().GetHex().substr(0, 10); // full is overkill, too verbose in the logs
417413

418-
LogPrintLevel(BCLog::I2P, BCLog::Level::Debug, "Creating %s SAM session %s with %s\n", session_type, session_id, m_control_host.ToStringAddrPort());
414+
LogPrintLevel(BCLog::I2P, BCLog::Level::Debug, "Creating %s SAM session %s with %s\n", session_type, session_id, m_control_host.ToString());
419415

420416
auto sock = Hello();
421417

src/i2p.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <compat/compat.h>
99
#include <fs.h>
1010
#include <netaddress.h>
11+
#include <netbase.h>
1112
#include <sync.h>
1213
#include <threadinterrupt.h>
1314
#include <util/sock.h>
@@ -67,7 +68,7 @@ class Session
6768
* `Session` object.
6869
*/
6970
Session(const fs::path& private_key_file,
70-
const CService& control_host,
71+
const Proxy& control_host,
7172
CThreadInterrupt* interrupt);
7273

7374
/**
@@ -81,7 +82,7 @@ class Session
8182
* `CThreadInterrupt` object is saved, so it must not be destroyed earlier than this
8283
* `Session` object.
8384
*/
84-
Session(const CService& control_host, CThreadInterrupt* interrupt);
85+
Session(const Proxy& control_host, CThreadInterrupt* interrupt);
8586

8687
/**
8788
* Destroy the session, closing the internally used sockets. The sockets that have been
@@ -227,9 +228,9 @@ class Session
227228
const fs::path m_private_key_file;
228229

229230
/**
230-
* The host and port of the SAM control service.
231+
* The SAM control service proxy.
231232
*/
232-
const CService m_control_host;
233+
const Proxy m_control_host;
233234

234235
/**
235236
* Cease network activity when this is signaled.

src/init.cpp

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,11 @@ void SetupServerArgs(ArgsManager& argsman)
567567
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);
568568
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);
569569
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);
570+
#if HAVE_SOCKADDR_UN
571+
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);
572+
#else
570573
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);
574+
#endif
571575
argsman.AddArg("-i2psam=<ip:port>", "I2P SAM proxy to reach I2P peers and accept I2P connections (default: none)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
572576
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);
573577
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);
@@ -580,7 +584,11 @@ void SetupServerArgs(ArgsManager& argsman)
580584
// TODO: remove the sentence "Nodes not using ... incoming connections." once the changes from
581585
// https://github.com/bitcoin/bitcoin/pull/23542 have become widespread.
582586
argsman.AddArg("-port=<port>", strprintf("Listen for connections on <port>. Nodes not using the default ports (default: %u, testnet: %u, regtest: %u) are unlikely to get incoming connections. Not relevant for I2P (see doc/i2p.md).", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort(), regtestChainParams->GetDefaultPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
587+
#if HAVE_SOCKADDR_UN
588+
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);
589+
#else
583590
argsman.AddArg("-proxy=<ip:port>", "Connect through SOCKS5 proxy, set -noproxy to disable (default: disabled)", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_ELISION, OptionsCategory::CONNECTION);
591+
#endif
584592
argsman.AddArg("-proxyrandomize", strprintf("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)", DEFAULT_PROXYRANDOMIZE), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
585593
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);
586594
argsman.AddArg("-socketevents=<mode>", "Socket events mode, which must be one of 'select', 'poll', 'epoll' or 'kqueue', depending on your system (default: Linux - 'epoll', FreeBSD/Apple - 'kqueue', Windows - 'select')", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
@@ -1719,7 +1727,14 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
17191727
std::string host_out;
17201728
uint16_t port_out{0};
17211729
if (!SplitHostPort(socket_addr, port_out, host_out)) {
1730+
#if HAVE_SOCKADDR_UN
1731+
// Allow unix domain sockets for -proxy and -onion e.g. unix:/some/file/path
1732+
if ((port_option != "-proxy" && port_option != "-onion") || socket_addr.find(ADDR_PREFIX_UNIX) != 0) {
1733+
return InitError(InvalidPortErrMsg(port_option, socket_addr));
1734+
}
1735+
#else
17221736
return InitError(InvalidPortErrMsg(port_option, socket_addr));
1737+
#endif
17231738
}
17241739
}
17251740
}
@@ -1792,12 +1807,18 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
17921807
// -noproxy (or -proxy=0) as well as the empty string can be used to not set a proxy, this is the default
17931808
std::string proxyArg = args.GetArg("-proxy", "");
17941809
if (proxyArg != "" && proxyArg != "0") {
1795-
const std::optional<CService> proxyAddr{Lookup(proxyArg, 9050, fNameLookup)};
1796-
if (!proxyAddr.has_value()) {
1797-
return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'"), proxyArg));
1810+
Proxy addrProxy;
1811+
if (IsUnixSocketPath(proxyArg)) {
1812+
addrProxy = Proxy(proxyArg, proxyRandomize);
1813+
} else {
1814+
const std::optional<CService> proxyAddr{Lookup(proxyArg, 9050, fNameLookup)};
1815+
if (!proxyAddr.has_value()) {
1816+
return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'"), proxyArg));
1817+
}
1818+
1819+
addrProxy = Proxy(proxyAddr.value(), proxyRandomize);
17981820
}
17991821

1800-
Proxy addrProxy = Proxy(proxyAddr.value(), proxyRandomize);
18011822
if (!addrProxy.IsValid())
18021823
return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'"), proxyArg));
18031824

@@ -1823,11 +1844,16 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
18231844
"reaching the Tor network is explicitly forbidden: -onion=0"));
18241845
}
18251846
} else {
1826-
const std::optional<CService> addr{Lookup(onionArg, 9050, fNameLookup)};
1827-
if (!addr.has_value() || !addr->IsValid()) {
1828-
return InitError(strprintf(_("Invalid -onion address or hostname: '%s'"), onionArg));
1847+
if (IsUnixSocketPath(onionArg)) {
1848+
onion_proxy = Proxy(onionArg, proxyRandomize);
1849+
} else {
1850+
const std::optional<CService> addr{Lookup(onionArg, 9050, fNameLookup)};
1851+
if (!addr.has_value() || !addr->IsValid()) {
1852+
return InitError(strprintf(_("Invalid -onion address or hostname: '%s'"), onionArg));
1853+
}
1854+
1855+
onion_proxy = Proxy(addr.value(), proxyRandomize);
18291856
}
1830-
onion_proxy = Proxy{addr.value(), proxyRandomize};
18311857
}
18321858
}
18331859

src/masternode/node.cpp

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -155,16 +155,9 @@ void CActiveMasternodeManager::InitInternal(const CBlockIndex* pindex)
155155

156156
// Check socket connectivity
157157
LogPrintf("CActiveMasternodeManager::Init -- Checking inbound connection to '%s'\n", m_info.service.ToStringAddrPort());
158-
std::unique_ptr<Sock> sock = CreateSock(m_info.service);
159-
if (!sock) {
160-
m_state = MasternodeState::SOME_ERROR;
161-
m_error = "Could not create socket to connect to " + m_info.service.ToStringAddrPort();
162-
LogPrintf("CActiveMasternodeManager::Init -- ERROR: %s\n", m_error);
163-
return;
164-
}
165-
bool fConnected = ConnectSocketDirectly(m_info.service, *sock, nConnectTimeout, true) && sock->IsSelectable();
158+
std::unique_ptr<Sock> sock{ConnectDirectly(m_info.service, /*manual_connection=*/true)};
159+
bool fConnected{sock && sock->IsSelectable()};
166160
sock = std::make_unique<Sock>(INVALID_SOCKET);
167-
168161
if (!fConnected && Params().RequireRoutableExternalIP()) {
169162
m_state = MasternodeState::SOME_ERROR;
170163
m_error = "Could not connect to " + m_info.service.ToStringAddrPort();

src/net.cpp

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,6 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
482482
}
483483

484484
// Connect
485-
bool connected = false;
486485
std::unique_ptr<Sock> sock;
487486
Proxy proxy;
488487
CAddress addr_bind;
@@ -495,6 +494,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
495494

496495
if (addrConnect.IsI2P() && use_proxy) {
497496
i2p::Connection conn;
497+
bool connected{false};
498498

499499
if (m_i2p_sam_session) {
500500
connected = m_i2p_sam_session->Connect(addrConnect, conn, proxyConnectionFailed);
@@ -503,7 +503,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
503503
LOCK(m_unused_i2p_sessions_mutex);
504504
if (m_unused_i2p_sessions.empty()) {
505505
i2p_transient_session =
506-
std::make_unique<i2p::sam::Session>(proxy.proxy, &interruptNet);
506+
std::make_unique<i2p::sam::Session>(proxy, &interruptNet);
507507
} else {
508508
i2p_transient_session.swap(m_unused_i2p_sessions.front());
509509
m_unused_i2p_sessions.pop();
@@ -523,38 +523,25 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
523523
addr_bind = CAddress{conn.me, NODE_NONE};
524524
}
525525
} else if (use_proxy) {
526-
sock = CreateSock(proxy.proxy);
527-
if (!sock) {
528-
return nullptr;
529-
}
530-
connected = ConnectThroughProxy(proxy, addrConnect.ToStringAddr(), addrConnect.GetPort(),
531-
*sock, nConnectTimeout, proxyConnectionFailed);
526+
LogPrintLevel(BCLog::PROXY, BCLog::Level::Debug, "Using proxy: %s to connect to %s:%s\n", proxy.ToString(), addrConnect.ToStringAddr(), addrConnect.GetPort());
527+
sock = ConnectThroughProxy(proxy, addrConnect.ToStringAddr(), addrConnect.GetPort(), proxyConnectionFailed);
532528
} else {
533529
// no proxy needed (none set for target network)
534-
sock = CreateSock(addrConnect);
535-
if (!sock) {
536-
return nullptr;
537-
}
538-
connected = ConnectSocketDirectly(addrConnect, *sock, nConnectTimeout, conn_type == ConnectionType::MANUAL);
530+
sock = ConnectDirectly(addrConnect, conn_type == ConnectionType::MANUAL);
539531
}
540532
if (!proxyConnectionFailed) {
541533
// If a connection to the node was attempted, and failure (if any) is not caused by a problem connecting to
542534
// the proxy, mark this as an attempt.
543535
addrman.Attempt(addrConnect, fCountFailure);
544536
}
545537
} else if (pszDest && GetNameProxy(proxy)) {
546-
sock = CreateSock(proxy.proxy);
547-
if (!sock) {
548-
return nullptr;
549-
}
550538
std::string host;
551539
uint16_t port{default_port};
552540
SplitHostPort(std::string(pszDest), port, host);
553541
bool proxyConnectionFailed;
554-
connected = ConnectThroughProxy(proxy, host, port, *sock, nConnectTimeout,
555-
proxyConnectionFailed);
542+
sock = ConnectThroughProxy(proxy, host, port, proxyConnectionFailed);
556543
}
557-
if (!connected) {
544+
if (!sock) {
558545
return nullptr;
559546
}
560547

@@ -4003,7 +3990,7 @@ bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError,
40033990
return false;
40043991
}
40053992

4006-
std::unique_ptr<Sock> sock = CreateSock(addrBind);
3993+
std::unique_ptr<Sock> sock = CreateSock(addrBind.GetSAFamily());
40073994
if (!sock) {
40083995
strError = strprintf(Untranslated("Couldn't open socket for incoming connections (socket returned error %s)"), NetworkErrorString(WSAGetLastError()));
40093996
LogPrintLevel(BCLog::NET, BCLog::Level::Error, "%s\n", strError.original);
@@ -4227,7 +4214,7 @@ bool CConnman::Start(CDeterministicMNManager& dmnman, CMasternodeMetaMan& mn_met
42274214
Proxy i2p_sam;
42284215
if (GetProxy(NET_I2P, i2p_sam) && connOptions.m_i2p_accept_incoming) {
42294216
m_i2p_sam_session = std::make_unique<i2p::sam::Session>(gArgs.GetDataDirNet() / "i2p_private_key",
4230-
i2p_sam.proxy, &interruptNet);
4217+
i2p_sam, &interruptNet);
42314218
}
42324219

42334220
for (const auto& strDest : connOptions.vSeedNodes) {

src/netaddress.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -824,6 +824,19 @@ bool CService::SetSockAddr(const struct sockaddr *paddr)
824824
}
825825
}
826826

827+
sa_family_t CService::GetSAFamily() const
828+
{
829+
switch (m_net) {
830+
case NET_IPV4:
831+
return AF_INET;
832+
case NET_IPV6:
833+
case NET_CJDNS:
834+
return AF_INET6;
835+
default:
836+
return AF_UNSPEC;
837+
}
838+
}
839+
827840
uint16_t CService::GetPort() const
828841
{
829842
return port;

src/netaddress.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,11 @@ class CService : public CNetAddr
551551
uint16_t GetPort() const;
552552
bool GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const;
553553
bool SetSockAddr(const struct sockaddr* paddr);
554+
/**
555+
* Get the address family
556+
* @returns AF_UNSPEC if unspecified
557+
*/
558+
[[nodiscard]] sa_family_t GetSAFamily() const;
554559
friend bool operator==(const CService& a, const CService& b);
555560
friend bool operator!=(const CService& a, const CService& b) { return !(a == b); }
556561
friend bool operator<(const CService& a, const CService& b);

0 commit comments

Comments
 (0)