Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

backport: bitcoin#19698, #20079, #20566, #20715, #20868, #21012, #21188, #21200, #21205, #21211, #21293 #6069

Merged
merged 11 commits into from
Jun 21, 2024
Merged
Prev Previous commit
Next Next commit
Merge bitcoin#20079: p2p: Treat handshake misbehavior like unknown me…
…ssage

faaad1b p2p: Ignore version msgs after initial version msg (MarcoFalke)
fad68af p2p: Ignore non-version msgs before version msg (MarcoFalke)

Pull request description:

  Handshake misbehaviour doesn't cost us more than any other unknown message, so it seems odd to treat it differently

ACKs for top commit:
  jnewbery:
    utACK faaad1b
  practicalswift:
    ACK faaad1b: patch looks correct

Tree-SHA512: 9f30c3b5c1f6604fd02cff878f10999956152419a3dd9825f8267cbdeff7d06787418b41c7fde8a00a5e557fe89204546e05d5689042dbf7b07fbb7eb95cddff
  • Loading branch information
MarcoFalke authored and knst committed Jun 19, 2024
commit 785f7310ed4f7d5ce4c1963284b7f0566b7a8a32
8 changes: 3 additions & 5 deletions src/net_processing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3274,10 +3274,8 @@ void PeerManagerImpl::ProcessMessage(
if (peer == nullptr) return;

if (msg_type == NetMsgType::VERSION) {
// Each connection can only send one version message
if (pfrom.nVersion != 0)
{
Misbehaving(pfrom.GetId(), 1, "redundant version message");
if (pfrom.nVersion != 0) {
LogPrint(BCLog::NET, "redundant version message from peer=%d\n", pfrom.GetId());
return;
}

Expand Down Expand Up @@ -3507,7 +3505,7 @@ void PeerManagerImpl::ProcessMessage(

if (pfrom.nVersion == 0) {
// Must have a version message before anything else
Misbehaving(pfrom.GetId(), 1, "non-version message before version handshake");
LogPrint(BCLog::NET, "non-version message before version handshake. Message \"%s\" from peer=%d\n", SanitizeString(msg_type), pfrom.GetId());
return;
}

Expand Down
9 changes: 9 additions & 0 deletions test/functional/p2p_invalid_messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
msg_headers,
msg_inv,
MSG_TX,
msg_version,
)
from test_framework.p2p import (
P2PDataStore, P2PInterface
Expand Down Expand Up @@ -50,6 +51,7 @@ def set_test_params(self):

def run_test(self):
self.test_buffer()
self.test_duplicate_version_msg()
self.test_magic_bytes()
self.test_checksum()
self.test_size()
Expand Down Expand Up @@ -78,6 +80,13 @@ def test_buffer(self):
conn.sync_with_ping(timeout=1)
self.nodes[0].disconnect_p2ps()

def test_duplicate_version_msg(self):
self.log.info("Test duplicate version message is ignored")
conn = self.nodes[0].add_p2p_connection(P2PDataStore())
with self.nodes[0].assert_debug_log(['redundant version message from peer']):
conn.send_and_ping(msg_version())
self.nodes[0].disconnect_p2ps()

def test_magic_bytes(self):
self.log.info("Test message with invalid magic bytes disconnects peer")
conn = self.nodes[0].add_p2p_connection(P2PDataStore())
Expand Down
19 changes: 1 addition & 18 deletions test/functional/p2p_leak.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@
assert_greater_than_or_equal,
)

DISCOURAGEMENT_THRESHOLD = 100


class LazyPeer(P2PInterface):
def __init__(self):
Expand Down Expand Up @@ -97,27 +95,16 @@ def setup_network(self):
self.setup_nodes()

def run_test(self):
# Peer that never sends a version. We will send a bunch of messages
# from this peer anyway and verify eventual disconnection.
no_version_disconnect_peer = self.nodes[0].add_p2p_connection(
LazyPeer(), send_version=False, wait_for_verack=False)

# Another peer that never sends a version, nor any other messages. It shouldn't receive anything from the node.
no_version_idle_peer = self.nodes[0].add_p2p_connection(LazyPeer(), send_version=False, wait_for_verack=False)

# Peer that sends a version but not a verack.
no_verack_idle_peer = self.nodes[0].add_p2p_connection(NoVerackIdlePeer(), wait_for_verack=False)

# Send enough ping messages (any non-version message will do) prior to sending
# version to reach the peer discouragement threshold. This should get us disconnected.
for _ in range(DISCOURAGEMENT_THRESHOLD):
no_version_disconnect_peer.send_message(msg_ping())

# Wait until we got the verack in response to the version. Though, don't wait for the node to receive the
# verack, since we never sent one
no_verack_idle_peer.wait_for_verack()

no_version_disconnect_peer.wait_until(lambda: no_version_disconnect_peer.ever_connected, check_connected=False)
no_version_idle_peer.wait_until(lambda: no_version_idle_peer.ever_connected)
no_verack_idle_peer.wait_until(lambda: no_verack_idle_peer.version_received)

Expand All @@ -127,13 +114,9 @@ def run_test(self):
#Give the node enough time to possibly leak out a message
time.sleep(5)

#This peer should have been banned
assert not no_version_disconnect_peer.is_connected

self.nodes[0].disconnect_p2ps()

# Make sure no unexpected messages came in
assert no_version_disconnect_peer.unexpected_msg == False
assert no_version_idle_peer.unexpected_msg == False
assert no_verack_idle_peer.unexpected_msg == False

Expand All @@ -152,7 +135,7 @@ def run_test(self):
p2p_old_peer = self.nodes[0].add_p2p_connection(P2PInterface(), send_version=False, wait_for_verack=False)
old_version_msg = msg_version()
old_version_msg.nVersion = 31799
with self.nodes[0].assert_debug_log(['peer=4 using obsolete version 31799; disconnecting']):
with self.nodes[0].assert_debug_log(['peer=3 using obsolete version 31799; disconnecting']):
p2p_old_peer.send_message(old_version_msg)
p2p_old_peer.wait_for_disconnect()

Expand Down
6 changes: 4 additions & 2 deletions test/functional/p2p_timeouts.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,10 @@ def run_test(self):
assert no_version_node.is_connected
assert no_send_node.is_connected

no_verack_node.send_message(msg_ping())
no_version_node.send_message(msg_ping())
with self.nodes[0].assert_debug_log(['Unsupported message "ping" prior to verack from peer=0']):
no_verack_node.send_message(msg_ping())
with self.nodes[0].assert_debug_log(['non-version message before version handshake. Message "ping" from peer=1']):
no_version_node.send_message(msg_ping())

self.mock_forward(1)

Expand Down