diff --git a/doc/README.md b/doc/README.md index 16dab17218dcb..a55addbf1421e 100644 --- a/doc/README.md +++ b/doc/README.md @@ -76,6 +76,7 @@ The Dash Core repo's [root README](/README.md) contains relevant information on - [I2P Support](i2p.md) - [Init Scripts (systemd/upstart/openrc)](init.md) - [Managing Wallets](managing-wallets.md) +- [P2P bad ports definition and list](p2p-bad-ports.md) - [PSBT support](psbt.md) - [Reduce Memory](reduce-memory.md) - [Reduce Traffic](reduce-traffic.md) diff --git a/doc/p2p-bad-ports.md b/doc/p2p-bad-ports.md new file mode 100644 index 0000000000000..0e4342ac56860 --- /dev/null +++ b/doc/p2p-bad-ports.md @@ -0,0 +1,114 @@ +When Dash Core automatically opens outgoing P2P connections, it chooses +a peer (address and port) from its list of potential peers. This list is +populated with unchecked data gossiped over the P2P network by other peers. + +A malicious actor may gossip an address:port where no Dash node is listening, +or one where a service is listening that is not related to the Dash network. +As a result, this service may occasionally get connection attempts from Dash +nodes. + +"Bad" ports are ones used by services which are usually not open to the public +and usually require authentication. A connection attempt (by Dash Core, +trying to connect because it thinks there is a Dash node on that +address:port) to such service may be considered a malicious action by an +ultra-paranoid administrator. An example for such a port is 22 (ssh). On the +other hand, connection attempts to public services that usually do not require +authentication are unlikely to be considered a malicious action, +e.g. port 80 (http). + +Below is a list of "bad" ports which Dash Core avoids when choosing a peer to +connect to. If a node is listening on such a port, it will likely receive fewer +incoming connections. + + 1: tcpmux + 7: echo + 9: discard + 11: systat + 13: daytime + 15: netstat + 17: qotd + 19: chargen + 20: ftp data + 21: ftp access + 22: ssh + 23: telnet + 25: smtp + 37: time + 42: name + 43: nicname + 53: domain + 69: tftp + 77: priv-rjs + 79: finger + 87: ttylink + 95: supdup + 101: hostname + 102: iso-tsap + 103: gppitnp + 104: acr-nema + 109: pop2 + 110: pop3 + 111: sunrpc + 113: auth + 115: sftp + 117: uucp-path + 119: nntp + 123: NTP + 135: loc-srv /epmap + 137: netbios + 139: netbios + 143: imap2 + 161: snmp + 179: BGP + 389: ldap + 427: SLP (Also used by Apple Filing Protocol) + 465: smtp+ssl + 512: print / exec + 513: login + 514: shell + 515: printer + 526: tempo + 530: courier + 531: chat + 532: netnews + 540: uucp + 548: AFP (Apple Filing Protocol) + 554: rtsp + 556: remotefs + 563: nntp+ssl + 587: smtp (rfc6409) + 601: syslog-conn (rfc3195) + 636: ldap+ssl + 989: ftps-data + 990: ftps + 993: ldap+ssl + 995: pop3+ssl + 1719: h323gatestat + 1720: h323hostcall + 1723: pptp + 2049: nfs + 3659: apple-sasl / PasswordServer + 4045: lockd + 5060: sip + 5061: sips + 6000: X11 + 6566: sane-port + 6665: Alternate IRC + 6666: Alternate IRC + 6667: Standard IRC + 6668: Alternate IRC + 6669: Alternate IRC + 6697: IRC + TLS + 10080: Amanda + +For further information see: + +[pull/23306](https://github.com/bitcoin/bitcoin/pull/23306#issuecomment-947516736) + +[pull/23542](https://github.com/bitcoin/bitcoin/pull/23542) + +[fetch.spec.whatwg.org](https://fetch.spec.whatwg.org/#port-blocking) + +[chromium.googlesource.com](https://chromium.googlesource.com/chromium/src.git/+/refs/heads/main/net/base/port_util.cc) + +[hg.mozilla.org](https://hg.mozilla.org/mozilla-central/file/tip/netwerk/base/nsIOService.cpp) diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 43790e9ac4d6e..82b098ce9906d 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -433,8 +433,8 @@ class NetinfoRequestHandler : public BaseRequestHandler bool is_addr_relay_enabled; bool is_bip152_hb_from; bool is_bip152_hb_to; - bool is_block_relay; bool is_outbound; + bool is_tx_relay; bool operator<(const Peer& rhs) const { return std::tie(is_outbound, min_ping) < std::tie(rhs.is_outbound, rhs.min_ping); } }; std::vector m_peers; @@ -498,13 +498,13 @@ class NetinfoRequestHandler : public BaseRequestHandler const int8_t network_id{NetworkStringToId(network)}; if (network_id == UNKNOWN_NETWORK) continue; const bool is_outbound{!peer["inbound"].get_bool()}; - const bool is_block_relay{peer["relaytxes"].isNull() ? false : !peer["relaytxes"].get_bool()}; + const bool is_tx_relay{peer["relaytxes"].isNull() ? true : peer["relaytxes"].get_bool()}; const std::string conn_type{peer["connection_type"].get_str()}; ++m_counts.at(is_outbound).at(network_id); // in/out by network ++m_counts.at(is_outbound).at(NETWORKS.size()); // in/out overall ++m_counts.at(2).at(network_id); // total by network ++m_counts.at(2).at(NETWORKS.size()); // total overall - if (is_block_relay) ++m_block_relay_peers_count; + if (conn_type == "block-relay-only") ++m_block_relay_peers_count; if (conn_type == "manual") ++m_manual_peers_count; if (DetailsRequested()) { // Push data for this peer to the peers vector. @@ -527,7 +527,7 @@ class NetinfoRequestHandler : public BaseRequestHandler const bool is_addr_relay_enabled{peer["addr_relay_enabled"].isNull() ? false : peer["addr_relay_enabled"].get_bool()}; const bool is_bip152_hb_from{peer["bip152_hb_from"].get_bool()}; const bool is_bip152_hb_to{peer["bip152_hb_to"].get_bool()}; - m_peers.push_back({addr, sub_version, conn_type, NETWORK_SHORT_NAMES[network_id], age, transport, min_ping, ping, addr_processed, addr_rate_limited, last_blck, last_recv, last_send, last_trxn, peer_id, mapped_as, version, is_addr_relay_enabled, is_bip152_hb_from, is_bip152_hb_to, is_block_relay, is_outbound}); + m_peers.push_back({addr, sub_version, conn_type, NETWORK_SHORT_NAMES[network_id], age, transport, min_ping, ping, addr_processed, addr_rate_limited, last_blck, last_recv, last_send, last_trxn, peer_id, mapped_as, version, is_addr_relay_enabled, is_bip152_hb_from, is_bip152_hb_to, is_outbound, is_tx_relay}); m_max_addr_length = std::max(addr.length() + 1, m_max_addr_length); m_max_addr_processed_length = std::max(ToString(addr_processed).length(), m_max_addr_processed_length); m_max_addr_rate_limited_length = std::max(ToString(addr_rate_limited).length(), m_max_addr_rate_limited_length); @@ -561,7 +561,7 @@ class NetinfoRequestHandler : public BaseRequestHandler PingTimeToString(peer.ping), peer.last_send ? ToString(time_now - peer.last_send) : "", peer.last_recv ? ToString(time_now - peer.last_recv) : "", - peer.last_trxn ? ToString((time_now - peer.last_trxn) / 60) : peer.is_block_relay ? "*" : "", + peer.last_trxn ? ToString((time_now - peer.last_trxn) / 60) : peer.is_tx_relay ? "" : "*", peer.last_blck ? ToString((time_now - peer.last_blck) / 60) : "", strprintf("%s%s", peer.is_bip152_hb_to ? "." : " ", peer.is_bip152_hb_from ? "*" : " "), m_max_addr_processed_length, // variable spacing diff --git a/src/compat/compat.h b/src/compat/compat.h index 5dd73eb717caa..d4b315bb1a84a 100644 --- a/src/compat/compat.h +++ b/src/compat/compat.h @@ -109,14 +109,6 @@ typedef char* sockopt_arg_type; #define USE_KQUEUE #endif -bool static inline IsSelectableSocket(const SOCKET& s) { -#if defined(USE_POLL) || defined(WIN32) - return true; -#else - return (s < FD_SETSIZE); -#endif -} - // MSG_NOSIGNAL is not available on some platforms, if it doesn't exist define it as 0 #if !defined(MSG_NOSIGNAL) #define MSG_NOSIGNAL 0 diff --git a/src/init.cpp b/src/init.cpp index 74e7991325661..2accb1ad92b0c 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -567,6 +567,8 @@ void SetupServerArgs(ArgsManager& argsman) argsman.AddArg("-txreconciliation", strprintf("Enable transaction reconciliations per BIP 330 (default: %d)", DEFAULT_TXRECONCILIATION_ENABLE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CONNECTION); argsman.AddArg("-peertimeout=", strprintf("Specify a p2p connection timeout delay in seconds. After connecting to a peer, wait this amount of time before considering disconnection based on inactivity (minimum: 1, default: %d)", DEFAULT_PEER_CONNECT_TIMEOUT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-permitbaremultisig", strprintf("Relay non-P2SH multisig (default: %u)", DEFAULT_PERMIT_BAREMULTISIG), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); + // TODO: remove the sentence "Nodes not using ... incoming connections." once the changes from + // https://github.com/bitcoin/bitcoin/pull/23542 have become widespread. argsman.AddArg("-port=", strprintf("Listen for connections on . 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); argsman.AddArg("-proxy=", "Connect through SOCKS5 proxy, set -noproxy to disable (default: disabled)", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_ELISION, 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); @@ -2271,6 +2273,14 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) const uint16_t default_bind_port = static_cast(args.GetArg("-port", Params().GetDefaultPort())); + const auto BadPortWarning = [](const char* prefix, uint16_t port) { + return strprintf(_("%s request to listen on port %u. This port is considered \"bad\" and " + "thus it is unlikely that any Dash Core peers connect to it. See " + "doc/p2p-bad-ports.md for details and a full list."), + prefix, + port); + }; + for (const std::string& bind_arg : args.GetArgs("-bind")) { std::optional bind_addr; const size_t index = bind_arg.rfind('='); @@ -2278,6 +2288,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) bind_addr = Lookup(bind_arg, default_bind_port, /*fAllowLookup=*/false); if (bind_addr.has_value()) { connOptions.vBinds.push_back(bind_addr.value()); + if (IsBadPort(bind_addr.value().GetPort())) { + InitWarning(BadPortWarning("-bind", bind_addr.value().GetPort())); + } continue; } } else { @@ -2305,6 +2318,15 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) // on any address - 0.0.0.0 (IPv4) and :: (IPv6). connOptions.bind_on_any = args.GetArgs("-bind").empty() && args.GetArgs("-whitebind").empty(); + // Emit a warning if a bad port is given to -port= but only if -bind and -whitebind are not + // given, because if they are, then -port= is ignored. + if (connOptions.bind_on_any && args.IsArgSet("-port")) { + const uint16_t port_arg = args.GetArg("-port", 0); + if (IsBadPort(port_arg)) { + InitWarning(BadPortWarning("-port", port_arg)); + } + } + CService onion_service_target; if (!connOptions.onion_binds.empty()) { onion_service_target = connOptions.onion_binds.front(); diff --git a/src/masternode/node.cpp b/src/masternode/node.cpp index ecf1d8f75a0fa..6f159ee1586a2 100644 --- a/src/masternode/node.cpp +++ b/src/masternode/node.cpp @@ -162,7 +162,7 @@ void CActiveMasternodeManager::InitInternal(const CBlockIndex* pindex) LogPrintf("CActiveMasternodeManager::Init -- ERROR: %s\n", m_error); return; } - bool fConnected = ConnectSocketDirectly(m_info.service, *sock, nConnectTimeout, true) && IsSelectableSocket(sock->Get()); + bool fConnected = ConnectSocketDirectly(m_info.service, *sock, nConnectTimeout, true) && sock->IsSelectable(); sock->Reset(); if (!fConnected && Params().RequireRoutableExternalIP()) { diff --git a/src/net.cpp b/src/net.cpp index de9a5f74cca47..b3d76edc8bcf4 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2020,8 +2020,7 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr&& sock, return; } - if (!IsSelectableSocket(sock->Get())) - { + if (!sock->IsSelectable()) { LogPrintf("%s: non-selectable socket\n", strDropped); return; } @@ -2306,10 +2305,11 @@ void CConnman::NotifyNumConnectionsChanged(CMasternodeSync& mn_sync) mn_sync.Reset(); } - if(nodes_size != nPrevNodeCount) { + if (nodes_size != nPrevNodeCount) { nPrevNodeCount = nodes_size; - if(clientInterface) - clientInterface->NotifyNumConnectionsChanged(nodes_size); + if (m_client_interface) { + m_client_interface->NotifyNumConnectionsChanged(nodes_size); + } CalculateNumConnectionsChangedStats(); } @@ -3140,6 +3140,12 @@ void CConnman::SetTryNewOutboundPeer(bool flag) LogPrint(BCLog::NET, "net: setting try another outbound peer=%s\n", flag ? "true" : "false"); } +void CConnman::StartExtraBlockRelayPeers() +{ + LogPrint(BCLog::NET, "net: enabling extra block-relay-only peers\n"); + m_start_extra_block_relay_peers = true; +} + // Return the number of peers we have over our outbound connection limit // Exclude peers that are marked for disconnect, or are going to be // disconnected soon (eg ADDR_FETCH and FEELER) @@ -3299,7 +3305,7 @@ void CConnman::ThreadOpenConnections(const std::vector connect, CDe // Therefore, we do not add them to addrman in the first place. // In case previously unreachable networks become reachable // (e.g. in case of -onlynet changes by the user), fixed seeds will - // be loaded only for networks for which we have no addressses. + // be loaded only for networks for which we have no addresses. seed_addrs.erase(std::remove_if(seed_addrs.begin(), seed_addrs.end(), [&fixed_seed_networks](const CAddress& addr) { return fixed_seed_networks.count(addr.GetNetwork()) == 0; }), seed_addrs.end()); @@ -3542,12 +3548,26 @@ void CConnman::ThreadOpenConnections(const std::vector connect, CDe 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 ((!isMasternode || !Params().AllowMultiplePorts()) && addr.GetPort() != Params().GetDefaultPort(addr.GetNetwork()) && addr.GetPort() != GetListenPort() && nTries < 50) { + // Port validation in Dash has additional rules. Some networks are prohibited + // from using a non-default port while others allow any arbitary port so long + // it isn't a bad port (and in the case of masternodes, it matches its listen + // port) + const bool is_prohibited_port = [this, &addr, &isMasternode](){ + if (!Params().AllowMultiplePorts()) { + const uint16_t default_port{Params().GetDefaultPort(addr.GetNetwork())}; + assert(!IsBadPort(default_port)); // Make sure we never set the default port to a bad port + return addr.GetPort() != default_port; + } + const bool is_bad_port{IsBadPort(addr.GetPort())}; + if (isMasternode) { + return addr.GetPort() != GetListenPort() || is_bad_port; + } else { + return is_bad_port; + } + }(); + + // Do not connect to prohibited ports, unless 50 invalid addresses have been selected already. + if (nTries < 50 && is_prohibited_port) { continue; } @@ -4196,7 +4216,9 @@ void CConnman::SetNetworkActive(bool active, CMasternodeSync* const mn_sync) mn_sync->Reset(); } - uiInterface.NotifyNetworkActiveChanged(fNetworkActive); + if (m_client_interface) { + m_client_interface->NotifyNetworkActiveChanged(fNetworkActive); + } } CConnman::CConnman(uint64_t nSeed0In, uint64_t nSeed1In, AddrMan& addrman_in, @@ -4225,8 +4247,8 @@ bool CConnman::Bind(const CService& addr_, unsigned int flags, NetPermissionFlag bilingual_str strError; if (!BindListenPort(addr, strError, permissions)) { - if ((flags & BF_REPORT_ERROR) && clientInterface) { - clientInterface->ThreadSafeMessageBox(strError, "", CClientUIInterface::MSG_ERROR); + if ((flags & BF_REPORT_ERROR) && m_client_interface) { + m_client_interface->ThreadSafeMessageBox(strError, "", CClientUIInterface::MSG_ERROR); } return false; } @@ -4276,8 +4298,8 @@ bool CConnman::Start(CDeterministicMNManager& dmnman, CMasternodeMetaMan& mn_met } if (fListen && !InitBinds(connOptions)) { - if (clientInterface) { - clientInterface->ThreadSafeMessageBox( + if (m_client_interface) { + m_client_interface->ThreadSafeMessageBox( _("Failed to listen on any port. Use -listen=0 if you want this."), "", CClientUIInterface::MSG_ERROR); } @@ -4303,7 +4325,9 @@ bool CConnman::Start(CDeterministicMNManager& dmnman, CMasternodeMetaMan& mn_met LogPrintf("%i block-relay-only anchors will be tried for connections.\n", m_anchors.size()); } - uiInterface.InitMessage(_("Starting network threads…").translated); + if (m_client_interface) { + m_client_interface->InitMessage(_("Starting network threads…").translated); + } fAddressesInitialized = true; @@ -4350,8 +4374,8 @@ bool CConnman::Start(CDeterministicMNManager& dmnman, CMasternodeMetaMan& mn_met threadOpenAddedConnections = std::thread(&util::TraceThread, "addcon", [this] { ThreadOpenAddedConnections(); }); if (connOptions.m_use_addrman_outgoing && !connOptions.m_specified_outgoing.empty()) { - if (clientInterface) { - clientInterface->ThreadSafeMessageBox( + if (m_client_interface) { + m_client_interface->ThreadSafeMessageBox( _("Cannot provide specific connections and have addrman find outgoing connections at the same time."), "", CClientUIInterface::MSG_ERROR); } diff --git a/src/net.h b/src/net.h index 4901507a6cda5..b945cedc6c7d6 100644 --- a/src/net.h +++ b/src/net.h @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -1230,7 +1229,7 @@ friend class CNode; nMaxAddnode = connOptions.nMaxAddnode; nMaxFeeler = connOptions.nMaxFeeler; m_max_outbound = m_max_outbound_full_relay + m_max_outbound_block_relay + nMaxFeeler; - clientInterface = connOptions.uiInterface; + m_client_interface = connOptions.uiInterface; m_banman = connOptions.m_banman; m_msgproc = connOptions.m_msgproc; nSendBufferMaxSize = connOptions.nSendBufferMaxSize; @@ -1464,10 +1463,7 @@ friend class CNode; void SetTryNewOutboundPeer(bool flag); bool GetTryNewOutboundPeer() const; - void StartExtraBlockRelayPeers() { - LogPrint(BCLog::NET, "net: enabling extra block-relay-only peers\n"); - m_start_extra_block_relay_peers = true; - } + void StartExtraBlockRelayPeers(); // Return the number of outbound peers we have in excess of our target (eg, // if we previously called SetTryNewOutboundPeer(true), and have since set @@ -1879,7 +1875,7 @@ friend class CNode; int nMaxFeeler; int m_max_outbound; bool m_use_addrman_outgoing; - CClientUIInterface* clientInterface; + CClientUIInterface* m_client_interface; NetEventsInterface* m_msgproc; /** Pointer to this node's banman. May be nullptr - check existence before dereferencing. */ BanMan* m_banman; diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 2753f2a354ec1..7e0c768b33c5c 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -644,6 +645,8 @@ class PeerManagerImpl final : public PeerManager bool IsInvInFilter(NodeId nodeid, const uint256& hash) const override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); void AskPeersForTransaction(const uint256& txid, bool is_masternode) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); private: + void _RelayTransaction(const uint256& txid) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + /** Helpers to process result of external handlers of message */ void ProcessPeerMsgRet(const PeerMsgRet& ret, CNode& pfrom) EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); void PostProcessMessage(MessageProcessingResult&& ret, NodeId node) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); @@ -1453,7 +1456,7 @@ void PeerManagerImpl::PushNodeVersion(CNode& pnode, const Peer& peer) nProtocolVersion = gArgs.GetArg("-pushversion", PROTOCOL_VERSION); } - const bool tx_relay = !m_ignore_incoming_txs && !pnode.IsBlockOnlyConn() && !pnode.IsFeelerConn(); + const bool tx_relay{!RejectIncomingTxs(pnode)}; m_connman.PushMessage(&pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERSION, nProtocolVersion, my_services, nTime, your_services, addr_you, // Together the pre-version-31402 serialization of CAddress "addrYou" (without nTime) my_services, CService(), // Together the pre-version-31402 serialization of CAddress "addrMe" (without nTime) @@ -1628,7 +1631,8 @@ void PeerManagerImpl::ReattemptInitialBroadcast(CScheduler& scheduler) CTransactionRef tx = m_mempool.get(txid); if (tx != nullptr) { - RelayTransaction(txid); + LOCK(cs_main); + _RelayTransaction(txid); } else { m_mempool.RemoveUnbroadcastTx(txid, true); } @@ -2427,6 +2431,11 @@ void PeerManagerImpl::RelayInvFiltered(CInv &inv, const uint256& relatedTxHash, } void PeerManagerImpl::RelayTransaction(const uint256& txid) +{ + WITH_LOCK(cs_main, _RelayTransaction(txid)); +} + +void PeerManagerImpl::_RelayTransaction(const uint256& txid) { const CInv inv{m_cj_ctx->dstxman->GetDSTX(txid) ? MSG_DSTX : MSG_TX, txid}; LOCK(m_peer_mutex); @@ -2465,11 +2474,13 @@ void PeerManagerImpl::RelayAddress(NodeId originator, // Relay to a limited number of other nodes // Use deterministic randomness to send to the same nodes for 24 hours // at a time so the m_addr_knowns of the chosen nodes prevent repeats - const uint64_t hashAddr{addr.GetHash()}; + const uint64_t hash_addr{CServiceHash(0, 0)(addr)}; const auto current_time{GetTime()}; // Adding address hash makes exact rotation time different per address, while preserving periodicity. - const uint64_t time_addr{(static_cast(count_seconds(current_time)) + hashAddr) / count_seconds(ROTATE_ADDR_RELAY_DEST_INTERVAL)}; - const CSipHasher hasher{m_connman.GetDeterministicRandomizer(RANDOMIZER_ID_ADDRESS_RELAY).Write(hashAddr).Write(time_addr)}; + const uint64_t time_addr{(static_cast(count_seconds(current_time)) + hash_addr) / count_seconds(ROTATE_ADDR_RELAY_DEST_INTERVAL)}; + const CSipHasher hasher{m_connman.GetDeterministicRandomizer(RANDOMIZER_ID_ADDRESS_RELAY) + .Write(hash_addr) + .Write(time_addr)}; FastRandomContext insecure_rand; // Relay reachable addresses to 2 peers. Unreachable addresses are relayed randomly to 1 or 2 peers. @@ -3205,7 +3216,7 @@ void PeerManagerImpl::ProcessOrphanTx(std::set& orphan_work_set) if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) { LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString()); - RelayTransaction(porphanTx->GetHash()); + _RelayTransaction(porphanTx->GetHash()); m_orphanage.AddChildrenToWorkSet(*porphanTx, orphan_work_set); m_orphanage.EraseTx(orphanHash); break; @@ -3488,7 +3499,7 @@ void PeerManagerImpl::PostProcessMessage(MessageProcessingResult&& result, NodeI WITH_LOCK(cs_main, EraseObjectRequest(node, result.m_to_erase.value())); } for (const auto& tx : result.m_transactions) { - WITH_LOCK(cs_main, RelayTransaction(tx)); + WITH_LOCK(cs_main, _RelayTransaction(tx)); } if (result.m_inventory) { RelayInv(result.m_inventory.value()); @@ -3661,11 +3672,13 @@ void PeerManagerImpl::ProcessMessage( if (greatest_common_version >= INCREASE_MAX_HEADERS2_VERSION && m_txreconciliation) { // Per BIP-330, we announce txreconciliation support if: // - protocol version per the peer's VERSION message supports INCREASE_MAX_HEADERS2_VERSION; - // - transaction relay is supported per the peer's VERSION message (see m_relays_txs); - // - this is not a block-relay-only connection and not a feeler (see m_relays_txs); + // - transaction relay is supported per the peer's VERSION message + // - this is not a block-relay-only connection and not a feeler // - this is not an addr fetch connection; // - we are not in -blocksonly mode. - if (pfrom.m_relays_txs && !pfrom.IsAddrFetchConn() && !m_ignore_incoming_txs) { + const auto* tx_relay = peer->GetTxRelay(); + if (tx_relay && WITH_LOCK(tx_relay->m_bloom_filter_mutex, return tx_relay->m_relay_txs) && + !pfrom.IsAddrFetchConn() && !m_ignore_incoming_txs) { const uint64_t recon_salt = m_txreconciliation->PreRegisterPeer(pfrom.GetId()); m_connman.PushMessage(&pfrom, msg_maker.Make(NetMsgType::SENDTXRCNCL, TXRECONCILIATION_VERSION, recon_salt)); @@ -3682,39 +3695,20 @@ void PeerManagerImpl::ProcessMessage( m_num_preferred_download_peers += state->fPreferredDownload; } - // Self advertisement & GETADDR logic - if (!pfrom.IsInboundConn() && SetupAddressRelay(pfrom, *peer)) { - // For outbound peers, we try to relay our address (so that other - // nodes can try to find us more quickly, as we have no guarantee - // that an outbound peer is even aware of how to reach us) and do a - // one-time address fetch (to help populate/update our addrman). If - // we're starting up for the first time, our addrman may be pretty - // empty and no one will know who we are, so these mechanisms are - // important to help us connect to the network. - // + // Attempt to initialize address relay for outbound peers and use result + // to decide whether to send GETADDR, so that we don't send it to + // inbound or outbound block-relay-only peers. + bool send_getaddr{false}; + if (!pfrom.IsInboundConn()) { + send_getaddr = SetupAddressRelay(pfrom, *peer); + } + if (send_getaddr) { + // Do a one-time address fetch to help populate/update our addrman. + // If we're starting up for the first time, our addrman may be pretty + // empty, so this mechanism is important to help us connect to the network. // We skip this for block-relay-only peers. We want to avoid // potentially leaking addr information and we do not want to // indicate to the peer that we will participate in addr relay. - if (fListen && !m_chainman.ActiveChainstate().IsInitialBlockDownload()) - { - CAddress addr{GetLocalAddress(pfrom), peer->m_our_services, Now()}; - FastRandomContext insecure_rand; - if (addr.IsRoutable()) - { - LogPrint(BCLog::NET, "ProcessMessages: advertising address %s\n", addr.ToStringAddrPort()); - PushAddress(*peer, addr, insecure_rand); - } else if (IsPeerAddrLocalGood(&pfrom)) { - // Override just the address with whatever the peer sees us as. - // Leave the port in addr as it was returned by GetLocalAddress() - // above, as this is an outbound connection and the peer cannot - // observe our listening port. - addr.SetIP(addrMe); - LogPrint(BCLog::NET, "ProcessMessages: advertising address %s\n", addr.ToStringAddrPort()); - PushAddress(*peer, addr, insecure_rand); - } - } - - // Get recent addresses m_connman.PushMessage(&pfrom, CNetMsgMaker(greatest_common_version).Make(NetMsgType::GETADDR)); peer->m_getaddr_sent = true; // When requesting a getaddr, accept an additional MAX_ADDR_TO_SEND addresses in response @@ -3903,7 +3897,8 @@ void PeerManagerImpl::ProcessMessage( // Peer must not offer us reconciliations if they specified no tx relay support in VERSION. // This flag might also be false in other cases, but the RejectIncomingTxs check above // eliminates them, so that this flag fully represents what we are looking for. - if (!pfrom.m_relays_txs) { + const auto* tx_relay = peer->GetTxRelay(); + if (!tx_relay || !WITH_LOCK(tx_relay->m_bloom_filter_mutex, return tx_relay->m_relay_txs)) { LogPrintLevel(BCLog::NET, BCLog::Level::Debug, "sendtxrcncl received from peer=%d which indicated no tx relay to us; disconnecting\n", pfrom.GetId()); pfrom.fDisconnect = true; return; @@ -4476,7 +4471,7 @@ void PeerManagerImpl::ProcessMessage( LogPrintf("Not relaying non-mempool transaction %s from forcerelay peer=%d\n", tx.GetHash().ToString(), pfrom.GetId()); } else { LogPrintf("Force relaying tx %s from peer=%d\n", tx.GetHash().ToString(), pfrom.GetId()); - RelayTransaction(tx.GetHash()); + _RelayTransaction(tx.GetHash()); } } return; @@ -4493,7 +4488,7 @@ void PeerManagerImpl::ProcessMessage( m_cj_ctx->dstxman->AddDSTX(dstx); } - RelayTransaction(tx.GetHash()); + _RelayTransaction(tx.GetHash()); m_orphanage.AddChildrenToWorkSet(tx, peer->m_orphan_work_set); pfrom.m_last_tx_time = GetTime(); @@ -5741,6 +5736,7 @@ bool PeerManagerImpl::RejectIncomingTxs(const CNode& peer) const { // block-relay-only peers may never send txs to us if (peer.IsBlockOnlyConn()) return true; + if (peer.IsFeelerConn()) return true; // In -blocksonly mode, peers need the 'relay' permission to send txs to us if (m_ignore_incoming_txs && !peer.HasPermission(NetPermissionFlags::Relay)) return true; return false; @@ -5754,8 +5750,9 @@ bool PeerManagerImpl::SetupAddressRelay(const CNode& node, Peer& peer) if (node.IsBlockOnlyConn()) return false; if (!peer.m_addr_relay_enabled.exchange(true)) { - // First addr message we have received from the peer, initialize - // m_addr_known + // During version message processing (non-block-relay-only outbound peers) + // or on first addr-related message we have received (inbound peers), initialize + // m_addr_known. peer.m_addr_known = std::make_unique(5000, 0.001); } diff --git a/src/net_processing.h b/src/net_processing.h index 0bd94f7729304..82b17a33e8378 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -7,7 +7,6 @@ #define BITCOIN_NET_PROCESSING_H #include -#include #include #include @@ -29,8 +28,6 @@ class CTransaction; struct CJContext; struct LLMQContext; -extern RecursiveMutex cs_main; - /** Default for -maxorphantxsize, maximum size in megabytes the orphan map can grow before entries are removed */ static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS_SIZE = 10; // this allows around 100 TXs of max size (and many more of normal size) /** Default number of orphan+recently-replaced txn to keep around for block reconstruction */ @@ -114,8 +111,7 @@ class PeerManager : public CValidationInterface, public NetEventsInterface const int minProtoVersion = MIN_PEER_PROTO_VERSION) = 0; /** Relay transaction to all peers. */ - virtual void RelayTransaction(const uint256& txid) - EXCLUSIVE_LOCKS_REQUIRED(cs_main) = 0; + virtual void RelayTransaction(const uint256& txid) = 0; /** Relay recovered sigs to all interested peers */ virtual void RelayRecoveredSig(const uint256& sigHash) = 0; diff --git a/src/netaddress.cpp b/src/netaddress.cpp index 6466aec6fa2c0..0247e38ebc941 100644 --- a/src/netaddress.cpp +++ b/src/netaddress.cpp @@ -732,14 +732,6 @@ std::vector CNetAddr::GetAddrBytes() const return std::vector(m_addr.begin(), m_addr.end()); } -uint64_t CNetAddr::GetHash() const -{ - uint256 hash = Hash(m_addr); - uint64_t nRet; - memcpy(&nRet, &hash, sizeof(nRet)); - return nRet; -} - // private extensions to enum Network, only returned by GetExtNetwork, // and only used in GetReachabilityFrom static const int NET_TEREDO = NET_MAX; @@ -1108,29 +1100,6 @@ bool CSubNet::IsValid() const return valid; } -bool CSubNet::SanityCheck() const -{ - switch (network.m_net) { - case NET_IPV4: - case NET_IPV6: - break; - case NET_ONION: - case NET_I2P: - case NET_CJDNS: - return true; - case NET_INTERNAL: - case NET_UNROUTABLE: - case NET_MAX: - return false; - } - - for (size_t x = 0; x < network.m_addr.size(); ++x) { - if (network.m_addr[x] & ~netmask[x]) return false; - } - - return true; -} - bool operator==(const CSubNet& a, const CSubNet& b) { return a.valid == b.valid && a.network == b.network && !memcmp(a.netmask, b.netmask, 16); diff --git a/src/netaddress.h b/src/netaddress.h index 4d53ed9694540..b9c0c744e7c73 100644 --- a/src/netaddress.h +++ b/src/netaddress.h @@ -203,7 +203,6 @@ class CNetAddr enum Network GetNetwork() const; std::string ToStringAddr() const; - uint64_t GetHash() const; bool GetInAddr(struct in_addr* pipv4Addr) const; Network GetNetClass() const; @@ -486,8 +485,6 @@ class CSubNet /// Is this value valid? (only used to signal parse errors) bool valid; - bool SanityCheck() const; - public: /** * Construct an invalid subnet (empty, `Match()` always returns false). @@ -564,6 +561,14 @@ class CService : public CNetAddr class CServiceHash { public: + CServiceHash() + : m_salt_k0{GetRand(std::numeric_limits::max())}, + m_salt_k1{GetRand(std::numeric_limits::max())} + { + } + + CServiceHash(uint64_t salt_k0, uint64_t salt_k1) : m_salt_k0{salt_k0}, m_salt_k1{salt_k1} {} + size_t operator()(const CService& a) const noexcept { CSipHasher hasher(m_salt_k0, m_salt_k1); @@ -574,8 +579,8 @@ class CServiceHash } private: - const uint64_t m_salt_k0 = GetRand(std::numeric_limits::max()); - const uint64_t m_salt_k1 = GetRand(std::numeric_limits::max()); + const uint64_t m_salt_k0; + const uint64_t m_salt_k1; }; #endif // BITCOIN_NETADDRESS_H diff --git a/src/netbase.cpp b/src/netbase.cpp index adaf3fa629639..1587e8def0198 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -288,8 +288,7 @@ enum class IntrRecvError { * read. * * @see This function can be interrupted by calling InterruptSocks5(bool). - * Sockets can be made non-blocking with SetSocketNonBlocking(const - * SOCKET&). + * Sockets can be made non-blocking with Sock::SetNonBlocking(). */ static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, const Sock& sock) { @@ -487,7 +486,7 @@ std::unique_ptr CreateSockTCP(const CService& address_family) // Ensure that waiting for I/O on this socket won't result in undefined // behavior. - if (!IsSelectableSocket(sock->Get())) { + if (!sock->IsSelectable()) { LogPrintf("Cannot create connection: non-selectable socket created (fd >= FD_SETSIZE ?)\n"); return nullptr; } @@ -509,7 +508,7 @@ std::unique_ptr CreateSockTCP(const CService& address_family) } // Set the non-blocking option on the socket. - if (!SetSocketNonBlocking(sock->Get())) { + if (!sock->SetNonBlocking()) { LogPrintf("Error setting socket to non-blocking: %s\n", NetworkErrorString(WSAGetLastError())); return nullptr; } @@ -701,22 +700,97 @@ bool LookupSubNet(const std::string& subnet_str, CSubNet& subnet_out) return false; } -bool SetSocketNonBlocking(const SOCKET& hSocket) +void InterruptSocks5(bool interrupt) { -#ifdef WIN32 - u_long nOne = 1; - if (ioctlsocket(hSocket, FIONBIO, &nOne) == SOCKET_ERROR) { -#else - int fFlags = fcntl(hSocket, F_GETFL, 0); - if (fcntl(hSocket, F_SETFL, fFlags | O_NONBLOCK) == SOCKET_ERROR) { -#endif - return false; - } - - return true; + interruptSocks5Recv = interrupt; } -void InterruptSocks5(bool interrupt) +bool IsBadPort(uint16_t port) { - interruptSocks5Recv = interrupt; + /* Don't forget to update doc/p2p-bad-ports.md if you change this list. */ + + 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 + case 6666: // Alternate IRC + case 6667: // Standard IRC + case 6668: // Alternate IRC + case 6669: // Alternate IRC + case 6697: // IRC + TLS + case 10080: // Amanda + return true; + } + return false; } diff --git a/src/netbase.h b/src/netbase.h index 51233967b3e86..58e4ad5d2515b 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -224,8 +224,6 @@ bool ConnectSocketDirectly(const CService &addrConnect, const Sock& sock, int nT */ bool ConnectThroughProxy(const Proxy& proxy, const std::string& strDest, uint16_t port, const Sock& sock, int nTimeout, bool& outProxyConnectionFailed); -/** Enable non-blocking mode for a socket */ -bool SetSocketNonBlocking(const SOCKET& hSocket); void InterruptSocks5(bool interrupt); /** @@ -248,4 +246,13 @@ 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 doc/p2p-bad-ports.md + * @param[in] port Port to check. + * @returns whether the port is bad + */ +bool IsBadPort(uint16_t port); + #endif // BITCOIN_NETBASE_H diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp index e74c58e41712a..5e637b1861638 100644 --- a/src/node/transaction.cpp +++ b/src/node/transaction.cpp @@ -110,7 +110,6 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t } if (relay) { - LOCK(cs_main); node.peerman->RelayTransaction(txid); } diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp index 4259906b5c20b..021398af0e9d0 100644 --- a/src/test/dbwrapper_tests.cpp +++ b/src/test/dbwrapper_tests.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -324,12 +325,6 @@ struct StringContentsSerializer { StringContentsSerializer() {} explicit StringContentsSerializer(const std::string& inp) : str(inp) {} - StringContentsSerializer& operator+=(const std::string& s) { - str += s; - return *this; - } - StringContentsSerializer& operator+=(const StringContentsSerializer& s) { return *this += s.str; } - template void Serialize(Stream& s) const { @@ -343,44 +338,34 @@ struct StringContentsSerializer { { str.clear(); uint8_t c{0}; - while (true) { - try { - s >> c; - str.push_back(c); - } catch (const std::ios_base::failure&) { - break; - } + while (!s.eof()) { + s >> c; + str.push_back(c); } } }; BOOST_AUTO_TEST_CASE(iterator_string_ordering) { - char buf[10]; - fs::path ph = m_args.GetDataDirBase() / "iterator_string_ordering"; CDBWrapper dbw(ph, (1 << 20), true, false, false); - for (int x=0x00; x<10; ++x) { - for (int y = 0; y < 10; y++) { - snprintf(buf, sizeof(buf), "%d", x); - StringContentsSerializer key(buf); - for (int z = 0; z < y; z++) + for (int x = 0; x < 10; ++x) { + for (int y = 0; y < 10; ++y) { + std::string key{ToString(x)}; + for (int z = 0; z < y; ++z) key += key; uint32_t value = x*x; - BOOST_CHECK(dbw.Write(key, value)); + BOOST_CHECK(dbw.Write(StringContentsSerializer{key}, value)); } } std::unique_ptr it(const_cast(dbw).NewIterator()); for (const int seek_start : {0, 5}) { - snprintf(buf, sizeof(buf), "%d", seek_start); - StringContentsSerializer seek_key(buf); - it->Seek(seek_key); - for (unsigned int x=seek_start; x<10; ++x) { - for (int y = 0; y < 10; y++) { - snprintf(buf, sizeof(buf), "%d", x); - std::string exp_key(buf); - for (int z = 0; z < y; z++) + it->Seek(StringContentsSerializer{ToString(seek_start)}); + for (unsigned int x = seek_start; x < 10; ++x) { + for (int y = 0; y < 10; ++y) { + std::string exp_key{ToString(x)}; + for (int z = 0; z < y; ++z) exp_key += exp_key; StringContentsSerializer key; uint32_t value; diff --git a/src/test/fuzz/netaddress.cpp b/src/test/fuzz/netaddress.cpp index 61279c32ec825..1b5122b1c9466 100644 --- a/src/test/fuzz/netaddress.cpp +++ b/src/test/fuzz/netaddress.cpp @@ -16,7 +16,6 @@ FUZZ_TARGET(netaddress) FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const CNetAddr net_addr = ConsumeNetAddr(fuzzed_data_provider); - (void)net_addr.GetHash(); (void)net_addr.GetNetClass(); if (net_addr.GetNetwork() == Network::NET_IPV4) { assert(net_addr.IsIPv4()); @@ -81,6 +80,8 @@ FUZZ_TARGET(netaddress) (void)service.GetKey(); (void)service.GetPort(); (void)service.ToStringAddrPort(); + (void)CServiceHash()(service); + (void)CServiceHash(0, 0)(service); const CNetAddr other_net_addr = ConsumeNetAddr(fuzzed_data_provider); (void)net_addr.GetReachabilityFrom(other_net_addr); diff --git a/src/test/fuzz/util.cpp b/src/test/fuzz/util.cpp index 92a2ad5070cda..4bc84f40cdb9c 100644 --- a/src/test/fuzz/util.cpp +++ b/src/test/fuzz/util.cpp @@ -14,7 +14,7 @@ #include FuzzedSock::FuzzedSock(FuzzedDataProvider& fuzzed_data_provider) - : m_fuzzed_data_provider{fuzzed_data_provider} + : m_fuzzed_data_provider{fuzzed_data_provider}, m_selectable{fuzzed_data_provider.ConsumeBool()} { m_socket = fuzzed_data_provider.ConsumeIntegralInRange(INVALID_SOCKET - 1, INVALID_SOCKET); } @@ -257,6 +257,24 @@ int FuzzedSock::GetSockName(sockaddr* name, socklen_t* name_len) const return 0; } +bool FuzzedSock::SetNonBlocking() const +{ + constexpr std::array setnonblocking_errnos{ + EBADF, + EPERM, + }; + if (m_fuzzed_data_provider.ConsumeBool()) { + SetFuzzedErrNo(m_fuzzed_data_provider, setnonblocking_errnos); + return false; + } + return true; +} + +bool FuzzedSock::IsSelectable() const +{ + return m_selectable; +} + bool FuzzedSock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred) const { constexpr std::array wait_errnos{ diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h index ebf741651b516..67296f00c24ee 100644 --- a/src/test/fuzz/util.h +++ b/src/test/fuzz/util.h @@ -53,6 +53,13 @@ class FuzzedSock : public Sock */ mutable std::optional m_peek_data; + /** + * Whether to pretend that the socket is select(2)-able. This is randomly set in the + * constructor. It should remain constant so that repeated calls to `IsSelectable()` + * return the same value. + */ + const bool m_selectable; + public: explicit FuzzedSock(FuzzedDataProvider& fuzzed_data_provider); @@ -80,6 +87,10 @@ class FuzzedSock : public Sock int GetSockName(sockaddr* name, socklen_t* name_len) const override; + bool SetNonBlocking() const override; + + bool IsSelectable() const override; + bool Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred = nullptr) const override; bool IsConnected(std::string& errmsg) const override; diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp index ffaac9144d435..ee96bd3f549f4 100644 --- a/src/test/netbase_tests.cpp +++ b/src/test/netbase_tests.cpp @@ -434,6 +434,26 @@ 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(9999)); + + // 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_CASE(netbase_parsenetwork) { BOOST_CHECK_EQUAL(ParseNetwork("ipv4"), NET_IPV4); diff --git a/src/test/util/net.h b/src/test/util/net.h index 7ca37616e5acd..64d3811107be6 100644 --- a/src/test/util/net.h +++ b/src/test/util/net.h @@ -201,6 +201,10 @@ class StaticContentsSock : public Sock return 0; } + bool SetNonBlocking() const override { return true; } + + bool IsSelectable() const override { return true; } + bool Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred = nullptr) const override diff --git a/src/util/sock.cpp b/src/util/sock.cpp index 25884ae8ea2c0..423a241673cba 100644 --- a/src/util/sock.cpp +++ b/src/util/sock.cpp @@ -126,6 +126,34 @@ int Sock::GetSockName(sockaddr* name, socklen_t* name_len) const return getsockname(m_socket, name, name_len); } +bool Sock::SetNonBlocking() const +{ +#ifdef WIN32 + u_long on{1}; + if (ioctlsocket(m_socket, FIONBIO, &on) == SOCKET_ERROR) { + return false; + } +#else + const int flags{fcntl(m_socket, F_GETFL, 0)}; + if (flags == SOCKET_ERROR) { + return false; + } + if (fcntl(m_socket, F_SETFL, flags | O_NONBLOCK) == SOCKET_ERROR) { + return false; + } +#endif + return true; +} + +bool Sock::IsSelectable() const +{ +#if defined(USE_POLL) || defined(WIN32) + return true; +#else + return m_socket < FD_SETSIZE; +#endif +} + bool Sock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred) const { #ifdef USE_POLL @@ -155,7 +183,7 @@ bool Sock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occur return true; #else - if (!IsSelectableSocket(m_socket)) { + if (!IsSelectable()) { return false; } diff --git a/src/util/sock.h b/src/util/sock.h index 5c7fc05fd53de..2edd79847b968 100644 --- a/src/util/sock.h +++ b/src/util/sock.h @@ -190,6 +190,18 @@ class Sock */ [[nodiscard]] virtual int GetSockName(sockaddr* name, socklen_t* name_len) const; + /** + * Set the non-blocking option on the socket. + * @return true if set successfully + */ + [[nodiscard]] virtual bool SetNonBlocking() const; + + /** + * Check if the underlying socket can be used for `select(2)` (or the `Wait()` method). + * @return true if selectable + */ + [[nodiscard]] virtual bool IsSelectable() const; + using Event = uint8_t; /** diff --git a/test/functional/p2p_blocksonly.py b/test/functional/p2p_blocksonly.py index a79eadc0b5631..3018a598190a3 100755 --- a/test/functional/p2p_blocksonly.py +++ b/test/functional/p2p_blocksonly.py @@ -57,6 +57,7 @@ def blocksonly_mode_tests(self): second_peer = self.nodes[0].add_p2p_connection(P2PInterface()) peer_1_info = self.nodes[0].getpeerinfo()[0] assert_equal(peer_1_info['permissions'], ['relay']) + assert_equal(first_peer.relay, 1) peer_2_info = self.nodes[0].getpeerinfo()[1] assert_equal(peer_2_info['permissions'], ['relay']) assert_equal(self.nodes[0].testmempoolaccept([tx_hex])[0]['allowed'], True) diff --git a/test/functional/test_framework/p2p.py b/test/functional/test_framework/p2p.py index 8f08eabc0f763..f3da60e113205 100755 --- a/test/functional/test_framework/p2p.py +++ b/test/functional/test_framework/p2p.py @@ -610,6 +610,7 @@ def on_version(self, message): self.send_message(msg_sendaddrv2()) self.send_message(msg_verack()) self.nServices = message.nServices + self.relay = message.relay if self.p2p_connected_to_node: self.send_message(msg_getaddr()) diff --git a/test/lint/lint-locale-dependence.py b/test/lint/lint-locale-dependence.py index 59d61fc047d2c..3e674c5b1daa1 100755 --- a/test/lint/lint-locale-dependence.py +++ b/test/lint/lint-locale-dependence.py @@ -37,7 +37,6 @@ # # TODO: Reduce KNOWN_VIOLATIONS by replacing uses of locale dependent stoul/strtol with locale # independent ToIntegral(...). -# TODO: Reduce KNOWN_VIOLATIONS by replacing uses of locale dependent snprintf with strprintf. import re import sys @@ -48,7 +47,6 @@ KNOWN_VIOLATIONS = [ "src/bitcoin-tx.cpp.*stoul", "src/dbwrapper.cpp:.*vsnprintf", - "src/test/dbwrapper_tests.cpp:.*snprintf", "src/test/fuzz/locale.cpp", "src/test/fuzz/string.cpp", "src/util/strencodings.cpp:.*strtoll",