Commit Graph
2480 Commits
Author SHA1 Message Date
Max Lv ca359160b2 Include C++ headers before crypto.h in test_crypto_aead2022_cpp.cpp
crypto.h defines min/max macros for the C sources; libstdc++ headers
included after it fail to parse.
2026-10-14 22:53:08 +08:00
Max Lv f92f578cce Include <algorithm> for std::min in test_crypto_aead2022_cpp.cpp 2026-10-14 22:53:08 +08:00
Max Lv 47163a57fd Add allocation-free C++ SIP022 codecs
TCP request/response streams with the salt echo between the two
directions, an exact-match salt pool on FixedLru, and UDP packets sealed
and opened in place with caller-owned session state. Differentially
tested against aead2022.cpp in both roles; the steady phase allocates
nothing.
2026-10-14 22:53:08 +08:00
Max Lv 98eda0bdff Include <netinet/in.h> in the UDP eviction test client (FreeBSD)
FreeBSD's <arpa/inet.h> does not define struct sockaddr_in.
2026-10-14 22:53:08 +08:00
Max Lv a6113cec4b Own AEAD-2022 UDP sessions by reference so eviction cannot free a live session
The session table and the relay contexts each hold a reference; evicting a
session from the full table no longer frees memory a relay still uses.
Ported from the parked fix/aead2022-udp-session-eviction work.
2026-10-14 22:53:08 +08:00
Max Lv 503f0f1936 Include C++ headers before crypto.h in test_crypto_stream_cpp.cpp
crypto.h defines min/max macros for the C sources; libstdc++ headers
included after it fail to parse.
2026-10-14 22:53:07 +08:00
Max Lv 8cf10ba890 Do not memcpy from an empty span in the stream decoder
An empty read before the IV arrived passed a null source to memcpy,
which UBSan reports (and the ASan CI job turns into a failure).
2026-10-14 22:53:07 +08:00
Max Lv bf5bc2f216 Include <algorithm> for std::min in test_crypto_stream_cpp.cpp 2026-10-14 22:53:07 +08:00
Max Lv 0e6f0ca980 Document the crypto codecs, cache rewrite and allocation progress 2026-10-14 22:53:07 +08:00
Max Lv d91ef14f76 Add allocation-free C++ stream cipher codecs
src/crypto/stream.hpp implements the legacy stream methods this build
supports (AES CFB/CTR, Camellia CFB, Salsa20, ChaCha20, ChaCha20-IETF)
as value types. The AES and Camellia contexts are held by value through
Mbed TLS's block-cipher APIs instead of an allocated
mbedtls_cipher_context_t, and the libsodium ciphers track their
keystream position, finishing partial 64-byte blocks from a stack block
instead of the padding copies stream.cpp makes.

test_crypto_stream_cpp runs TCP streams (with fragments that split
keystream blocks) and UDP packets between the C and C++
implementations in both directions for all twelve methods and checks
IV replay; alloc_steady_crypto_stream requires zero allocations for new
connections and packets.
2026-10-14 22:53:07 +08:00
Max Lv ed75faf80f Rewrite the cache on a fixed slab without per-entry allocation
cache.cpp keeps its C API but replaces uthash and the two mallocs per
entry with one slab of nodes holding their keys inline, linked in
last-use order, and an open-addressing index of node ids that hashes
through the slab with a per-cache random seed. Both blocks are reserved
by cache_create() and touched only as entries are used, so a large,
mostly empty cache costs no resident memory.

Semantics are unchanged: an insert evicts the least recently used entry
once the cache holds capacity entries (the AEAD-2022 session table's
4095/4096 boundary), lookups refresh recency and age, cache_clear()
drops stale entries, and free_cb may re-enter the cache. Re-inserting a
key now replaces its data instead of shadowing it. cache_create_keyed()
sizes keys per cache; the AEAD-2022 salt pool and session table use it.

test_cache_cpp covers the boundary, ordering, replacement, ageing,
re-entrant callbacks and limits; alloc_steady_cache requires zero
allocations across 100,000 inserts on a full cache. AEAD-2022 ss-server
drops from 84 to 80 allocations per relayed connection.
2026-10-14 22:53:07 +08:00
Max Lv 110459eba8 Disable the zero-allocation checks with a system Mbed TLS
They hold for the bundled Mbed TLS (MBEDTLS_BLOCK_CIPHER_C); a system
one may allocate in GCM key setup, as the Debian package build shows.
2026-10-14 22:53:06 +08:00
Max Lv ab365b8fff Include C++ headers before crypto.h in test_crypto_aead_cpp.cpp
crypto.h defines min/max macros for the C sources; libstdc++ headers
included after it fail to parse.
2026-10-14 22:51:57 +08:00
Max Lv 0ff1fa6d66 Build the C++ AEAD codecs against Mbed TLS 2.x
Mbed TLS 2 returns void from mbedtls_sha1_starts/update/finish (the int
versions are the _ret variants), which the system-library build on
Ubuntu (2.28) rejects. crypto/mbedtls_compat.hpp gives one int-returning
spelling for both.
2026-10-14 22:51:57 +08:00
Max Lv 3e662faae7 Include <algorithm> for std::min in test_crypto_aead_cpp.cpp 2026-10-14 22:51:57 +08:00
Max Lv 556c1f0696 Add allocation-free C++ AEAD codecs
src/crypto/aead.hpp implements the SIP004/SIP007 AEAD ciphers as value
types: AeadEngine holds the libsodium or Mbed TLS GCM state by value,
AeadTcpEncryptor/AeadTcpDecryptor frame the chunk stream with a fixed
reassembly buffer, and aead_udp_seal/open handle packets. HKDF-SHA1 runs
on stack SHA-1 state instead of Mbed TLS's allocating HMAC API.

The bundled Mbed TLS config now enables MBEDTLS_BLOCK_CIPHER_C, which
keeps the AES context inside mbedtls_gcm_context, so GCM key setup no
longer allocates. That alone saves the C programs two allocations per
connection.

test_crypto_aead_cpp checks HKDF against RFC 5869 and crypto_hkdf, runs
TCP streams and UDP packets between the C and C++ implementations in
both directions for all five AEAD methods, and covers tampering,
framing and salt replay. alloc_steady_crypto_aead runs it under the
malloc counter and requires zero allocations across 600 new
connections and 600 packets (tests/alloc_audit/check_steady.py).

ss_strict_warnings() now applies -Wextra per source file so targets
mixing new C++ with converted C modules keep the latter at -Wall, and
Result gains move assignment.
2026-10-14 22:51:56 +08:00
Max Lv df7a6dc13f Add the C++ event layer over libuv
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.
2026-10-14 22:51:38 +08:00
Max Lv b040563874 Add the two casts ss-redir needs as C++
ss-redir only builds on Linux, so the macOS conversion checks never
compiled it: getsockname() wants a struct sockaddr *, and memcpy's
void * result needs a cast.
2026-10-14 22:51:17 +08:00
Max Lv 227ad1bb9c Compile the event loop, UDP relay and programs as C++
Rename utils, ss_event, udprelay, winsock, local, server, tunnel, redir
and manager from .c to .cpp. Inside the moved files only the
\snippet{doc} file references and header comments change; snippet
references in doc/*.md, tests/test_cli_docs.py, the build files and
CONTRIBUTION.md follow the new names.

Also cast the Windows-only string literal returned by
get_default_conf(), found by the zig x86_64-windows-gnu build.

Every module of src/ except the unbuilt android.c and the vendored
blake3 is now compiled as C++20. The new .cpp files join the no-heap
ratchet baseline.
2026-10-14 22:51:17 +08:00
Max Lv 1f259d0fa0 Make the event loop, UDP relay and programs compile as C++
Cast allocator results and void* callback data explicitly, cast string
literals stored in char * variables, replace compound literals in
ss_event.c with zeroing plus member assignment, and give ss-server's
listener array its MAX_REMOTE_NUM bound instead of a VLA.

Where a goto CLEAN_UP jumps over an initialised declaration (C++
rejects that), split the declaration from its initialisation in place.
The variables were already indeterminate at the label in C, and the
cleanup code does not read them. Checked for every MODULE_* variant of
udprelay.c, plus LIB_ONLY for local.c. Still valid C.
2026-10-14 22:51:16 +08:00
Max Lv 349d895fd2 Compile the crypto, config, DNS, ACL and plugin modules as C++
Rename netutils, jconf, stream, aead, aead2022, aead2022_udp, crypto,
acl, plugin, plugin_disabled and resolv from .c to .cpp without changing
their contents, and update source lists, snippet references, test
comments and debian/copyright.

The Doxygen source list and scripts/check_cli_docs.py now accept either
extension for each module, so the remaining programs can move one at a
time. The new .cpp files are added to the no-heap ratchet baseline.
2026-10-14 22:51:16 +08:00
Max Lv 3ae698f5be Raise the test origin's listen backlog
socketserver listens with a backlog of 5. With concurrent connections
through the proxy on a loaded machine (parallel ctest), the backlog
overflows, the kernel resets the origin connection, and the proxy
correctly drops the client, which surfaced as intermittent
alloc_audit_report failures ("getpeername: Invalid argument" in
ss-local, "Connection reset by peer" in ss-server). Reproduced
reliably at 64 concurrent connections; gone with a backlog of 256.
2026-10-14 22:51:16 +08:00
Max Lv 68bfae9b57 Make the crypto, config, DNS and plugin modules compile as C++
Cast allocator and memchr results explicitly, pass mbedtls_operation_t
to mbedtls_cipher_setkey, and declare the two locals in
ss2022_encrypt_first() before the gotos that jump past them. Drop the
unused RESOLV_MODE global that resolv.c defined by accident through an
anonymous enum declaration. Still valid C.
2026-10-14 22:51:16 +08:00
Max Lv a739789775 Keep GCC's C++-only warnings off for modules converted from C
GCC's C++ -Wall includes -Wsign-compare and -Wclass-memaccess, which C's
-Wall does not, so the converted modules failed -Werror on GCC (Linux,
UCRT64) although nothing in them changed. They keep the warning set they
were written against; code under ss_strict_warnings() re-enables both.
2026-10-14 22:51:16 +08:00
Max Lv e16c6bcd3e check_cxx_runtime: ignore glibc symbol versions
nm on glibc prints __cxa_atexit@GLIBC_2.2.5, which the allowlist for the
libc-provided __cxa_atexit/__cxa_finalize did not match.
2026-10-14 22:51:15 +08:00
Max Lv 21c13e8ddc Keep clang-tidy clean for the converted modules
Once the C headers are included from C++ translation units,
bugprone-dynamic-static-initializers reports each extern variable
declaration in them; none is defined in a header, so disable the check.
Mark the two bounded memchr searches in ssurl.cpp, which
bugprone-not-null-terminated-result misreads as string copies.
2026-10-14 22:51:15 +08:00
Max Lv 96ce822c6b Fix the Windows branches of process.cpp for C++
Cast allocator results and the thread argument explicitly and
value-initialise JOBOBJECT_EXTENDED_LIMIT_INFORMATION with {}. Found by
the zig x86_64-windows-gnu cross build.
2026-10-14 22:51:15 +08:00
Max Lv 738956b5da Compile the leaf modules as C++
Rename base64, json, ipset, process, ppbloom, rule, ssurl and cache from
.c to .cpp without touching their contents, and update the CMake source
lists, debian/copyright and the docs.

Ported code keeps -Wall; -Wextra is now opt-in through
ss_strict_warnings() for code written as C++ (the core tests).
check_no_heap.py gains a ratchet baseline for the converted modules,
which still allocate the way the C code did. A new check_cxx_runtime
test verifies with nm that the programs and libshadowsocks-c.a still
need no C++ runtime library, so C consumers are unaffected.
2026-10-14 22:51:15 +08:00
Max Lv 537bc77e79 Make headers and leaf modules compile as C++
Wrap the declarations of every project header in extern "C" (after the
system includes, which must keep C++ linkage), so converted modules and
the remaining C files agree on linkage.

In the leaf modules, cast allocator and memchr results explicitly,
rename identifiers called new, replace the getpwnam_r VLA with the
fixed 16 KiB buffer the loop already capped itself at, and make the
rlimit initialiser conversion explicit. All of this is still valid C.
2026-10-14 22:51:15 +08:00
Max Lv e052bf2e84 CI: let clang-tidy ignore GCC-only warning flags
The compile database comes from a GCC build, whose flags (for example
-Wno-class-memaccess) clang-tidy does not know and reports as errors.
2026-10-14 22:51:04 +08:00
Max Lv af339ccd4d CI: allow running the test and build workflows by hand
Stacked PRs target their parent branch, which these workflows do not
trigger on; workflow_dispatch lets each layer run the full matrix.
2026-10-14 20:11:15 +08:00
Max Lv 18550f5868 Fix CI for the C++ core: libstdc++ headers, static unwinder, newer clang
- docker/static: Alpine's libstdc++ headers come with g++, and the C++
  tests' static libstdc++ needs an unwinder that compiler-rt lacks; add
  g++ and llvm-libunwind.
- FixedBuffer: drop the unused kCapacity (clang-tidy 18 reports it as a
  dynamically initialized header static).
- ss-server on Windows: manager_addr was only read in POSIX-only code,
  which newer clang rejects (-Wunused-but-set-global); say that the
  option is ignored there.
2026-10-14 19:20:21 +08:00
Max Lv 2285528788 Document the C++20 port and allocation baseline
docs/cpp-core.md describes the language profile, the core primitives,
the phase plan and the measured C baseline (77-89 allocations per
connection in ss-server, 27-39 in ss-local). The compatibility contract
now keeps the C11 public header instead of a C11 code base. CLAUDE.md
drops stale libev, submodule and gnu99 references.
2026-10-14 12:51:47 +08:00
Max Lv 641643ec48 Add heap allocation audit for relayed connections
alloc_counter is a preloadable malloc counter (glibc, macOS) that writes
a snapshot on SIGUSR2. measure_alloc.py warms ss-local and ss-server up,
snapshots both, relays more connections and reports allocations per
connection. It runs report-only as the alloc_audit_report ctest.
interop.peers() now yields the started processes.
2026-10-14 12:51:47 +08:00
Max Lv f90cfe9034 Add C++20 ownership and allocation core
Header-only primitives for the no-heap-after-startup design:
Result/Status, Region, ObjectPool with PoolPtr (std::unique_ptr with a
pool deleter), generation-checked Handle and asynchronous close support,
intrusive RefPtr, FixedBuffer, MonotonicArena, FixedHashMap and FixedLru,
IntrusiveList, and a bounded FixedHeap for third-party allocator hooks.

Unit tests cover each primitive (test_core_*). scripts/check_no_heap.py
rejects heap allocation in C++ sources under src/ and runs as a ctest.
clang-tidy now also analyses C++ sources and the core headers.
2026-10-14 12:51:47 +08:00
Max Lv 0799d3a9aa Enable C++20 alongside C11 in the build
Add CXX to the project with C++20 (no extensions) and build C++ sources
with -Wextra -fno-exceptions -fno-rtti -fno-threadsafe-statics on top of
the existing warning flags. Give every toolchain a C++ compiler: the zig
Windows toolchain, the static macOS/Windows CI jobs, the static musl
image (clang++ plus libstdc++ headers) and the RPM spec.
2026-10-14 12:51:47 +08:00
Max LvandClaude 553cdc7945 Add opt-in printable TCP salt experiment for ss-local (#3063)
Add `ss-local --printable-salt` / `"printable_salt": true`, disabled by
default. For each outgoing TCP salt it picks a prefix length uniformly
from 6-12 bytes and samples those bytes as printable ASCII (0x20-0x7e)
immediately before key derivation. The salt stays 32 bytes with no
framing or markers, so the wire format is unchanged and any stock
server interoperates. Only chacha20-ietf-poly1305 and aes-256-gcm are
accepted; other ciphers fail at startup. UDP, server responses, and
AEAD-2022 are untouched.

Crypto unit tests cover length distribution, empty first writes, exact
framing overhead, salt-tail preservation, byte-at-a-time decryption,
tampering and replay rejection. CLI tests check local-only exposure and
cipher rejection via flag, config key and bad config type. interop.py
gains --printable-salt and --server-bin for independent stock servers.

docs/printable-salt.md records the design, entropy bound, fingerprint
caveats and route-experiment procedure; docs/encoding-experiments.md
and tests/experiments/ hold the Base85/lowpop85/TLS outer-transport
prototypes. docs/measurements/printable-salt-2026-09-23/ archives the
sanitized results of the aborted field trials.

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-13 09:09:51 +08:00
Max Lv d52ddf2bce Merge pull request #3062 from shadowsocks/feature/server-endpoints
Combine server host and port with strict IPv6 endpoint parsing
2026-09-13 07:23:22 +08:00
Max Lv 454bdc0a67 Accept combined server endpoints with strict IPv6 parsing 2026-09-13 07:15:28 +08:00
Max Lv 243f1f92ec Merge pull request #3061 from shadowsocks/feature/redesigned-cli
Redesign CLI flags, help, and argument validation
2026-09-12 21:25:11 +08:00
Max Lv ea951e6bd1 Redesign CLI flags, help, and argument validation 2026-09-12 21:17:38 +08:00
Max Lv 7bcb3d7dee Merge pull request #3060 from shadowsocks/madeye/cmake-build-test-error-on-macos
Raise the file descriptor limit for descriptor-heavy tests
2026-09-12 15:30:51 +08:00
Max Lv 8773939dce Raise the file descriptor limit for descriptor-heavy tests
test_event opens 256 UDP receivers plus a sender on top of the loop's own
descriptors. macOS starts processes with a soft RLIMIT_NOFILE of 256, so
socket() eventually returned -1 and the unchecked descriptor tripped the
bind() assertion when running ctest from a stock terminal (#3058).

Wrap every registered unit test in a small sh launcher that raises the soft
limit to 1024 before exec'ing the binary, and have test_event raise its own
limit as well so it also passes when run directly. Assert on socket()
failures so a limit problem is reported as such instead of as a bind error.

Fixes #3058

Claude-Session: https://claude.ai/code/session_01PSG4XV3jiqitDgdgFSGATx
2026-09-12 15:23:43 +08:00
Max Lv 36a5cc6b8d Merge pull request #3059 from shadowsocks/feature/publish-doxygen-pages
Publish native Doxygen documentation with GitHub Pages
2026-09-12 15:01:38 +08:00
Max Lv 6b11c7a632 Include downloadable man pages in the published documentation 2026-09-12 14:55:13 +08:00
Max Lv f5d5bf0be1 Publish native Doxygen documentation with GitHub Pages 2026-09-12 14:46:42 +08:00
Max Lv b281b739c7 Merge pull request #3057 from shadowsocks/feature/generated-cli-docs
Generate CLI manual sections from source option declarations
2026-09-12 10:57:59 +08:00
Max Lv 146818c182 Generate CLI manual sections from source option declarations 2026-09-12 10:51:25 +08:00
Max Lv 463ff72ca0 Merge pull request #3056 from shadowsocks/docs/add-contribution-guide
docs: add contribution guide
2026-09-12 10:31:55 +08:00
Max Lv 00ea8dec77 docs: add contribution guide 2026-09-12 10:31:30 +08:00