Files
Max LvandClaude 50f1ae54f7 Fix findings from the security audit (#3083)
* Fix findings from the security audit

Remote:
- TCP: handshake/connect deadlines follow --timeout instead of a 24h
  minimum, so idle sockets can no longer fill the connection pool.
- ACL: IPv4-mapped IPv6 addresses are matched against the IPv4 lists.
- UDP server now enforces the peer ACL and the outbound block list.
- ACL loading fails closed on bad files or invalid entries.
- TCP half-close is relayed instead of tearing the connection down.
- AEAD-2022 UDP sessions are created only after timestamp/replay checks.

ss-manager:
- Owner-only conf files (deleted on remove), 0600 control socket,
  umask 022, safe working directory, loopback-only IP addresses,
  per-user sender check on Linux, plugins only over the UNIX socket.
- Port canonicalisation, list() overflow, add/remove waits for the
  server, stale pid files can't signal unrelated processes.

Also: plugins drop to --user, daemonize readiness/pid file handling,
resolver cancellation, replay-filter fail-closed, key wiping, spec
strictness in AEAD/2022 parsing, JSON surrogate validation, ss:// plugin
name checks, embedding API state reset, Android protect_socket replies.

Adds tests/test_server_acl.py and tests/test_half_close.py and extends
the manager, ACL, AEAD-2022 and ssurl tests.

Co-Authored-By: Claude <noreply@anthropic.com>

* Fix CI on Ubuntu 26.04 runners; use string_view over raw string pointers

- Define _GLIBCXX_NO_ASSERTIONS: libstdc++ 15 enables assertions at -O0,
  which pulls __glibcxx_assert_fail (C++ runtime) into the programs.
- test_redir_qemu: boot the guest with -cpu max so the runner's glibc
  loader runs under TCG.
- find_program: std::string_view PATH splitting into a fixed buffer, no
  heap and no null snprintf destination (GCC -Wformat-truncation).
- manager: parse /proc/net/udp numbers with strtoul (cert-err34-c);
  walk /proc/<pid>/cmdline with std::string_view.
- ssurl: sanitise the tag through std::span.

Co-Authored-By: Claude <noreply@anthropic.com>

* coverage: tolerate gcov 15 lambda inconsistencies in lcov 2

Co-Authored-By: Claude <noreply@anthropic.com>

* resolver: replace init's cleanup lambda with a member; lcov --list ignores inconsistent

gcov 15 marks the lambda as not hit while its lines are, which lcov 2
rejects.

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-28 09:34:36 +08:00

164 lines
6.0 KiB
CMake

cmake_minimum_required(VERSION 3.20)
set(PROJECT_NAME shadowsocks-c)
set(RELEASE_DATE 2026-02-09)
set(PROJECT_VERSION "3.3.6")
set(PROJECT_DESC "a lightweight secured socks5 proxy")
set(PROJECT_URL "https://shadowsocks.org")
set(PROJECT_ISSUES_URL "https://github.com/shadowsocks/shadowsocks-libev")
project(${PROJECT_NAME} VERSION ${PROJECT_VERSION} LANGUAGES C CXX)
if(MSVC)
message(FATAL_ERROR "MSVC is not supported yet. Use MinGW-w64 (UCRT64) or the Zig Windows toolchain; see docs/modernization.md.")
endif()
include(GNUInstallDirs)
include(CTest)
# Compilation database for clang-tidy and other tooling
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# C11 with GNU extensions. _GNU_SOURCE stays for the glibc-specific
# interfaces used by the Linux-only modules (ss-redir, connmark/nftables).
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS ON)
# The C++20 core (src/core) is being introduced module by module; see
# docs/cpp-core.md. It is built without exceptions or RTTI, so it adds
# no unwinding tables or typeinfo to the static binaries.
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
option(ENABLE_SANITIZERS "Build with AddressSanitizer and UndefinedBehaviorSanitizer" OFF)
option(ENABLE_COVERAGE "Build with coverage instrumentation" OFF)
option(SS_WARNINGS_AS_ERRORS "Treat project warnings as errors" ON)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/cmake)
set(CMAKE_MACOSX_RPATH TRUE)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build configuration" FORCE)
endif ()
# Detect linux
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(LINUX TRUE)
endif ()
message(STATUS "Running cmake version ${CMAKE_VERSION}")
option(WITH_STATIC "Link executable dependencies statically" ON)
option(SS_BUILD_EXECUTABLES "Build command-line programs" ON)
option(SS_BUILD_STATIC_LIBRARY "Build the static embedding library" ON)
option(SS_BUILD_SHARED_LIBRARY "Build the shared embedding library" ON)
option(SS_INSTALL_TOOLS "Install platform shell tools" OFF)
option(ENABLE_CONNMARKTOS "Enable saved connmark to IP TOS QoS feature" OFF)
option(ENABLE_NFTABLES "Report malicious IP to nftables" OFF)
option(SS_MINIMAL "Disable regex, plugins and manager" OFF)
option(SS_ENABLE_REGEX "Enable PCRE2 ACL patterns" ON)
option(SS_ENABLE_PLUGINS "Enable SIP003 and obfsproxy subprocesses" ON)
if(SS_MINIMAL)
set(SS_ENABLE_REGEX OFF)
set(SS_ENABLE_PLUGINS OFF)
set(WITH_SS_MANAGER OFF)
endif()
include(cmake/Dependencies.cmake)
# Connmarktos support
if(ENABLE_CONNMARKTOS)
if(NOT LINUX)
message(FATAL_ERROR "connmarktos is only supported on Linux")
endif()
find_library(NETFILTER_CONNTRACK_LIB netfilter_conntrack)
find_library(NFNETLINK_LIB nfnetlink)
if(NOT NETFILTER_CONNTRACK_LIB)
message(FATAL_ERROR "--enable-connmarktos specified but libnetfilter_conntrack not found")
endif()
set(USE_NFCONNTRACK_TOS 1)
message(STATUS "Connmarktos enabled")
endif()
# Nftables support
if(ENABLE_NFTABLES)
if(NOT LINUX)
message(FATAL_ERROR "nftables is only supported on Linux")
endif()
find_library(MNL_LIB mnl)
find_library(NFTNL_LIB nftnl)
if(NOT MNL_LIB OR NOT NFTNL_LIB)
message(FATAL_ERROR "--enable-nftables specified but libmnl or libnftnl not found")
endif()
set(USE_NFTABLES 1)
message(STATUS "Nftables enabled")
endif()
# Run platform tests
include(${PROJECT_SOURCE_DIR}/cmake/configure.cmake)
configure_file(${PROJECT_SOURCE_DIR}/cmake/config.h.cmake ${PROJECT_BINARY_DIR}/src/config.h)
include(cmake/ProjectOptions.cmake)
include_directories(${PROJECT_BINARY_DIR}/src)
add_compile_definitions(HAVE_CONFIG_H)
set(LIBBLOOM_SOURCE
third_party/bloom/bloom.c
third_party/bloom/murmur2/MurmurHash2.c
)
add_library(bloom STATIC ${LIBBLOOM_SOURCE})
target_include_directories(bloom PUBLIC third_party/bloom third_party/bloom/murmur2)
add_subdirectory(src)
include(cmake/InstallPackage.cmake)
add_subdirectory(doc)
# Testing
include(CTest)
if(BUILD_TESTING)
add_subdirectory(tests)
endif()
# Coverage report (requires ENABLE_COVERAGE=ON, lcov and genhtml)
# Usage: make coverage (after running ctest / integration tests)
# gcov 15 can record a lambda's lines as hit but the lambda itself as not;
# lcov 2 treats that as fatal ("inconsistent"), so it is only a warning here.
if (ENABLE_COVERAGE)
find_program(LCOV_EXECUTABLE lcov)
find_program(GENHTML_EXECUTABLE genhtml)
if (LCOV_EXECUTABLE AND GENHTML_EXECUTABLE)
add_custom_target(coverage
COMMAND ${LCOV_EXECUTABLE} --capture --directory ${PROJECT_BINARY_DIR}
--output-file ${PROJECT_BINARY_DIR}/coverage.info
--include ${PROJECT_SOURCE_DIR}/src/*
--ignore-errors mismatch,unused,empty,gcov,inconsistent
COMMAND ${LCOV_EXECUTABLE} --list ${PROJECT_BINARY_DIR}/coverage.info
--ignore-errors inconsistent
COMMAND ${GENHTML_EXECUTABLE} ${PROJECT_BINARY_DIR}/coverage.info
--ignore-errors inconsistent
--output-directory ${PROJECT_BINARY_DIR}/coverage-html
COMMAND ${CMAKE_COMMAND} -E echo "Coverage report: ${PROJECT_BINARY_DIR}/coverage-html/index.html"
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
VERBATIM
)
else ()
message(WARNING "lcov/genhtml not found - 'coverage' target unavailable")
endif ()
endif ()
# Install ss-nat on Linux
if(LINUX AND SS_INSTALL_TOOLS)
install(PROGRAMS src/ss-nat DESTINATION ${CMAKE_INSTALL_BINDIR})
endif()
# Install ss-setup TUI tool
if(UNIX AND SS_INSTALL_TOOLS)
install(PROGRAMS scripts/ss-setup.sh DESTINATION ${CMAKE_INSTALL_BINDIR} RENAME ss-setup)
endif()