diff options
| author | cirakg <cirakg@cirakg.xyz> | 2026-11-03 01:39:55 +0100 |
|---|---|---|
| committer | cirakg <cirakg@cirakg.xyz> | 2026-11-03 01:39:55 +0100 |
| commit | d8d16713b891245fd786b27d375f68d695c76d0e (patch) | |
| tree | 2972f24f132a2ece0896438bbc8f2cfd47f3c579 | |
| parent | 0b927d171c6d918a675c25f57b2bc0121b7f889d (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.txt | 1 | ||||
| -rw-r--r-- | src/tests/common/zstd_compression.cpp | 59 | ||||
| -rw-r--r-- | src/tests/core/internal_network/socket_proxy.cpp | 37 |
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))); +} |
