src/event/loop.hpp wraps uv_loop/poll/timer/signal/async handles that live by value inside their owner and dispatch to an owner member function chosen at compile time. A Poll owns its socket and carries read and write interest on one handle, closing the descriptor in libuv's close callback. CloseGroup collects the asynchronous closes of an owner's handles and reports completion afterwards, which is how pooled owners implement AsyncClosable. DeadlineList drives the idle timeouts of many owners from one timer. test_event_cpp covers readiness, closing a peer from inside a callback, recycling a pooled owner only after its handles closed, repeating timers, deadline ordering and cross-thread wakeups.
6.6 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
shadowsocks-c (formerly shadowsocks-libev) is a lightweight SOCKS5 proxy. Version 3.3.6, licensed under GPLv3.
The sources are C11 and are being ported to C++20 module by module (see docs/cpp-core.md); the public
src/shadowsocks.h stays a C header with a stable ABI.
Build Commands
CMake (sole build system)
cmake --preset bundled # or: cmake -S . -B build -G Ninja
cmake --build --preset bundled
ctest --preset bundled
Bundled mode (the default, SS_DEPENDENCY_MODE=bundled) builds every dependency from the pinned archives in
third_party/dist with no network access. SS_DEPENDENCY_MODE=system uses distro libraries instead.
There are no git submodules.
Binaries land in <build>/bin/, libraries in <build>/lib/.
Build Dependencies
- CMake >= 3.20, C11 and C++20 compilers (GCC >= 11, Clang >= 14, Apple Clang >= 15), Ninja for the presets
- System mode only: Mbed TLS 2.x/3.x, libsodium >= 1.0.4, PCRE2 (8-bit), libuv, c-ares
- Doxygen (documentation only)
CMake Options
-DSS_MINIMAL=ON: no regex, plugins, manager or legacy stream ciphers-DWITH_STATIC=OFF: link third-party dependencies dynamically-DSS_BUILD_EXECUTABLES / SS_BUILD_STATIC_LIBRARY / SS_BUILD_SHARED_LIBRARY: choose outputs-DWITH_DOC_MAN=ON/-DWITH_DOC_HTML=ON: build documentation-DENABLE_CONNMARKTOS=ON,-DENABLE_NFTABLES=ON: Linux netfilter features-DENABLE_SANITIZERS=ON,-DENABLE_COVERAGE=ON,-DDISABLE_SSP=ON,-DBUILD_TESTING=OFF
Testing
Unit Tests (CTest)
ctest --test-dir build -LE memcheck --output-on-failure
C unit tests (tests/test_*.c) compile the specific src/*.c files they need (see ss_add_test in
tests/CMakeLists.txt). C++ core tests are tests/test_core_*.cpp using tests/test_helpers.hpp.
Labels: unit, vendor, integration, memcheck (Linux + valgrind).
Integration Tests
python3 tests/interop.py --self --bin build/bin # TCP/UDP relay, all AEAD/2022 methods
python3 tests/test.py --bin build/bin/ -c tests/aes-gcm.json # one cipher via curl/dig (needs network)
python3 tests/test_cli.py --bin-dir build/bin # CLI contract: help/version/exit codes
python3 tests/stress_test.py --bin build/bin/ --size 10
Allocation audit
python3 tests/alloc_audit/measure_alloc.py --bin build/bin --shim build/lib/libss_alloc_audit.dylib
Reports heap allocations per relayed connection (C baseline numbers are in docs/cpp-core.md).
Code Formatting
Uses uncrustify with the config at .uncrustify.cfg. Key settings: 4-space indent, no tabs, 120-column width, K&R brace style (braces on same line).
Code Quality Tooling
- clang-tidy: config in
.clang-tidy. The build exportscompile_commands.jsonautomatically. Run locally:On macOS with Homebrew LLVM, addrun-clang-tidy -quiet -p build '/(src/(core/|event/|relay/|crypto/|api/)?[^/]+\.(c|cpp)|tests/test_(core_[^/]+|event_cpp)\.cpp)$'-extra-arg="-isysroot$(xcrun --show-sdk-path)". CI (clang-tidyjob intests.yml) enforces a warning-count ratchet viaMAX_WARNINGS— lower it when fixing findings; never raise it without justification. - No-heap lint:
scripts/check_no_heap.py --self-test(also a ctest) rejects heap allocation in C++ sources undersrc/. - Sanitizers:
cmake .. -DENABLE_SANITIZERS=ONbuilds with ASan + UBSan. CI runs ctest and the stress test under sanitizers on every PR. - Coverage:
cmake .. -DENABLE_COVERAGE=ON, run tests, thenmake coverage(needs lcov). HTML report lands inbuild/coverage-html/. CI uploads it as thecoverage-htmlartifact.
Architecture
Binaries (all in src/)
Each binary is compiled with a module define that controls conditional compilation:
| Binary | Define | Purpose |
|---|---|---|
ss-local |
MODULE_LOCAL |
SOCKS5 client proxy |
ss-server |
MODULE_REMOTE |
Server-side proxy |
ss-tunnel |
MODULE_TUNNEL |
Port forwarding tunnel (implies MODULE_LOCAL) |
ss-redir |
MODULE_REDIR |
Transparent proxy via iptables (Linux only, implies MODULE_LOCAL) |
ss-manager |
MODULE_MANAGER |
Multi-server manager daemon (Linux only by default) |
A library libshadowsocks-c (legacy names libshadowsocks-libev.* are kept) is built from the ss-local sources with -DLIB_ONLY. Its public API is in src/shadowsocks.h.
Source Organization (src/)
Shared by all binaries:
utils.cpp- logging, system utilities,ss_malloc/ss_freejconf.cpp/json.cpp- JSON config file parsingnetutils.cpp- network address utilitiescache.cpp- uthash-based connection cache (UDP relay, AEAD-2022 tables)udprelay.cpp- UDP relay implementation (shared, but uses#ifdef MODULE_*for per-binary behavior)ss_event.cpp/ss_event.h- event loop API over libuvresolv.cpp- asynchronous DNS via c-ares
Crypto layer (behind a common crypto_t function-pointer interface):
crypto.cpp/crypto.h- crypto initialization, key derivation (HKDF),buffer_tmanagementstream.cpp- legacy stream ciphersaead.cpp- AEAD ciphers (AES-GCM via Mbed TLS/libsodium, ChaCha20-Poly1305 via libsodium)aead2022.cpp/aead2022_udp.cpp- Shadowsocks 2022 (SIP022), BLAKE3 fromsrc/blake3ppbloom.cpp- ping-pong bloom filter for salt replay detection (third_party/bloom)
ACL: acl.cpp / rule.cpp / ipset.cpp - IP prefix sets and PCRE2/literal domain rules.
Plugins: plugin.cpp / process.cpp - SIP003 plugin subprocess management.
C++ core: src/core/*.hpp - header-only ownership and allocation primitives (pools, handles, fixed
buffers, arena, fixed hash map/LRU, intrusive list, bounded heap). See docs/cpp-core.md.
Event Loop
All binaries use the ss_event API (libuv backend: kqueue/epoll/IOCP). The connection lifecycle follows stages defined in src/common.h: STAGE_INIT -> STAGE_HANDSHAKE -> STAGE_RESOLVE -> STAGE_STREAM -> STAGE_STOP. Each binary defines its own listen_ctx_t, server_t, and remote_t structs (note: "server" in local.h means the local-side connection, "remote" means the ss-server side).
Compiler Flags
From cmake/ProjectOptions.cmake: -Wall -Werror -Wno-deprecated-declarations -fno-strict-aliasing -fstack-protector-strong, plus _GNU_SOURCE on Linux. C is C11 with GNU extensions. C++ is C++20 without extensions and adds -Wextra -fno-exceptions -fno-rtti -fno-threadsafe-statics.
-Werror means all warnings are errors — new code must compile warning-free.