diff --git a/CMakeLists.txt b/CMakeLists.txt index c8bc18d..103af6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -115,6 +115,7 @@ set(COMMON_SOURCES src/core.c src/crypto.c src/network.c + src/packet_validate.c src/opus_codec.c src/display_sdl2.c src/config.c @@ -234,6 +235,7 @@ if(UNIX AND NOT APPLE) src/vaapi_decoder.c src/display_sdl2.c src/network.c + src/packet_validate.c src/crypto.c src/config.c src/input.c @@ -344,6 +346,10 @@ add_executable(test_encoding tests/unit/test_encoding.c) target_include_directories(test_encoding PRIVATE ${CMAKE_SOURCE_DIR}/include) add_test(NAME encoding_tests COMMAND test_encoding) +add_executable(test_packet tests/unit/test_packet.c src/packet_validate.c) +target_include_directories(test_packet PRIVATE ${CMAKE_SOURCE_DIR}/include) +add_test(NAME packet_tests COMMAND test_packet) + # ============================================================================= # Summary # ============================================================================= diff --git a/assets/rootstream.desktop b/assets/rootstream.desktop new file mode 100644 index 0000000..33d067c --- /dev/null +++ b/assets/rootstream.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Type=Application +Name=RootStream +Comment=Secure peer-to-peer game streaming +Exec=rootstream +Icon=rootstream +Terminal=false +Categories=Game;Network; diff --git a/include/rootstream.h b/include/rootstream.h index f7e00e5..003defc 100644 --- a/include/rootstream.h +++ b/include/rootstream.h @@ -510,6 +510,7 @@ int rootstream_net_send_video(rootstream_ctx_t *ctx, peer_t *peer, int rootstream_net_recv(rootstream_ctx_t *ctx, int timeout_ms); int rootstream_net_handshake(rootstream_ctx_t *ctx, peer_t *peer); void rootstream_net_tick(rootstream_ctx_t *ctx); +int rootstream_net_validate_packet(const uint8_t *buffer, size_t len); /* --- Peer Management --- */ peer_t* rootstream_add_peer(rootstream_ctx_t *ctx, const char *rootstream_code); diff --git a/install.sh b/install.sh index 297017f..b0d9ae8 100644 --- a/install.sh +++ b/install.sh @@ -123,6 +123,13 @@ install_binary() { sudo install -m 755 rootstream /usr/local/bin/ echo -e "${GREEN}✓ Installed to /usr/local/bin/rootstream${NC}" + + if [ -f assets/rootstream.desktop ]; then + sudo install -m 644 assets/rootstream.desktop /usr/local/share/applications/rootstream.desktop + fi + if [ -f assets/rootstream.png ]; then + sudo install -m 644 assets/rootstream.png /usr/local/share/icons/hicolor/256x256/apps/rootstream.png + fi } # Setup permissions @@ -181,6 +188,12 @@ main() { install_binary setup_permissions test_vaapi + + if [ -f rootstream.service ]; then + echo -e "\n${BLUE}🧩 Installing systemd service template...${NC}" + sudo install -m 644 rootstream.service /etc/systemd/system/rootstream@.service + echo -e "${GREEN}✓ Installed /etc/systemd/system/rootstream@.service${NC}" + fi echo -e "\n${GREEN}╔═══════════════════════════════════════════════╗${NC}" echo -e "${GREEN}║ 🎉 Installation Complete! ║${NC}" diff --git a/src/network.c b/src/network.c index 1289173..92c35eb 100644 --- a/src/network.c +++ b/src/network.c @@ -379,19 +379,12 @@ int rootstream_net_recv(rootstream_ctx_t *ctx, int timeout_ms) { return 0; } - packet_header_t *hdr = (packet_header_t*)buffer; - - /* Validate magic number */ - if (hdr->magic != PACKET_MAGIC) { - fprintf(stderr, "WARNING: Invalid magic number, ignoring packet\n"); + if (rootstream_net_validate_packet(buffer, (size_t)recv_len) != 0) { + fprintf(stderr, "WARNING: Invalid packet received (%d bytes)\n", recv_len); return 0; } - /* Validate version */ - if (hdr->version != 1) { - fprintf(stderr, "WARNING: Unsupported protocol version %d\n", hdr->version); - return 0; - } + packet_header_t *hdr = (packet_header_t*)buffer; /* Find or create peer */ peer_t *peer = rootstream_find_peer_by_addr(ctx, &from, fromlen); diff --git a/src/network_stub.c b/src/network_stub.c index 8a492c4..521092e 100644 --- a/src/network_stub.c +++ b/src/network_stub.c @@ -47,6 +47,12 @@ int rootstream_net_recv(rootstream_ctx_t *ctx, int timeout_ms) { return -1; } +int rootstream_net_validate_packet(const uint8_t *buffer, size_t len) { + (void)buffer; + (void)len; + return -1; +} + int rootstream_net_handshake(rootstream_ctx_t *ctx, peer_t *peer) { (void)ctx; (void)peer; diff --git a/src/packet_validate.c b/src/packet_validate.c new file mode 100644 index 0000000..15038fc --- /dev/null +++ b/src/packet_validate.c @@ -0,0 +1,26 @@ +/* + * packet_validate.c - Lightweight packet validation + */ + +#include "../include/rootstream.h" + +int rootstream_net_validate_packet(const uint8_t *buffer, size_t len) { + if (!buffer || len < sizeof(packet_header_t)) { + return -1; + } + + const packet_header_t *hdr = (const packet_header_t*)buffer; + if (hdr->magic != 0x524F4F54) { + return -1; + } + + if (hdr->version != 1) { + return -1; + } + + if (hdr->payload_size > len - sizeof(packet_header_t)) { + return -1; + } + + return 0; +} diff --git a/tests/unit/test_packet.c b/tests/unit/test_packet.c new file mode 100644 index 0000000..6fa7975 --- /dev/null +++ b/tests/unit/test_packet.c @@ -0,0 +1,46 @@ +#include "../../include/rootstream.h" +#include +#include +#include + +static void fill_random(uint8_t *buf, size_t len) { + for (size_t i = 0; i < len; i++) { + buf[i] = (uint8_t)(rand() & 0xFF); + } +} + +int main(void) { + srand(1); + + uint8_t buffer[512]; + + /* Random fuzz cases should never crash */ + for (int i = 0; i < 1000; i++) { + size_t len = (size_t)(rand() % sizeof(buffer)); + fill_random(buffer, len); + (void)rootstream_net_validate_packet(buffer, len); + } + + /* Construct a minimal valid packet */ + packet_header_t hdr = {0}; + hdr.magic = 0x524F4F54; + hdr.version = 1; + hdr.type = PKT_PING; + hdr.payload_size = 0; + + memcpy(buffer, &hdr, sizeof(hdr)); + if (rootstream_net_validate_packet(buffer, sizeof(hdr)) != 0) { + fprintf(stderr, "Expected valid packet to pass validation\n"); + return 1; + } + + /* Invalid payload size should fail */ + hdr.payload_size = 1024; + memcpy(buffer, &hdr, sizeof(hdr)); + if (rootstream_net_validate_packet(buffer, sizeof(hdr)) == 0) { + fprintf(stderr, "Expected oversized payload to fail validation\n"); + return 1; + } + + return 0; +} diff --git a/tools/smoke.sh b/tools/smoke.sh new file mode 100755 index 0000000..bf13bcb --- /dev/null +++ b/tools/smoke.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" + +cmake -S "${ROOT_DIR}" -B "${ROOT_DIR}/build" +cmake --build "${ROOT_DIR}/build" +ctest --test-dir "${ROOT_DIR}/build" + +"${ROOT_DIR}/build/rootstream" --version +"${ROOT_DIR}/build/rootstream" --help >/dev/null