Skip to content

Commit 713f90e

Browse files
Merge #6634: backport: merge bitcoin#22087, bitcoin#28695, bitcoin#27375, bitcoin#29649, bitcoin#27679, bitcoin#29968, partial bitcoin#26312 (UNIX domain sockets support)
10c7c1d merge bitcoin#29968: Avoid unused-variable warning in init.cpp (Kittywhiskers Van Gogh) 82a0fb2 merge bitcoin#27679: Support UNIX domain sockets (Kittywhiskers Van Gogh) a23d651 merge bitcoin#29649: remove unnecessary log message (Kittywhiskers Van Gogh) 343bf83 merge bitcoin#27375: support unix domain sockets for -proxy and -onion (Kittywhiskers Van Gogh) 0fd83f3 merge bitcoin#28695: Sanity check private keys received from SAM proxy (Kittywhiskers Van Gogh) 6e55ab5 partial bitcoin#26312: Remove Sock::Get() and Sock::Sock() (Kittywhiskers Van Gogh) 859da12 merge bitcoin#22087: Validate port-options (Kittywhiskers Van Gogh) Pull request description: ## Additional Information * Depends on #6630 ## Breaking Changes None expected. ## Checklist - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas **(note: N/A)** - [x] I have added or updated relevant unit/integration/functional/e2e tests - [x] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ ACKs for top commit: UdjinM6: utACK 10c7c1d Tree-SHA512: cf3bb84a34315fd188bf1427f59e8b050a04f26d2d4a152a8477c50daef154b35e0f29e49787ac8b585078e985d7192bc39bde27ea39a3b547c297cb2fc3a2ae
2 parents c820921 + 10c7c1d commit 713f90e

26 files changed

+632
-184
lines changed

configure.ac

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1246,10 +1246,23 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
12461246
]], [[
12471247
getauxval(AT_HWCAP);
12481248
]])],
1249-
[ AC_MSG_RESULT([yes]); HAVE_STRONG_GETAUXVAL=1; AC_DEFINE([HAVE_STRONG_GETAUXVAL], [1], [Define this symbol to build code that uses getauxval)]) ],
1249+
[ AC_MSG_RESULT([yes]); HAVE_STRONG_GETAUXVAL=1; AC_DEFINE([HAVE_STRONG_GETAUXVAL], [1], [Define this symbol to build code that uses getauxval]) ],
12501250
[ AC_MSG_RESULT([no]); HAVE_STRONG_GETAUXVAL=0 ]
12511251
)
12521252

1253+
# Check for UNIX sockets
1254+
AC_MSG_CHECKING(for sockaddr_un)
1255+
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
1256+
#include <sys/socket.h>
1257+
#include <sys/un.h>
1258+
]], [[
1259+
struct sockaddr_un addr;
1260+
addr.sun_family = AF_UNIX;
1261+
]])],
1262+
[ AC_MSG_RESULT([yes]); AC_DEFINE([HAVE_SOCKADDR_UN], [1], [Define this symbol if platform supports unix domain sockets]) ],
1263+
[ AC_MSG_RESULT([no]); ]
1264+
)
1265+
12531266
have_any_system=no
12541267
AC_MSG_CHECKING([for std::system])
12551268
AC_LINK_IFELSE(

doc/release-notes-6634.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Updated settings
2+
----------------
3+
4+
- 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`).
11+
12+
- UNIX socket paths are now accepted for `-zmqpubrawblock` and `-zmqpubrawtx` with the format `-zmqpubrawtx=unix:/path/to/file`.

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: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -114,20 +114,18 @@ 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},
121121
m_interrupt{interrupt},
122-
m_control_sock{std::make_unique<Sock>(INVALID_SOCKET)},
123122
m_transient{false}
124123
{
125124
}
126125

127-
Session::Session(const CService& control_host, CThreadInterrupt* interrupt)
126+
Session::Session(const Proxy& control_host, CThreadInterrupt* interrupt)
128127
: m_control_host{control_host},
129128
m_interrupt{interrupt},
130-
m_control_sock{std::make_unique<Sock>(INVALID_SOCKET)},
131129
m_transient{true}
132130
{
133131
}
@@ -326,14 +324,10 @@ Session::Reply Session::SendRequestAndGetReply(const Sock& sock,
326324

327325
std::unique_ptr<Sock> Session::Hello() const
328326
{
329-
auto sock = CreateSock(m_control_host);
327+
auto sock = m_control_host.Connect();
330328

331329
if (!sock) {
332-
throw std::runtime_error("Cannot create socket");
333-
}
334-
335-
if (!ConnectSocketDirectly(m_control_host, *sock, nConnectTimeout, true)) {
336-
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()));
337331
}
338332

339333
SendRequestAndGetReply(*sock, "HELLO VERSION MIN=3.1 MAX=3.1");
@@ -346,7 +340,7 @@ void Session::CheckControlSock()
346340
LOCK(m_mutex);
347341

348342
std::string errmsg;
349-
if (!m_control_sock->IsConnected(errmsg)) {
343+
if (m_control_sock && !m_control_sock->IsConnected(errmsg)) {
350344
LogPrintLevel(BCLog::I2P, BCLog::Level::Debug, "Control socket error: %s\n", errmsg);
351345
Disconnect();
352346
}
@@ -384,25 +378,40 @@ Binary Session::MyDestination() const
384378
static constexpr size_t CERT_LEN_POS = 385;
385379

386380
uint16_t cert_len;
381+
382+
if (m_private_key.size() < CERT_LEN_POS + sizeof(cert_len)) {
383+
throw std::runtime_error(strprintf("The private key is too short (%d < %d)",
384+
m_private_key.size(),
385+
CERT_LEN_POS + sizeof(cert_len)));
386+
}
387+
387388
memcpy(&cert_len, &m_private_key.at(CERT_LEN_POS), sizeof(cert_len));
388389
cert_len = be16toh_internal(cert_len);
389390

390391
const size_t dest_len = DEST_LEN_BASE + cert_len;
391392

393+
if (dest_len > m_private_key.size()) {
394+
throw std::runtime_error(strprintf("Certificate length (%d) designates that the private key should "
395+
"be %d bytes, but it is only %d bytes",
396+
cert_len,
397+
dest_len,
398+
m_private_key.size()));
399+
}
400+
392401
return Binary{m_private_key.begin(), m_private_key.begin() + dest_len};
393402
}
394403

395404
void Session::CreateIfNotCreatedAlready()
396405
{
397406
std::string errmsg;
398-
if (m_control_sock->IsConnected(errmsg)) {
407+
if (m_control_sock && m_control_sock->IsConnected(errmsg)) {
399408
return;
400409
}
401410

402411
const auto session_type = m_transient ? "transient" : "persistent";
403412
const auto session_id = GetRandHash().GetHex().substr(0, 10); // full is overkill, too verbose in the logs
404413

405-
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());
406415

407416
auto sock = Hello();
408417

@@ -468,14 +477,14 @@ std::unique_ptr<Sock> Session::StreamAccept()
468477

469478
void Session::Disconnect()
470479
{
471-
if (m_control_sock->Get() != INVALID_SOCKET) {
480+
if (m_control_sock) {
472481
if (m_session_id.empty()) {
473482
LogPrintLevel(BCLog::I2P, BCLog::Level::Info, "Destroying incomplete SAM session\n");
474483
} else {
475484
LogPrintLevel(BCLog::I2P, BCLog::Level::Info, "Destroying SAM session %s\n", m_session_id);
476485
}
486+
m_control_sock.reset();
477487
}
478-
m_control_sock = std::make_unique<Sock>(INVALID_SOCKET);
479488
m_session_id.clear();
480489
}
481490
} // namespace sam

src/i2p.h

Lines changed: 6 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.
@@ -253,6 +254,7 @@ class Session
253254
* ("SESSION CREATE"). With the established session id we later open
254255
* other connections to the SAM service to accept incoming I2P
255256
* connections and make outgoing ones.
257+
* If not connected then this unique_ptr will be empty.
256258
* See https://geti2p.net/en/docs/api/samv3
257259
*/
258260
std::unique_ptr<Sock> m_control_sock GUARDED_BY(m_mutex);

src/init.cpp

Lines changed: 94 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);
@@ -1670,6 +1678,73 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
16701678
}
16711679
}
16721680

1681+
// Check port numbers
1682+
for (const std::string port_option : {
1683+
"-port",
1684+
"-rpcport",
1685+
}) {
1686+
if (args.IsArgSet(port_option)) {
1687+
const std::string port = args.GetArg(port_option, "");
1688+
uint16_t n;
1689+
if (!ParseUInt16(port, &n) || n == 0) {
1690+
return InitError(InvalidPortErrMsg(port_option, port));
1691+
}
1692+
}
1693+
}
1694+
1695+
for ([[maybe_unused]] const auto& [arg, unix] : std::vector<std::pair<std::string, bool>>{
1696+
// arg name UNIX socket support
1697+
{"-i2psam", false},
1698+
{"-onion", true},
1699+
{"-proxy", true},
1700+
{"-rpcbind", false},
1701+
{"-torcontrol", false},
1702+
{"-whitebind", false},
1703+
{"-zmqpubhashblock", true},
1704+
{"-zmqpubhashchainlock", true},
1705+
{"-zmqpubhashgovernanceobject", true},
1706+
{"-zmqpubhashgovernancevote", true},
1707+
{"-zmqpubhashinstantsenddoublespend", true},
1708+
{"-zmqpubhashrecoveredsig", true},
1709+
{"-zmqpubhashtx", true},
1710+
{"-zmqpubhashtxlock", true},
1711+
{"-zmqpubrawblock", true},
1712+
{"-zmqpubrawchainlock", true},
1713+
{"-zmqpubrawchainlocksig", true},
1714+
{"-zmqpubrawgovernancevote", true},
1715+
{"-zmqpubrawgovernanceobject", true},
1716+
{"-zmqpubrawinstantsenddoublespend", true},
1717+
{"-zmqpubrawrecoveredsig", true},
1718+
{"-zmqpubrawtx", true},
1719+
{"-zmqpubrawtxlock", true},
1720+
{"-zmqpubrawtxlocksig", true},
1721+
{"-zmqpubsequence", true},
1722+
}) {
1723+
for (const std::string& socket_addr : args.GetArgs(arg)) {
1724+
std::string host_out;
1725+
uint16_t port_out{0};
1726+
if (!SplitHostPort(socket_addr, port_out, host_out)) {
1727+
#if HAVE_SOCKADDR_UN
1728+
// Allow unix domain sockets for some options e.g. unix:/some/file/path
1729+
if (!unix || socket_addr.find(ADDR_PREFIX_UNIX) != 0) {
1730+
return InitError(InvalidPortErrMsg(arg, socket_addr));
1731+
}
1732+
#else
1733+
return InitError(InvalidPortErrMsg(arg, socket_addr));
1734+
#endif
1735+
}
1736+
}
1737+
}
1738+
1739+
for (const std::string& socket_addr : args.GetArgs("-bind")) {
1740+
std::string host_out;
1741+
uint16_t port_out{0};
1742+
std::string bind_socket_addr = socket_addr.substr(0, socket_addr.rfind('='));
1743+
if (!SplitHostPort(bind_socket_addr, port_out, host_out)) {
1744+
return InitError(InvalidPortErrMsg("-bind", socket_addr));
1745+
}
1746+
}
1747+
16731748
// sanitize comments per BIP-0014, format user agent and check total size
16741749
std::vector<std::string> uacomments;
16751750

@@ -1729,12 +1804,18 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
17291804
// -noproxy (or -proxy=0) as well as the empty string can be used to not set a proxy, this is the default
17301805
std::string proxyArg = args.GetArg("-proxy", "");
17311806
if (proxyArg != "" && proxyArg != "0") {
1732-
const std::optional<CService> proxyAddr{Lookup(proxyArg, 9050, fNameLookup)};
1733-
if (!proxyAddr.has_value()) {
1734-
return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'"), proxyArg));
1807+
Proxy addrProxy;
1808+
if (IsUnixSocketPath(proxyArg)) {
1809+
addrProxy = Proxy(proxyArg, proxyRandomize);
1810+
} else {
1811+
const std::optional<CService> proxyAddr{Lookup(proxyArg, 9050, fNameLookup)};
1812+
if (!proxyAddr.has_value()) {
1813+
return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'"), proxyArg));
1814+
}
1815+
1816+
addrProxy = Proxy(proxyAddr.value(), proxyRandomize);
17351817
}
17361818

1737-
Proxy addrProxy = Proxy(proxyAddr.value(), proxyRandomize);
17381819
if (!addrProxy.IsValid())
17391820
return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'"), proxyArg));
17401821

@@ -1760,11 +1841,16 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
17601841
"reaching the Tor network is explicitly forbidden: -onion=0"));
17611842
}
17621843
} else {
1763-
const std::optional<CService> addr{Lookup(onionArg, 9050, fNameLookup)};
1764-
if (!addr.has_value() || !addr->IsValid()) {
1765-
return InitError(strprintf(_("Invalid -onion address or hostname: '%s'"), onionArg));
1844+
if (IsUnixSocketPath(onionArg)) {
1845+
onion_proxy = Proxy(onionArg, proxyRandomize);
1846+
} else {
1847+
const std::optional<CService> addr{Lookup(onionArg, 9050, fNameLookup)};
1848+
if (!addr.has_value() || !addr->IsValid()) {
1849+
return InitError(strprintf(_("Invalid -onion address or hostname: '%s'"), onionArg));
1850+
}
1851+
1852+
onion_proxy = Proxy(addr.value(), proxyRandomize);
17661853
}
1767-
onion_proxy = Proxy{addr.value(), proxyRandomize};
17681854
}
17691855
}
17701856

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();

0 commit comments

Comments
 (0)