net_peer_connection_tests.cpp raw
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()
167