net_peer_connection_tests.cpp
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 <node/protocol_version.h> 14 #include <protocol.h> 15 #include <random.h> 16 #include <test/util/logging.h> 17 #include <test/util/net.h> 18 #include <test/util/random.h> 19 #include <test/util/setup_common.h> 20 #include <tinyformat.h> 21 #include <util/chaintype.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 struct PeerTest : LogIPsTestingSetup { 47 /** Create a peer and connect to it. If the optional `address` (IP/CJDNS only) isn't passed, a random address is created. */ 48 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) 49 { 50 CAddress addr{}; 51 52 if (address.has_value()) { 53 addr = CAddress{MaybeFlipIPv6toCJDNS(LookupNumeric(address.value(), Params().GetDefaultPort())), NODE_NONE}; 54 } else if (onion_peer) { 55 auto tor_addr{m_rng.randbytes(ADDR_TORV3_SIZE)}; 56 BOOST_REQUIRE(addr.SetSpecial(OnionToString(tor_addr))); 57 } 58 59 while (!addr.IsLocal() && !addr.IsRoutable()) { 60 addr = CAddress{ip(m_rng.randbits(32)), NODE_NONE}; 61 } 62 63 BOOST_REQUIRE(addr.IsValid()); 64 65 const bool inbound_onion{onion_peer && conn_type == ConnectionType::INBOUND}; 66 67 nodes.emplace_back(new CNode{++id, 68 /*sock=*/nullptr, 69 addr, 70 /*nKeyedNetGroupIn=*/0, 71 /*nLocalHostNonceIn=*/0, 72 CAddress{}, 73 /*addrNameIn=*/"", 74 conn_type, 75 /*inbound_onion=*/inbound_onion, 76 /*network_key=*/0}); 77 CNode& node = *nodes.back(); 78 node.SetCommonVersion(PROTOCOL_VERSION); 79 80 peerman.InitializeNode(node, ServiceFlags(NODE_NETWORK | NODE_WITNESS)); 81 node.fSuccessfullyConnected = true; 82 83 connman.AddTestNode(node); 84 } 85 }; // struct PeerTest 86 87 BOOST_FIXTURE_TEST_CASE(test_addnode_getaddednodeinfo_and_connection_detection, PeerTest) 88 { 89 auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman, Params()); 90 auto peerman = PeerManager::make(*connman, *m_node.addrman, nullptr, *m_node.chainman, *m_node.mempool, *m_node.warnings, {}); 91 NodeId id{0}; 92 std::vector<CNode*> nodes; 93 94 // Connect a localhost peer. 95 { 96 ASSERT_DEBUG_LOG("Added connection to 127.0.0.1:8333 peer=1"); 97 AddPeer(id, nodes, *peerman, *connman, ConnectionType::MANUAL, /*onion_peer=*/false, /*address=*/"127.0.0.1"); 98 BOOST_REQUIRE(nodes.back() != nullptr); 99 } 100 101 // Call ConnectNode(), which is also called by RPC addnode onetry, for a localhost 102 // address that resolves to multiple IPs, including that of the connected peer. 103 // The connection attempt should consistently fail due to the check in ConnectNode(). 104 for (int i = 0; i < 10; ++i) { 105 ASSERT_DEBUG_LOG("Not opening a connection to localhost, already connected to 127.0.0.1:8333"); 106 BOOST_CHECK(!connman->ConnectNodePublic(*peerman, "localhost", ConnectionType::MANUAL)); 107 } 108 109 // Add 3 more peer connections. 110 AddPeer(id, nodes, *peerman, *connman, ConnectionType::OUTBOUND_FULL_RELAY); 111 AddPeer(id, nodes, *peerman, *connman, ConnectionType::BLOCK_RELAY, /*onion_peer=*/true); 112 AddPeer(id, nodes, *peerman, *connman, ConnectionType::INBOUND); 113 114 // Add a CJDNS peer connection. 115 AddPeer(id, nodes, *peerman, *connman, ConnectionType::INBOUND, /*onion_peer=*/false, 116 /*address=*/"[fc00:3344:5566:7788:9900:aabb:ccdd:eeff]:1234"); 117 BOOST_CHECK(nodes.back()->IsInboundConn()); 118 BOOST_CHECK_EQUAL(nodes.back()->ConnectedThroughNetwork(), Network::NET_CJDNS); 119 120 BOOST_TEST_MESSAGE("Call AddNode() for all the peers"); 121 for (auto node : connman->TestNodes()) { 122 BOOST_CHECK(connman->AddNode({/*m_added_node=*/node->addr.ToStringAddrPort(), /*m_use_v2transport=*/true})); 123 BOOST_TEST_MESSAGE(strprintf("peer id=%s addr=%s", node->GetId(), node->addr.ToStringAddrPort())); 124 } 125 126 BOOST_TEST_MESSAGE("\nCall AddNode() with 2 addrs resolving to existing localhost addnode entry; neither should be added"); 127 BOOST_CHECK(!connman->AddNode({/*m_added_node=*/"127.0.0.1", /*m_use_v2transport=*/true})); 128 // OpenBSD doesn't support the IPv4 shorthand notation with omitted zero-bytes. 129 #if !defined(__OpenBSD__) 130 BOOST_CHECK(!connman->AddNode({/*m_added_node=*/"127.1", /*m_use_v2transport=*/true})); 131 #endif 132 133 BOOST_TEST_MESSAGE("\nExpect GetAddedNodeInfo to return expected number of peers with `include_connected` true/false"); 134 BOOST_CHECK_EQUAL(connman->GetAddedNodeInfo(/*include_connected=*/true).size(), nodes.size()); 135 BOOST_CHECK(connman->GetAddedNodeInfo(/*include_connected=*/false).empty()); 136 137 // Test AddedNodesContain() 138 for (auto node : connman->TestNodes()) { 139 BOOST_CHECK(connman->AddedNodesContain(node->addr)); 140 } 141 AddPeer(id, nodes, *peerman, *connman, ConnectionType::OUTBOUND_FULL_RELAY); 142 BOOST_CHECK(!connman->AddedNodesContain(nodes.back()->addr)); 143 144 BOOST_TEST_MESSAGE("\nPrint GetAddedNodeInfo contents:"); 145 for (const auto& info : connman->GetAddedNodeInfo(/*include_connected=*/true)) { 146 BOOST_TEST_MESSAGE(strprintf("\nadded node: %s", info.m_params.m_added_node)); 147 BOOST_TEST_MESSAGE(strprintf("connected: %s", info.fConnected)); 148 if (info.fConnected) { 149 BOOST_TEST_MESSAGE(strprintf("IP address: %s", info.resolvedAddress.ToStringAddrPort())); 150 BOOST_TEST_MESSAGE(strprintf("direction: %s", info.fInbound ? "inbound" : "outbound")); 151 } 152 } 153 154 BOOST_TEST_MESSAGE("\nCheck that all connected peers are correctly detected as connected"); 155 for (const auto& node : connman->TestNodes()) { 156 BOOST_CHECK(connman->AlreadyConnectedToAddressPublic(node->addr)); 157 } 158 159 // Clean up 160 for (auto node : connman->TestNodes()) { 161 peerman->FinalizeNode(*node); 162 } 163 connman->ClearTestNodes(); 164 } 165 166 BOOST_AUTO_TEST_SUITE_END()