Skip to content

Commit 2c8a478

Browse files
committed
Merge bitcoin/bitcoin#33231: net: Prevent node from binding to the same CService
4d4789d net: Prevent node from binding to the same CService (woltx) Pull request description: Currently, if the node inadvertently starts with repeated `-bind` options (e.g. `./build/bin/bitcoind -listen -bind=0.0.0.0 -bind=0.0.0.0`), the user will receive a misleading message followed by the node shutdown: ``` [net:error] Unable to bind to 0.0.0.0:8333 on this computer. Bitcoin Core is probably already running. [error] Unable to bind to 0.0.0.0:8333 on this computer. Bitcoin Core is probably already running. ``` And the user might spend some time looking for a `bitcoind` process or what application is using port 8333, when what happens is that Bitcoin Core successfully connected to port 8333 and then tries again, generating this fatal error. This PR proposes that repeated `-bind` options have no effect. ACKs for top commit: l0rinc: ACK 4d4789d yuvicc: re-ACK 4d4789d sipa: utACK 4d4789d achow101: ACK 4d4789d vasild: ACK 4d4789d naiyoma: Tested ACK 4d4789d Tree-SHA512: f1042c00417da16550403cfcb75cb8b12740e67cf92a1d8e3c007ae81fcf741907088a633129ce12a6a48ad07fc9f320602792cafed73ec33f6306cd854514b4
2 parents 591eea7 + 4d4789d commit 2c8a478

File tree

2 files changed

+55
-3
lines changed

2 files changed

+55
-3
lines changed

src/init.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1233,6 +1233,40 @@ bool CheckHostPortOptions(const ArgsManager& args) {
12331233
return true;
12341234
}
12351235

1236+
/**
1237+
* @brief Checks for duplicate bindings across all binding configurations.
1238+
*
1239+
* @param[in] conn_options Connection options containing the binding vectors to check
1240+
* @return std::optional<CService> containing the first duplicate found, or std::nullopt if no duplicates
1241+
*/
1242+
static std::optional<CService> CheckBindingConflicts(const CConnman::Options& conn_options)
1243+
{
1244+
std::set<CService> seen;
1245+
1246+
// Check all whitelisted bindings
1247+
for (const auto& wb : conn_options.vWhiteBinds) {
1248+
if (!seen.insert(wb.m_service).second) {
1249+
return wb.m_service;
1250+
}
1251+
}
1252+
1253+
// Check regular bindings
1254+
for (const auto& bind : conn_options.vBinds) {
1255+
if (!seen.insert(bind).second) {
1256+
return bind;
1257+
}
1258+
}
1259+
1260+
// Check onion bindings
1261+
for (const auto& onion_bind : conn_options.onion_binds) {
1262+
if (!seen.insert(onion_bind).second) {
1263+
return onion_bind;
1264+
}
1265+
}
1266+
1267+
return std::nullopt;
1268+
}
1269+
12361270
// A GUI user may opt to retry once with do_reindex set if there is a failure during chainstate initialization.
12371271
// The function therefore has to support re-entry.
12381272
static ChainstateLoadResult InitAndLoadChainstate(
@@ -2142,6 +2176,13 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
21422176

21432177
connOptions.m_i2p_accept_incoming = args.GetBoolArg("-i2pacceptincoming", DEFAULT_I2P_ACCEPT_INCOMING);
21442178

2179+
if (auto conflict = CheckBindingConflicts(connOptions)) {
2180+
return InitError(strprintf(
2181+
_("Duplicate binding configuration for address %s. "
2182+
"Please check your -bind, -bind=...=onion and -whitebind settings."),
2183+
conflict->ToStringAddrPort()));
2184+
}
2185+
21452186
if (!node.connman->Start(scheduler, connOptions)) {
21462187
return false;
21472188
}

test/functional/feature_bind_extra.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,26 @@
33
# Distributed under the MIT software license, see the accompanying
44
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
55
"""
6-
Test starting bitcoind with -bind and/or -bind=...=onion and confirm
7-
that bind happens on the expected ports.
6+
Test starting bitcoind with -bind and/or -bind=...=onion, confirm that
7+
it binds to the expected ports, and verify that duplicate or conflicting
8+
-bind/-whitebind configurations are rejected with a descriptive error.
89
"""
910

11+
from itertools import combinations_with_replacement
1012
from test_framework.netutil import (
1113
addr_to_hex,
1214
get_bind_addrs,
1315
)
1416
from test_framework.test_framework import (
1517
BitcoinTestFramework,
1618
)
19+
from test_framework.test_node import ErrorMatch
1720
from test_framework.util import (
1821
assert_equal,
1922
p2p_port,
2023
rpc_port,
2124
)
2225

23-
2426
class BindExtraTest(BitcoinTestFramework):
2527
def set_test_params(self):
2628
self.setup_clean_chain = True
@@ -87,5 +89,14 @@ def run_test(self):
8789
binds = set(filter(lambda e: e[1] != rpc_port(i), binds))
8890
assert_equal(binds, set(expected_services))
8991

92+
self.stop_node(0)
93+
94+
addr = "127.0.0.1:11012"
95+
for opt1, opt2 in combinations_with_replacement([f"-bind={addr}", f"-bind={addr}=onion", f"-whitebind=noban@{addr}"], 2):
96+
self.nodes[0].assert_start_raises_init_error(
97+
[opt1, opt2],
98+
"Error: Duplicate binding configuration",
99+
match=ErrorMatch.PARTIAL_REGEX)
100+
90101
if __name__ == '__main__':
91102
BindExtraTest(__file__).main()

0 commit comments

Comments
 (0)