mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-19 05:45:05 +01:00
Merge bitcoin/bitcoin#22729: Make it possible to disable Tor binds and abort startup on bind failure
bca346a970
net: require P2P binds to succeed (Vasil Dimov)af552534ab
net: report an error if unable to bind on the Tor listening addr:port (Vasil Dimov)9a7e5f4d68
net: don't extra bind for Tor if binds are restricted (Vasil Dimov) Pull request description: Make it possible to disable the Tor binding on `127.0.0.1:8334` and stop startup if any P2P bind fails instead of "if all P2P binds fail". Fixes https://github.com/bitcoin/bitcoin/issues/22726 Fixes https://github.com/bitcoin/bitcoin/issues/22727 ACKs for top commit: achow101: ACKbca346a970
cbergqvist: ACKbca346a970
pinheadmz: ACKbca346a970
Tree-SHA512: fabef89a957191eea4f3e3b6109d2b8389f27ecc74440a920b0c10f31fff00a85bcfd1eb3c91826c7169c618f4de8a8d0a260e2caf40fd854f07ea9a980d8603
This commit is contained in:
commit
45750f61d6
@ -1881,6 +1881,8 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|||||||
CService onion_service_target;
|
CService onion_service_target;
|
||||||
if (!connOptions.onion_binds.empty()) {
|
if (!connOptions.onion_binds.empty()) {
|
||||||
onion_service_target = connOptions.onion_binds.front();
|
onion_service_target = connOptions.onion_binds.front();
|
||||||
|
} else if (!connOptions.vBinds.empty()) {
|
||||||
|
onion_service_target = connOptions.vBinds.front();
|
||||||
} else {
|
} else {
|
||||||
onion_service_target = DefaultOnionServiceTarget();
|
onion_service_target = DefaultOnionServiceTarget();
|
||||||
connOptions.onion_binds.push_back(onion_service_target);
|
connOptions.onion_binds.push_back(onion_service_target);
|
||||||
|
28
src/net.cpp
28
src/net.cpp
@ -3202,24 +3202,36 @@ bool CConnman::Bind(const CService& addr_, unsigned int flags, NetPermissionFlag
|
|||||||
|
|
||||||
bool CConnman::InitBinds(const Options& options)
|
bool CConnman::InitBinds(const Options& options)
|
||||||
{
|
{
|
||||||
bool fBound = false;
|
|
||||||
for (const auto& addrBind : options.vBinds) {
|
for (const auto& addrBind : options.vBinds) {
|
||||||
fBound |= Bind(addrBind, BF_REPORT_ERROR, NetPermissionFlags::None);
|
if (!Bind(addrBind, BF_REPORT_ERROR, NetPermissionFlags::None)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for (const auto& addrBind : options.vWhiteBinds) {
|
for (const auto& addrBind : options.vWhiteBinds) {
|
||||||
fBound |= Bind(addrBind.m_service, BF_REPORT_ERROR, addrBind.m_flags);
|
if (!Bind(addrBind.m_service, BF_REPORT_ERROR, addrBind.m_flags)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for (const auto& addr_bind : options.onion_binds) {
|
for (const auto& addr_bind : options.onion_binds) {
|
||||||
fBound |= Bind(addr_bind, BF_DONT_ADVERTISE, NetPermissionFlags::None);
|
if (!Bind(addr_bind, BF_REPORT_ERROR | BF_DONT_ADVERTISE, NetPermissionFlags::None)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (options.bind_on_any) {
|
if (options.bind_on_any) {
|
||||||
|
// Don't consider errors to bind on IPv6 "::" fatal because the host OS
|
||||||
|
// may not have IPv6 support and the user did not explicitly ask us to
|
||||||
|
// bind on that.
|
||||||
|
const CService ipv6_any{in6_addr(IN6ADDR_ANY_INIT), GetListenPort()}; // ::
|
||||||
|
Bind(ipv6_any, BF_NONE, NetPermissionFlags::None);
|
||||||
|
|
||||||
struct in_addr inaddr_any;
|
struct in_addr inaddr_any;
|
||||||
inaddr_any.s_addr = htonl(INADDR_ANY);
|
inaddr_any.s_addr = htonl(INADDR_ANY);
|
||||||
struct in6_addr inaddr6_any = IN6ADDR_ANY_INIT;
|
const CService ipv4_any{inaddr_any, GetListenPort()}; // 0.0.0.0
|
||||||
fBound |= Bind(CService(inaddr6_any, GetListenPort()), BF_NONE, NetPermissionFlags::None);
|
if (!Bind(ipv4_any, BF_REPORT_ERROR, NetPermissionFlags::None)) {
|
||||||
fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE, NetPermissionFlags::None);
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return fBound;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
|
bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
|
||||||
|
@ -27,7 +27,7 @@ class BindExtraTest(BitcoinTestFramework):
|
|||||||
# Avoid any -bind= on the command line. Force the framework to avoid
|
# Avoid any -bind= on the command line. Force the framework to avoid
|
||||||
# adding -bind=127.0.0.1.
|
# adding -bind=127.0.0.1.
|
||||||
self.bind_to_localhost_only = False
|
self.bind_to_localhost_only = False
|
||||||
self.num_nodes = 2
|
self.num_nodes = 3
|
||||||
|
|
||||||
def skip_test_if_missing_module(self):
|
def skip_test_if_missing_module(self):
|
||||||
# Due to OS-specific network stats queries, we only run on Linux.
|
# Due to OS-specific network stats queries, we only run on Linux.
|
||||||
@ -60,14 +60,21 @@ class BindExtraTest(BitcoinTestFramework):
|
|||||||
)
|
)
|
||||||
port += 2
|
port += 2
|
||||||
|
|
||||||
|
# Node2, no -bind=...=onion, thus no extra port for Tor target.
|
||||||
|
self.expected.append(
|
||||||
|
[
|
||||||
|
[f"-bind=127.0.0.1:{port}"],
|
||||||
|
[(loopback_ipv4, port)]
|
||||||
|
],
|
||||||
|
)
|
||||||
|
port += 1
|
||||||
|
|
||||||
self.extra_args = list(map(lambda e: e[0], self.expected))
|
self.extra_args = list(map(lambda e: e[0], self.expected))
|
||||||
self.add_nodes(self.num_nodes, self.extra_args)
|
self.setup_nodes()
|
||||||
# Don't start the nodes, as some of them would collide trying to bind on the same port.
|
|
||||||
|
|
||||||
def run_test(self):
|
def run_test(self):
|
||||||
for i in range(len(self.expected)):
|
for i, (args, expected_services) in enumerate(self.expected):
|
||||||
self.log.info(f"Starting node {i} with {self.expected[i][0]}")
|
self.log.info(f"Checking listening ports of node {i} with {args}")
|
||||||
self.start_node(i)
|
|
||||||
pid = self.nodes[i].process.pid
|
pid = self.nodes[i].process.pid
|
||||||
binds = set(get_bind_addrs(pid))
|
binds = set(get_bind_addrs(pid))
|
||||||
# Remove IPv6 addresses because on some CI environments "::1" is not configured
|
# Remove IPv6 addresses because on some CI environments "::1" is not configured
|
||||||
@ -78,9 +85,7 @@ class BindExtraTest(BitcoinTestFramework):
|
|||||||
binds = set(filter(lambda e: len(e[0]) != ipv6_addr_len_bytes, binds))
|
binds = set(filter(lambda e: len(e[0]) != ipv6_addr_len_bytes, binds))
|
||||||
# Remove RPC ports. They are not relevant for this test.
|
# Remove RPC ports. They are not relevant for this test.
|
||||||
binds = set(filter(lambda e: e[1] != rpc_port(i), binds))
|
binds = set(filter(lambda e: e[1] != rpc_port(i), binds))
|
||||||
assert_equal(binds, set(self.expected[i][1]))
|
assert_equal(binds, set(expected_services))
|
||||||
self.stop_node(i)
|
|
||||||
self.log.info(f"Stopped node {i}")
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
BindExtraTest().main()
|
BindExtraTest().main()
|
||||||
|
@ -169,7 +169,7 @@ can be called after the TestShell is shut down.
|
|||||||
|
|
||||||
| Test parameter key | Default Value | Description |
|
| Test parameter key | Default Value | Description |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `bind_to_localhost_only` | `True` | Binds bitcoind RPC services to `127.0.0.1` if set to `True`.|
|
| `bind_to_localhost_only` | `True` | Binds bitcoind P2P services to `127.0.0.1` if set to `True`.|
|
||||||
| `cachedir` | `"/path/to/bitcoin/test/cache"` | Sets the bitcoind datadir directory. |
|
| `cachedir` | `"/path/to/bitcoin/test/cache"` | Sets the bitcoind datadir directory. |
|
||||||
| `chain` | `"regtest"` | Sets the chain-type for the underlying test bitcoind processes. |
|
| `chain` | `"regtest"` | Sets the chain-type for the underlying test bitcoind processes. |
|
||||||
| `configfile` | `"/path/to/bitcoin/test/config.ini"` | Sets the location of the test framework config file. |
|
| `configfile` | `"/path/to/bitcoin/test/config.ini"` | Sets the location of the test framework config file. |
|
||||||
|
@ -39,6 +39,7 @@ from .util import (
|
|||||||
rpc_url,
|
rpc_url,
|
||||||
wait_until_helper_internal,
|
wait_until_helper_internal,
|
||||||
p2p_port,
|
p2p_port,
|
||||||
|
tor_port,
|
||||||
)
|
)
|
||||||
|
|
||||||
BITCOIND_PROC_WAIT_TIMEOUT = 60
|
BITCOIND_PROC_WAIT_TIMEOUT = 60
|
||||||
@ -88,8 +89,11 @@ class TestNode():
|
|||||||
self.coverage_dir = coverage_dir
|
self.coverage_dir = coverage_dir
|
||||||
self.cwd = cwd
|
self.cwd = cwd
|
||||||
self.descriptors = descriptors
|
self.descriptors = descriptors
|
||||||
|
self.has_explicit_bind = False
|
||||||
if extra_conf is not None:
|
if extra_conf is not None:
|
||||||
append_config(self.datadir_path, extra_conf)
|
append_config(self.datadir_path, extra_conf)
|
||||||
|
# Remember if there is bind=... in the config file.
|
||||||
|
self.has_explicit_bind = any(e.startswith("bind=") for e in extra_conf)
|
||||||
# Most callers will just need to add extra args to the standard list below.
|
# Most callers will just need to add extra args to the standard list below.
|
||||||
# For those callers that need more flexibility, they can just set the args property directly.
|
# For those callers that need more flexibility, they can just set the args property directly.
|
||||||
# Note that common args are set in the config file (see initialize_datadir)
|
# Note that common args are set in the config file (see initialize_datadir)
|
||||||
@ -210,6 +214,17 @@ class TestNode():
|
|||||||
if extra_args is None:
|
if extra_args is None:
|
||||||
extra_args = self.extra_args
|
extra_args = self.extra_args
|
||||||
|
|
||||||
|
# If listening and no -bind is given, then bitcoind would bind P2P ports on
|
||||||
|
# 0.0.0.0:P and 127.0.0.1:18445 (for incoming Tor connections), where P is
|
||||||
|
# a unique port chosen by the test framework and configured as port=P in
|
||||||
|
# bitcoin.conf. To avoid collisions on 127.0.0.1:18445, change it to
|
||||||
|
# 127.0.0.1:tor_port().
|
||||||
|
will_listen = all(e != "-nolisten" and e != "-listen=0" for e in extra_args)
|
||||||
|
has_explicit_bind = self.has_explicit_bind or any(e.startswith("-bind=") for e in extra_args)
|
||||||
|
if will_listen and not has_explicit_bind:
|
||||||
|
extra_args.append(f"-bind=0.0.0.0:{p2p_port(self.index)}")
|
||||||
|
extra_args.append(f"-bind=127.0.0.1:{tor_port(self.index)}=onion")
|
||||||
|
|
||||||
self.use_v2transport = "-v2transport=1" in extra_args or (self.default_to_v2 and "-v2transport=0" not in extra_args)
|
self.use_v2transport = "-v2transport=1" in extra_args or (self.default_to_v2 and "-v2transport=0" not in extra_args)
|
||||||
|
|
||||||
# Add a new stdout and stderr file each time bitcoind is started
|
# Add a new stdout and stderr file each time bitcoind is started
|
||||||
|
@ -316,9 +316,9 @@ def sha256sum_file(filename):
|
|||||||
|
|
||||||
# The maximum number of nodes a single test can spawn
|
# The maximum number of nodes a single test can spawn
|
||||||
MAX_NODES = 12
|
MAX_NODES = 12
|
||||||
# Don't assign rpc or p2p ports lower than this
|
# Don't assign p2p, rpc or tor ports lower than this
|
||||||
PORT_MIN = int(os.getenv('TEST_RUNNER_PORT_MIN', default=11000))
|
PORT_MIN = int(os.getenv('TEST_RUNNER_PORT_MIN', default=11000))
|
||||||
# The number of ports to "reserve" for p2p and rpc, each
|
# The number of ports to "reserve" for p2p, rpc and tor, each
|
||||||
PORT_RANGE = 5000
|
PORT_RANGE = 5000
|
||||||
|
|
||||||
|
|
||||||
@ -358,7 +358,11 @@ def p2p_port(n):
|
|||||||
|
|
||||||
|
|
||||||
def rpc_port(n):
|
def rpc_port(n):
|
||||||
return PORT_MIN + PORT_RANGE + n + (MAX_NODES * PortSeed.n) % (PORT_RANGE - 1 - MAX_NODES)
|
return p2p_port(n) + PORT_RANGE
|
||||||
|
|
||||||
|
|
||||||
|
def tor_port(n):
|
||||||
|
return p2p_port(n) + PORT_RANGE * 2
|
||||||
|
|
||||||
|
|
||||||
def rpc_url(datadir, i, chain, rpchost):
|
def rpc_url(datadir, i, chain, rpchost):
|
||||||
|
Loading…
Reference in New Issue
Block a user