aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcirakg <cirakg@cirakg.xyz>2026-11-03 01:39:54 +0100
committercirakg <cirakg@cirakg.xyz>2026-11-03 01:39:54 +0100
commit51341ec09b25de1d9b01d0c00ee828dd5b861d5c (patch)
tree2be34cc3ca2158087320ce2e31e48484903ad3fb
parent4304d2704a57c22d5b3f8576b5acebc57a6a7eef (diff)
tests: cover room packet matching and size-checked ZSTD decompression
Assisted-by: Claude Opus 5.5 (investigation and implementation)
-rw-r--r--src/tests/CMakeLists.txt1
-rw-r--r--src/tests/common/zstd_compression.cpp59
-rw-r--r--src/tests/core/internal_network/socket_proxy.cpp37
3 files changed, 94 insertions, 3 deletions
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index a31c073765..6bf14f4cbd 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -18,6 +18,7 @@ add_executable(tests
common/slot_vector.cpp
common/undefined_fix.cpp
common/unique_function.cpp
+ common/zstd_compression.cpp
core/core_timing.cpp
core/ldn.cpp
core/internal_network/socket_proxy.cpp
diff --git a/src/tests/common/zstd_compression.cpp b/src/tests/common/zstd_compression.cpp
new file mode 100644
index 0000000000..6996f2e1ac
--- /dev/null
+++ b/src/tests/common/zstd_compression.cpp
@@ -0,0 +1,59 @@
+// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include <array>
+#include <vector>
+
+#include <catch2/catch_test_macros.hpp>
+
+#include "common/common_types.h"
+#include "common/zstd_compression.h"
+
+namespace {
+
+std::vector<u8> Compress(const std::vector<u8>& data) {
+ return Common::Compression::CompressDataZSTDDefault(data.data(), data.size());
+}
+
+} // namespace
+
+TEST_CASE("ZSTD frames within the size limit decompress", "[common][zstd]") {
+ const std::vector<u8> data(100, 0x5a);
+ const auto compressed = Compress(data);
+
+ const auto result = Common::Compression::TryDecompressDataZSTD(compressed, data.size());
+ REQUIRE(result.has_value());
+ REQUIRE(*result == data);
+ REQUIRE(Common::Compression::DecompressDataZSTD(compressed) == data);
+
+ const auto empty = Common::Compression::TryDecompressDataZSTD(Compress({}), 0);
+ REQUIRE(empty.has_value());
+ REQUIRE(empty->empty());
+}
+
+TEST_CASE("ZSTD frames over the size limit are rejected", "[common][zstd]") {
+ const std::vector<u8> data(100, 0x5a);
+ const auto compressed = Compress(data);
+ REQUIRE_FALSE(Common::Compression::TryDecompressDataZSTD(compressed, data.size() - 1));
+}
+
+TEST_CASE("Invalid ZSTD frames are rejected without throwing", "[common][zstd]") {
+ const std::array<u8, 1> one_byte{0x01};
+ const std::array<u8, 5> bad_magic{0x01, 0x02, 0x03, 0x04, 0x05};
+ REQUIRE_FALSE(Common::Compression::TryDecompressDataZSTD(one_byte, 0x10000));
+ REQUIRE_FALSE(Common::Compression::TryDecompressDataZSTD(bad_magic, 0x10000));
+ REQUIRE(Common::Compression::DecompressDataZSTD(one_byte).empty());
+
+ // The header declares the size, but the data after it is cut off
+ std::vector<u8> truncated = Compress(std::vector<u8>(100, 0x5a));
+ truncated.pop_back();
+ REQUIRE_FALSE(Common::Compression::TryDecompressDataZSTD(truncated, 0x10000));
+}
+
+TEST_CASE("ZSTD frames without a declared size are rejected", "[common][zstd]") {
+ // Magic number, a frame header without the content size, then one raw 3-byte last block
+ const std::array<u8, 12> frame{0x28, 0xb5, 0x2f, 0xfd, 0x00, 0x00,
+ 0x19, 0x00, 0x00, 'a', 'b', 'c'};
+ REQUIRE_FALSE(Common::Compression::TryDecompressDataZSTD(frame, 0x10000));
+ REQUIRE(Common::Compression::DecompressDataZSTD(frame).empty());
+}
diff --git a/src/tests/core/internal_network/socket_proxy.cpp b/src/tests/core/internal_network/socket_proxy.cpp
index 0ba5f2aa2b..137de5b421 100644
--- a/src/tests/core/internal_network/socket_proxy.cpp
+++ b/src/tests/core/internal_network/socket_proxy.cpp
@@ -21,9 +21,7 @@ void BindSocket(Network::ProxySocket& socket, Network::Protocol protocol) {
.portno = 35000}) == Network::Errno::SUCCESS);
}
-template <std::size_t N>
-void QueuePacket(Network::ProxySocket& socket, Network::Protocol protocol,
- const std::array<u8, N>& payload) {
+Network::ProxyPacket MakePacket(Network::Protocol protocol) {
Network::ProxyPacket packet{};
packet.local_endpoint = {.family = Network::Domain::INET,
.ip = {192, 168, 1, 2},
@@ -32,6 +30,13 @@ void QueuePacket(Network::ProxySocket& socket, Network::Protocol protocol,
.ip = {192, 168, 1, 1},
.portno = 35000};
packet.protocol = protocol;
+ return packet;
+}
+
+template <std::size_t N>
+void QueuePacket(Network::ProxySocket& socket, Network::Protocol protocol,
+ const std::array<u8, N>& payload) {
+ Network::ProxyPacket packet = MakePacket(protocol);
packet.data.assign(payload.begin(), payload.end());
socket.HandleProxyPacket(packet);
}
@@ -228,3 +233,29 @@ TEST_CASE("One room packet wakes every blocked peek reader", "[core][sockets]")
REQUIRE(finished[i] - queued < std::chrono::milliseconds{1000});
}
}
+
+TEST_CASE("Room sockets accept only packets for their port and protocol", "[core][sockets]") {
+ Network::ProxySocket socket;
+ BindSocket(socket, Network::Protocol::UDP);
+
+ REQUIRE(socket.AcceptsProxyPacket(MakePacket(Network::Protocol::UDP)));
+ REQUIRE_FALSE(socket.AcceptsProxyPacket(MakePacket(Network::Protocol::TCP)));
+
+ Network::ProxyPacket other_port = MakePacket(Network::Protocol::UDP);
+ other_port.remote_endpoint.portno = 35001;
+ REQUIRE_FALSE(socket.AcceptsProxyPacket(other_port));
+
+ Network::ProxyPacket broadcast = MakePacket(Network::Protocol::UDP);
+ broadcast.broadcast = true;
+ REQUIRE_FALSE(socket.AcceptsProxyPacket(broadcast));
+ REQUIRE(socket.SetBroadcast(true) == Network::Errno::SUCCESS);
+ REQUIRE(socket.AcceptsProxyPacket(broadcast));
+
+ REQUIRE(socket.Close() == Network::Errno::SUCCESS);
+ REQUIRE_FALSE(socket.AcceptsProxyPacket(MakePacket(Network::Protocol::UDP)));
+}
+
+TEST_CASE("Native sockets accept no room packets", "[core][sockets]") {
+ Network::Socket socket;
+ REQUIRE_FALSE(socket.AcceptsProxyPacket(MakePacket(Network::Protocol::UDP)));
+}