net: require P2P binds to succeed

In the Tor case, this prevents us from telling the Tor daemon to send
our incoming connections from the Tor network to an address where we
do not listen (we tried to listen but failed probably because another
application is already listening).

In the other cases (IPv4/IPv6 binds) this also prevents unpleasant
surprises caused by continuing operations even on bind failure. For
example, another application may be listening on portX, bitcoind tries
to bind on portX and portY, only succeeds with portY and continues
operation leaving the user thinking that his bitcoind is listening on
portX whereas another application is listening (the error message in
the log could easily be missed).

Avoid having the functional testing framework start multiple `bitcoind`s
that try to listen on the same `127.0.0.1:18445` (Tor listen for
regtest) if `bind_to_localhost_only` is set to `False`.

Also fix a typo in `test-shell.md` related to `bind_to_localhost_only`.

Fixes https://github.com/bitcoin/bitcoin/issues/22727
This commit is contained in:
Vasil Dimov
2021-08-13 10:33:24 +02:00
parent af552534ab
commit bca346a970
4 changed files with 43 additions and 12 deletions

View File

@@ -3198,24 +3198,36 @@ bool CConnman::Bind(const CService& addr_, unsigned int flags, NetPermissionFlag
bool CConnman::InitBinds(const Options& options)
{
bool fBound = false;
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) {
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) {
fBound |= Bind(addr_bind, BF_REPORT_ERROR | BF_DONT_ADVERTISE, NetPermissionFlags::None);
if (!Bind(addr_bind, BF_REPORT_ERROR | BF_DONT_ADVERTISE, NetPermissionFlags::None)) {
return false;
}
}
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;
inaddr_any.s_addr = htonl(INADDR_ANY);
struct in6_addr inaddr6_any = IN6ADDR_ANY_INIT;
fBound |= Bind(CService(inaddr6_any, GetListenPort()), BF_NONE, NetPermissionFlags::None);
fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE, NetPermissionFlags::None);
const CService ipv4_any{inaddr_any, GetListenPort()}; // 0.0.0.0
if (!Bind(ipv4_any, BF_REPORT_ERROR, NetPermissionFlags::None)) {
return false;
}
}
return fBound;
return true;
}
bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)