1 #!/usr/bin/env python3
2 # Copyright (c) 2017-2022 The Limenka developers
3 # Distributed under the MIT software license, see the accompanying
4 # file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 """Test message sending before handshake completion.
6 7 Before receiving a VERACK, a node should not send anything but VERSION/VERACK
8 and feature negotiation messages (WTXIDRELAY, SENDADDRV2).
9 10 This test connects to a node and sends it a few messages, trying to entice it
11 into sending us something it shouldn't."""
12 13 import time
14 15 from test_framework.messages import (
16 msg_getaddr,
17 msg_ping,
18 msg_version,
19 )
20 from test_framework.p2p import (
21 P2PInterface,
22 P2P_SUBVERSION,
23 P2P_SERVICES,
24 P2P_VERSION_RELAY,
25 )
26 from test_framework.test_framework import LimenkaTestFramework
27 from test_framework.util import (
28 assert_equal,
29 assert_greater_than_or_equal,
30 )
31 32 PEER_TIMEOUT = 3
33 34 35 class LazyPeer(P2PInterface):
36 def __init__(self):
37 super().__init__()
38 self.unexpected_msg = False
39 self.ever_connected = False
40 self.got_wtxidrelay = False
41 self.got_sendaddrv2 = False
42 43 def bad_message(self, message):
44 self.unexpected_msg = True
45 print("should not have received message: %s" % message.msgtype)
46 47 def on_open(self):
48 self.ever_connected = True
49 50 # Does not respond to "version" with "verack"
51 def on_version(self, message): self.bad_message(message)
52 def on_verack(self, message): self.bad_message(message)
53 def on_inv(self, message): self.bad_message(message)
54 def on_addr(self, message): self.bad_message(message)
55 def on_getdata(self, message): self.bad_message(message)
56 def on_getblocks(self, message): self.bad_message(message)
57 def on_tx(self, message): self.bad_message(message)
58 def on_block(self, message): self.bad_message(message)
59 def on_getaddr(self, message): self.bad_message(message)
60 def on_headers(self, message): self.bad_message(message)
61 def on_getheaders(self, message): self.bad_message(message)
62 def on_ping(self, message): self.bad_message(message)
63 def on_mempool(self, message): self.bad_message(message)
64 def on_pong(self, message): self.bad_message(message)
65 def on_feefilter(self, message): self.bad_message(message)
66 def on_sendheaders(self, message): self.bad_message(message)
67 def on_sendcmpct(self, message): self.bad_message(message)
68 def on_cmpctblock(self, message): self.bad_message(message)
69 def on_getblocktxn(self, message): self.bad_message(message)
70 def on_blocktxn(self, message): self.bad_message(message)
71 def on_wtxidrelay(self, message): self.got_wtxidrelay = True
72 def on_sendaddrv2(self, message): self.got_sendaddrv2 = True
73 74 75 # Peer that sends a version but not a verack.
76 class NoVerackIdlePeer(LazyPeer):
77 def __init__(self):
78 self.version_received = False
79 super().__init__()
80 81 def on_verack(self, message): pass
82 # When version is received, don't reply with a verack. Instead, see if the
83 # node will give us a message that it shouldn't. This is not an exhaustive
84 # list!
85 def on_version(self, message):
86 self.version_received = True
87 self.send_message(msg_ping())
88 self.send_message(msg_getaddr())
89 90 91 class P2PVersionStore(P2PInterface):
92 version_received = None
93 94 def on_version(self, msg):
95 # Responds with an appropriate verack
96 super().on_version(msg)
97 self.version_received = msg
98 99 100 class P2PLeakTest(LimenkaTestFramework):
101 def set_test_params(self):
102 self.num_nodes = 1
103 self.extra_args = [[f"-peertimeout={PEER_TIMEOUT}"]]
104 105 def create_old_version(self, nversion):
106 old_version_msg = msg_version()
107 old_version_msg.nVersion = nversion
108 old_version_msg.strSubVer = P2P_SUBVERSION
109 old_version_msg.nServices = P2P_SERVICES
110 old_version_msg.relay = P2P_VERSION_RELAY
111 return old_version_msg
112 113 def run_test(self):
114 self.log.info('Check that the node doesn\'t send unexpected messages before handshake completion')
115 # Peer that never sends a version, nor any other messages. It shouldn't receive anything from the node.
116 no_version_idle_peer = self.nodes[0].add_p2p_connection(LazyPeer(), send_version=False, wait_for_verack=False)
117 118 # Peer that sends a version but not a verack.
119 no_verack_idle_peer = self.nodes[0].add_p2p_connection(NoVerackIdlePeer(), wait_for_verack=False)
120 121 # Pre-wtxidRelay peer that sends a version but not a verack and does not support feature negotiation
122 # messages which start at nVersion == 70016
123 pre_wtxidrelay_peer = self.nodes[0].add_p2p_connection(NoVerackIdlePeer(), send_version=False, wait_for_verack=False)
124 pre_wtxidrelay_peer.send_message(self.create_old_version(70015))
125 126 # Wait until the peer gets the verack in response to the version. Though, don't wait for the node to receive the
127 # verack, since the peer never sent one
128 no_verack_idle_peer.wait_for_verack()
129 pre_wtxidrelay_peer.wait_for_verack()
130 131 no_version_idle_peer.wait_until(lambda: no_version_idle_peer.ever_connected)
132 no_verack_idle_peer.wait_until(lambda: no_verack_idle_peer.version_received)
133 pre_wtxidrelay_peer.wait_until(lambda: pre_wtxidrelay_peer.version_received)
134 135 # Mine a block and make sure that it's not sent to the connected peers
136 self.generate(self.nodes[0], nblocks=1)
137 138 # Give the node enough time to possibly leak out a message
139 time.sleep(PEER_TIMEOUT + 2)
140 141 self.log.info("Connect peer to ensure the net thread runs the disconnect logic at least once")
142 self.nodes[0].add_p2p_connection(P2PInterface())
143 144 # Make sure only expected messages came in
145 assert not no_version_idle_peer.unexpected_msg
146 assert not no_version_idle_peer.got_wtxidrelay
147 assert not no_version_idle_peer.got_sendaddrv2
148 149 assert not no_verack_idle_peer.unexpected_msg
150 assert no_verack_idle_peer.got_wtxidrelay
151 assert no_verack_idle_peer.got_sendaddrv2
152 153 assert not pre_wtxidrelay_peer.unexpected_msg
154 assert not pre_wtxidrelay_peer.got_wtxidrelay
155 assert not pre_wtxidrelay_peer.got_sendaddrv2
156 157 # Expect peers to be disconnected due to timeout
158 assert not no_version_idle_peer.is_connected
159 assert not no_verack_idle_peer.is_connected
160 assert not pre_wtxidrelay_peer.is_connected
161 162 self.log.info('Check that the version message does not leak the local address of the node')
163 p2p_version_store = self.nodes[0].add_p2p_connection(P2PVersionStore())
164 ver = p2p_version_store.version_received
165 # Check that received time is within one hour of now
166 assert_greater_than_or_equal(ver.nTime, time.time() - 3600)
167 assert_greater_than_or_equal(time.time() + 3600, ver.nTime)
168 assert_equal(ver.addrFrom.port, 0)
169 assert_equal(ver.addrFrom.ip, '0.0.0.0')
170 assert_equal(ver.nStartingHeight, 201)
171 assert_equal(ver.relay, 1)
172 173 self.log.info('Check that old peers are disconnected')
174 p2p_old_peer = self.nodes[0].add_p2p_connection(P2PInterface(), send_version=False, wait_for_verack=False)
175 with self.nodes[0].assert_debug_log(["using obsolete version 31799, disconnecting peer=5"]):
176 p2p_old_peer.send_message(self.create_old_version(31799))
177 p2p_old_peer.wait_for_disconnect()
178 179 180 if __name__ == '__main__':
181 P2PLeakTest(__file__).main()
182