From 634f2838ee137234093a1fc89162f395366a387f Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Thu, 18 Nov 2021 09:19:09 +0100 Subject: [PATCH] net: open p2p connections to nodes that listen on non-default ports By default, for mainnet, the p2p listening port is 8333. Bitcoin Core has a strong preference for only connecting to nodes that listen on that port. Remove that preference because connections over clearnet that involve port 8333 make it easy to detect, analyze, block or divert Bitcoin p2p traffic before the connection is even established (at TCP SYN time). For further justification see the OP of: https://github.com/bitcoin/bitcoin/pull/23306 --- src/init.cpp | 9 +++- src/net.cpp | 8 +--- src/netbase.cpp | 88 ++++++++++++++++++++++++++++++++++++++ src/netbase.h | 13 ++++++ src/test/netbase_tests.cpp | 20 +++++++++ 5 files changed, 131 insertions(+), 7 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index f659de3a020391..94bbef612eca8f 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -444,7 +444,7 @@ void SetupServerArgs(ArgsManager& argsman) argsman.AddArg("-peerbloomfilters", strprintf("Support filtering of blocks and transaction with bloom filters (default: %u)", DEFAULT_PEERBLOOMFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-peerblockfilters", strprintf("Serve compact block filters to peers per BIP 157 (default: %u)", DEFAULT_PEERBLOCKFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-permitbaremultisig", strprintf("Relay non-P2SH multisig (default: %u)", DEFAULT_PERMIT_BAREMULTISIG), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); - argsman.AddArg("-port=", strprintf("Listen for connections on . Nodes not using the default ports (default: %u, testnet: %u, signet: %u, regtest: %u) are unlikely to get incoming connections. Not relevant for I2P (see doc/i2p.md).", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort(), signetChainParams->GetDefaultPort(), regtestChainParams->GetDefaultPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION); + argsman.AddArg("-port=", "Listen for connections on . Not relevant for I2P (see doc/i2p.md).", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION); argsman.AddArg("-proxy=", "Connect through SOCKS5 proxy, set -noproxy to disable (default: disabled)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-proxyrandomize", strprintf("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)", DEFAULT_PROXYRANDOMIZE), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-seednode=", "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); @@ -1770,6 +1770,13 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) if (index == std::string::npos) { if (Lookup(bind_arg, bind_addr, GetListenPort(), false)) { connOptions.vBinds.push_back(bind_addr); + if (IsBadPort(bind_addr.GetPort())) { + InitWarning(strprintf( + _("-bind request to listen on port %u. This port is considered \"bad\" and " + "thus it is unlikely that any Bitcoin Core peers connect to it. See " + "https://fetch.spec.whatwg.org/#port-blocking"), + bind_addr.GetPort())); + } continue; } } else { diff --git a/src/net.cpp b/src/net.cpp index d1f1b54007228f..869364d10dff82 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2084,12 +2084,8 @@ void CConnman::ThreadOpenConnections(const std::vector connect) continue; } - // Do not allow non-default ports, unless after 50 invalid - // addresses selected already. This is to prevent malicious peers - // from advertising themselves as a service on another host and - // port, causing a DoS attack as nodes around the network attempt - // to connect to it fruitlessly. - if (addr.GetPort() != Params().GetDefaultPort(addr.GetNetwork()) && nTries < 50) { + // Do not connect to bad ports, unless 50 invalid addresses have been selected already. + if (IsBadPort(addr.GetPort()) && (addr.IsIPv4() || addr.IsIPv6()) && nTries < 50) { continue; } diff --git a/src/netbase.cpp b/src/netbase.cpp index 6191f25cd91d66..010ff415c556e6 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -755,3 +755,91 @@ void InterruptSocks5(bool interrupt) { interruptSocks5Recv = interrupt; } + +bool IsBadPort(uint16_t port) +{ + switch (port) { + case 1: // tcpmux + case 7: // echo + case 9: // discard + case 11: // systat + case 13: // daytime + case 15: // netstat + case 17: // qotd + case 19: // chargen + case 20: // ftp data + case 21: // ftp access + case 22: // ssh + case 23: // telnet + case 25: // smtp + case 37: // time + case 42: // name + case 43: // nicname + case 53: // domain + case 69: // tftp + case 77: // priv-rjs + case 79: // finger + case 87: // ttylink + case 95: // supdup + case 101: // hostname + case 102: // iso-tsap + case 103: // gppitnp + case 104: // acr-nema + case 109: // pop2 + case 110: // pop3 + case 111: // sunrpc + case 113: // auth + case 115: // sftp + case 117: // uucp-path + case 119: // nntp + case 123: // NTP + case 135: // loc-srv /epmap + case 137: // netbios + case 139: // netbios + case 143: // imap2 + case 161: // snmp + case 179: // BGP + case 389: // ldap + case 427: // SLP (Also used by Apple Filing Protocol) + case 465: // smtp+ssl + case 512: // print / exec + case 513: // login + case 514: // shell + case 515: // printer + case 526: // tempo + case 530: // courier + case 531: // chat + case 532: // netnews + case 540: // uucp + case 548: // AFP (Apple Filing Protocol) + case 554: // rtsp + case 556: // remotefs + case 563: // nntp+ssl + case 587: // smtp (rfc6409) + case 601: // syslog-conn (rfc3195) + case 636: // ldap+ssl + case 989: // ftps-data + case 990: // ftps + case 993: // ldap+ssl + case 995: // pop3+ssl + case 1719: // h323gatestat + case 1720: // h323hostcall + case 1723: // pptp + case 2049: // nfs + case 3659: // apple-sasl / PasswordServer + case 4045: // lockd + case 5060: // sip + case 5061: // sips + case 6000: // X11 + case 6566: // sane-port + case 6665: // Alternate IRC [Apple addition] + case 6666: // Alternate IRC [Apple addition] + case 6667: // Standard IRC [Apple addition] + case 6668: // Alternate IRC [Apple addition] + case 6669: // Alternate IRC [Apple addition] + case 6697: // IRC + TLS + case 10080: // Amanda + return true; + } + return false; +} diff --git a/src/netbase.h b/src/netbase.h index 6a87c338a07b99..eca42db6065fda 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -246,4 +246,17 @@ void InterruptSocks5(bool interrupt); */ bool Socks5(const std::string& strDest, uint16_t port, const ProxyCredentials* auth, const Sock& socket); +/** + * Determine if a port is "bad" from the perspective of attempting to connect + * to a node on that port. + * See + * https://fetch.spec.whatwg.org/#port-blocking + * https://chromium.googlesource.com/chromium/src.git/+/refs/heads/main/net/base/port_util.cc + * https://hg.mozilla.org/mozilla-central/file/tip/netwerk/base/nsIOService.cpp + * https://github.com/bitcoin/bitcoin/pull/23306#issuecomment-947516736 + * @param[in] port Port to check. + * @return true if the port is bad + */ +bool IsBadPort(uint16_t port); + #endif // BITCOIN_NETBASE_H diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp index b6d7496cc777d9..8e5e623193ffa7 100644 --- a/src/test/netbase_tests.cpp +++ b/src/test/netbase_tests.cpp @@ -576,4 +576,24 @@ BOOST_AUTO_TEST_CASE(caddress_unserialize_v2) BOOST_CHECK(fixture_addresses == addresses_unserialized); } +BOOST_AUTO_TEST_CASE(isbadport) +{ + BOOST_CHECK(IsBadPort(1)); + BOOST_CHECK(IsBadPort(22)); + BOOST_CHECK(IsBadPort(6000)); + + BOOST_CHECK(!IsBadPort(80)); + BOOST_CHECK(!IsBadPort(443)); + BOOST_CHECK(!IsBadPort(8333)); + + // Check all ports, there must be 80 bad ports in total. + size_t total_bad_ports{0}; + for (uint16_t port = std::numeric_limits::max(); port > 0; --port) { + if (IsBadPort(port)) { + ++total_bad_ports; + } + } + BOOST_CHECK_EQUAL(total_bad_ports, 80); +} + BOOST_AUTO_TEST_SUITE_END()