|
| 1 | +// Copyright (c) 2023-present The Bitcoin Core developers |
| 2 | +// Distributed under the MIT software license, see the accompanying |
| 3 | +// file COPYING or http://www.opensource.org/licenses/mit-license.php. |
| 4 | + |
| 5 | +#include <chainparams.h> |
| 6 | +#include <compat/compat.h> |
| 7 | +#include <net.h> |
| 8 | +#include <net_processing.h> |
| 9 | +#include <netaddress.h> |
| 10 | +#include <netbase.h> |
| 11 | +#include <netgroup.h> |
| 12 | +#include <node/connection_types.h> |
| 13 | +#include <protocol.h> |
| 14 | +#include <random.h> |
| 15 | +#include <test/util/logging.h> |
| 16 | +#include <test/util/net.h> |
| 17 | +#include <test/util/random.h> |
| 18 | +#include <test/util/setup_common.h> |
| 19 | +#include <tinyformat.h> |
| 20 | +#include <util/chaintype.h> |
| 21 | +#include <version.h> |
| 22 | + |
| 23 | +#include <algorithm> |
| 24 | +#include <cstdint> |
| 25 | +#include <memory> |
| 26 | +#include <optional> |
| 27 | +#include <string> |
| 28 | +#include <vector> |
| 29 | + |
| 30 | +#include <boost/test/unit_test.hpp> |
| 31 | + |
| 32 | +struct LogIPsTestingSetup : public TestingSetup { |
| 33 | + LogIPsTestingSetup() |
| 34 | + : TestingSetup{ChainType::MAIN, /*extra_args=*/{"-logips"}} {} |
| 35 | +}; |
| 36 | + |
| 37 | +BOOST_FIXTURE_TEST_SUITE(net_peer_connection_tests, LogIPsTestingSetup) |
| 38 | + |
| 39 | +static CService ip(uint32_t i) |
| 40 | +{ |
| 41 | + struct in_addr s; |
| 42 | + s.s_addr = i; |
| 43 | + return CService{CNetAddr{s}, Params().GetDefaultPort()}; |
| 44 | +} |
| 45 | + |
| 46 | +/** Create a peer and connect to it. If the optional `address` (IP/CJDNS only) isn't passed, a random address is created. */ |
| 47 | +static void AddPeer(NodeId& id, std::vector<CNode*>& nodes, PeerManager& peerman, ConnmanTestMsg& connman, ConnectionType conn_type, bool onion_peer = false, std::optional<std::string> address = std::nullopt) |
| 48 | +{ |
| 49 | + CAddress addr{}; |
| 50 | + |
| 51 | + if (address.has_value()) { |
| 52 | + addr = CAddress{MaybeFlipIPv6toCJDNS(LookupNumeric(address.value(), Params().GetDefaultPort())), NODE_NONE}; |
| 53 | + } else if (onion_peer) { |
| 54 | + auto tor_addr{g_insecure_rand_ctx.randbytes(ADDR_TORV3_SIZE)}; |
| 55 | + BOOST_REQUIRE(addr.SetSpecial(OnionToString(tor_addr))); |
| 56 | + } |
| 57 | + |
| 58 | + while (!addr.IsLocal() && !addr.IsRoutable()) { |
| 59 | + addr = CAddress{ip(g_insecure_rand_ctx.randbits(32)), NODE_NONE}; |
| 60 | + } |
| 61 | + |
| 62 | + BOOST_REQUIRE(addr.IsValid()); |
| 63 | + |
| 64 | + const bool inbound_onion{onion_peer && conn_type == ConnectionType::INBOUND}; |
| 65 | + |
| 66 | + nodes.emplace_back(new CNode{++id, |
| 67 | + /*sock=*/nullptr, |
| 68 | + addr, |
| 69 | + /*nKeyedNetGroupIn=*/0, |
| 70 | + /*nLocalHostNonceIn=*/0, |
| 71 | + CAddress{}, |
| 72 | + /*addrNameIn=*/"", |
| 73 | + conn_type, |
| 74 | + /*inbound_onion=*/inbound_onion}); |
| 75 | + CNode& node = *nodes.back(); |
| 76 | + node.SetCommonVersion(PROTOCOL_VERSION); |
| 77 | + |
| 78 | + peerman.InitializeNode(node, ServiceFlags(NODE_NETWORK | NODE_WITNESS)); |
| 79 | + node.fSuccessfullyConnected = true; |
| 80 | + |
| 81 | + connman.AddTestNode(node); |
| 82 | +} |
| 83 | + |
| 84 | +BOOST_AUTO_TEST_CASE(test_addnode_getaddednodeinfo_and_connection_detection) |
| 85 | +{ |
| 86 | + auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman, Params()); |
| 87 | + auto peerman = PeerManager::make(*connman, *m_node.addrman, nullptr, *m_node.chainman, *m_node.mempool, {}); |
| 88 | + NodeId id{0}; |
| 89 | + std::vector<CNode*> nodes; |
| 90 | + |
| 91 | + // Connect a localhost peer. |
| 92 | + { |
| 93 | + ASSERT_DEBUG_LOG("Added connection to 127.0.0.1:8333 peer=1"); |
| 94 | + AddPeer(id, nodes, *peerman, *connman, ConnectionType::MANUAL, /*onion_peer=*/false, /*address=*/"127.0.0.1"); |
| 95 | + BOOST_REQUIRE(nodes.back() != nullptr); |
| 96 | + } |
| 97 | + |
| 98 | + // Call ConnectNode(), which is also called by RPC addnode onetry, for a localhost |
| 99 | + // address that resolves to multiple IPs, including that of the connected peer. |
| 100 | + // The connection attempt should consistently fail due to the check in ConnectNode(). |
| 101 | + for (int i = 0; i < 10; ++i) { |
| 102 | + ASSERT_DEBUG_LOG("Not opening a connection to localhost, already connected to 127.0.0.1:8333"); |
| 103 | + BOOST_CHECK(!connman->ConnectNodePublic(*peerman, "localhost", ConnectionType::MANUAL)); |
| 104 | + } |
| 105 | + |
| 106 | + // Add 3 more peer connections. |
| 107 | + AddPeer(id, nodes, *peerman, *connman, ConnectionType::OUTBOUND_FULL_RELAY); |
| 108 | + AddPeer(id, nodes, *peerman, *connman, ConnectionType::BLOCK_RELAY, /*onion_peer=*/true); |
| 109 | + AddPeer(id, nodes, *peerman, *connman, ConnectionType::INBOUND); |
| 110 | + |
| 111 | + BOOST_TEST_MESSAGE("Call AddNode() for all the peers"); |
| 112 | + for (auto node : connman->TestNodes()) { |
| 113 | + BOOST_CHECK(connman->AddNode({/*m_added_node=*/node->addr.ToStringAddrPort(), /*m_use_v2transport=*/true})); |
| 114 | + BOOST_TEST_MESSAGE(strprintf("peer id=%s addr=%s", node->GetId(), node->addr.ToStringAddrPort())); |
| 115 | + } |
| 116 | + |
| 117 | + BOOST_TEST_MESSAGE("\nCall AddNode() with 2 addrs resolving to existing localhost addnode entry; neither should be added"); |
| 118 | + BOOST_CHECK(!connman->AddNode({/*m_added_node=*/"127.0.0.1", /*m_use_v2transport=*/true})); |
| 119 | + BOOST_CHECK(!connman->AddNode({/*m_added_node=*/"127.1", /*m_use_v2transport=*/true})); |
| 120 | + |
| 121 | + BOOST_TEST_MESSAGE("\nExpect GetAddedNodeInfo to return expected number of peers with `include_connected` true/false"); |
| 122 | + BOOST_CHECK_EQUAL(connman->GetAddedNodeInfo(/*include_connected=*/true).size(), nodes.size()); |
| 123 | + BOOST_CHECK(connman->GetAddedNodeInfo(/*include_connected=*/false).empty()); |
| 124 | + |
| 125 | + BOOST_TEST_MESSAGE("\nPrint GetAddedNodeInfo contents:"); |
| 126 | + for (const auto& info : connman->GetAddedNodeInfo(/*include_connected=*/true)) { |
| 127 | + BOOST_TEST_MESSAGE(strprintf("\nadded node: %s", info.m_params.m_added_node)); |
| 128 | + BOOST_TEST_MESSAGE(strprintf("connected: %s", info.fConnected)); |
| 129 | + if (info.fConnected) { |
| 130 | + BOOST_TEST_MESSAGE(strprintf("IP address: %s", info.resolvedAddress.ToStringAddrPort())); |
| 131 | + BOOST_TEST_MESSAGE(strprintf("direction: %s", info.fInbound ? "inbound" : "outbound")); |
| 132 | + } |
| 133 | + } |
| 134 | + |
| 135 | + BOOST_TEST_MESSAGE("\nCheck that all connected peers are correctly detected as connected"); |
| 136 | + for (auto node : connman->TestNodes()) { |
| 137 | + BOOST_CHECK(connman->AlreadyConnectedPublic(node->addr)); |
| 138 | + } |
| 139 | + |
| 140 | + // Clean up |
| 141 | + for (auto node : connman->TestNodes()) { |
| 142 | + peerman->FinalizeNode(*node); |
| 143 | + } |
| 144 | + connman->ClearTestNodes(); |
| 145 | +} |
| 146 | + |
| 147 | +BOOST_AUTO_TEST_SUITE_END() |
0 commit comments