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 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& 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 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()