aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPieter Wuille <pieter@wuille.net>2023-09-05 23:38:15 -0400
committerPieter Wuille <pieter@wuille.net>2023-09-07 09:04:55 -0400
commitdb9888feec48c6220a2fcf92865503bbbdab02a4 (patch)
tree8caab5c93e39ff31a57861941ec9c7411f3a1379
parent91e1ef8684997fb4b3e8b64ef3935a936445066b (diff)
net: detect wrong-network V1 talking to V2Transport
-rw-r--r--src/net.cpp22
-rw-r--r--src/net.h2
-rw-r--r--src/test/net_tests.cpp25
3 files changed, 46 insertions, 3 deletions
diff --git a/src/net.cpp b/src/net.cpp
index bd3ba3f6f8..3955005dfa 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -1109,12 +1109,29 @@ void V2Transport::ProcessReceivedMaybeV1Bytes() noexcept
}
}
-void V2Transport::ProcessReceivedKeyBytes() noexcept
+bool V2Transport::ProcessReceivedKeyBytes() noexcept
{
AssertLockHeld(m_recv_mutex);
AssertLockNotHeld(m_send_mutex);
Assume(m_recv_state == RecvState::KEY);
Assume(m_recv_buffer.size() <= EllSwiftPubKey::size());
+
+ // As a special exception, if bytes 4-16 of the key on a responder connection match the
+ // corresponding bytes of a V1 version message, but bytes 0-4 don't match the network magic
+ // (if they did, we'd have switched to V1 state already), assume this is a peer from
+ // another network, and disconnect them. They will almost certainly disconnect us too when
+ // they receive our uniformly random key and garbage, but detecting this case specially
+ // means we can log it.
+ static constexpr std::array<uint8_t, 12> MATCH = {'v', 'e', 'r', 's', 'i', 'o', 'n', 0, 0, 0, 0, 0};
+ static constexpr size_t OFFSET = sizeof(CMessageHeader::MessageStartChars);
+ if (!m_initiating && m_recv_buffer.size() >= OFFSET + MATCH.size()) {
+ if (std::equal(MATCH.begin(), MATCH.end(), m_recv_buffer.begin() + OFFSET)) {
+ LogPrint(BCLog::NET, "V2 transport error: V1 peer with wrong MessageStart %s\n",
+ HexStr(Span(m_recv_buffer).first(OFFSET)));
+ return false;
+ }
+ }
+
if (m_recv_buffer.size() == EllSwiftPubKey::size()) {
// Other side's key has been fully received, and can now be Diffie-Hellman combined with
// our key to initialize the encryption ciphers.
@@ -1157,6 +1174,7 @@ void V2Transport::ProcessReceivedKeyBytes() noexcept
} else {
// We still have to receive more key bytes.
}
+ return true;
}
bool V2Transport::ProcessReceivedGarbageBytes() noexcept
@@ -1378,7 +1396,7 @@ bool V2Transport::ReceivedBytes(Span<const uint8_t>& msg_bytes) noexcept
break;
case RecvState::KEY:
- ProcessReceivedKeyBytes();
+ if (!ProcessReceivedKeyBytes()) return false;
break;
case RecvState::GARB_GARBTERM:
diff --git a/src/net.h b/src/net.h
index 90f5c4b388..cf7a240202 100644
--- a/src/net.h
+++ b/src/net.h
@@ -617,7 +617,7 @@ private:
/** Process bytes in m_recv_buffer, while in KEY_MAYBE_V1 state. */
void ProcessReceivedMaybeV1Bytes() noexcept EXCLUSIVE_LOCKS_REQUIRED(m_recv_mutex, !m_send_mutex);
/** Process bytes in m_recv_buffer, while in KEY state. */
- void ProcessReceivedKeyBytes() noexcept EXCLUSIVE_LOCKS_REQUIRED(m_recv_mutex, !m_send_mutex);
+ bool ProcessReceivedKeyBytes() noexcept EXCLUSIVE_LOCKS_REQUIRED(m_recv_mutex, !m_send_mutex);
/** Process bytes in m_recv_buffer, while in GARB_GARBTERM state. */
bool ProcessReceivedGarbageBytes() noexcept EXCLUSIVE_LOCKS_REQUIRED(m_recv_mutex);
/** Process bytes in m_recv_buffer, while in GARBAUTH/VERSION/APP state. */
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index feaa0aef61..900e311d22 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -1104,6 +1104,15 @@ public:
m_to_send.insert(m_to_send.end(), data.begin(), data.end());
}
+ /** Send V1 version message header to the transport. */
+ void SendV1Version(const CMessageHeader::MessageStartChars& magic)
+ {
+ CMessageHeader hdr(magic, "version", 126 + InsecureRandRange(11));
+ CDataStream ser(SER_NETWORK, CLIENT_VERSION);
+ ser << hdr;
+ m_to_send.insert(m_to_send.end(), UCharCast(ser.data()), UCharCast(ser.data() + ser.size()));
+ }
+
/** Schedule bytes to be sent to the transport. */
void Send(Span<const std::byte> data) { Send(MakeUCharSpan(data)); }
@@ -1505,6 +1514,22 @@ BOOST_AUTO_TEST_CASE(v2transport_test)
BOOST_CHECK((*ret)[1] && (*ret)[1]->m_type == "block" && Span{(*ret)[1]->m_recv} == MakeByteSpan(msg_data_1));
tester.ReceiveMessage(uint8_t(3), msg_data_2); // "blocktxn" short id
}
+
+ // Send correct network's V1 header
+ {
+ V2TransportTester tester(false);
+ tester.SendV1Version(Params().MessageStart());
+ auto ret = tester.Interact();
+ BOOST_CHECK(ret);
+ }
+
+ // Send wrong network's V1 header
+ {
+ V2TransportTester tester(false);
+ tester.SendV1Version(CChainParams::Main()->MessageStart());
+ auto ret = tester.Interact();
+ BOOST_CHECK(!ret);
+ }
}
BOOST_AUTO_TEST_SUITE_END()