From 146818c182e5377b58085ff595d69b4afd0cdb11 Mon Sep 17 00:00:00 2001 From: Max Lv Date: Sat, 12 Sep 2026 10:51:25 +0800 Subject: [PATCH] Generate CLI manual sections from source option declarations --- .github/workflows/tests.yml | 21 ++++ CONTRIBUTION.md | 32 ++++++ README.md | 6 +- doc/CMakeLists.txt | 58 ++++++++-- doc/asciidoc.conf | 4 +- doc/shadowsocks-c.asciidoc | 211 ++++++------------------------------ doc/ss-local.asciidoc | 183 +++++++++++++++---------------- doc/ss-manager.asciidoc | 156 +++++++++++++------------- doc/ss-nat.asciidoc | 28 +++-- doc/ss-redir.asciidoc | 139 ++++++++++++------------ doc/ss-server.asciidoc | 168 ++++++++++++++-------------- doc/ss-tunnel.asciidoc | 156 +++++++++++++------------- scripts/gen_cli_docs.py | 194 +++++++++++++++++++++++++++++++++ src/manager.c | 10 ++ src/redir.c | 4 + src/server.c | 13 +++ src/ss-nat | 46 ++++++++ src/utils.c | 156 ++++++++++++++++++++++++++ tests/test_gen_cli_docs.py | 107 ++++++++++++++++++ 19 files changed, 1082 insertions(+), 610 deletions(-) create mode 100644 scripts/gen_cli_docs.py create mode 100644 tests/test_gen_cli_docs.py diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3bfcfd5ce..49513d8f0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -26,6 +26,27 @@ jobs: run: actionlint -shellcheck= -pyflakes= - name: Lint Python scripts and tests run: ruff check --select E9,F63,F7,F82 tests scripts + - name: Check generated CLI documentation + run: | + python3 scripts/gen_cli_docs.py --check + python3 -m unittest discover -s tests -p test_gen_cli_docs.py + + docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - name: Install documentation tools + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends asciidoc xmlto docbook-xml docbook-xsl + - name: Build man pages and HTML from source + run: | + cmake -S . -B build-docs -DWITH_DOC_MAN=ON -DWITH_DOC_HTML=ON + cmake --build build-docs --target doc-man doc-html --parallel 2 + test -s build-docs/man/ss-local.1 + test -s build-docs/man/ss-nat.1 + test -s build-docs/man/shadowsocks-c.8 + test -s build-docs/html/ss-local.html tests: strategy: diff --git a/CONTRIBUTION.md b/CONTRIBUTION.md index 882083e71..4481d2149 100644 --- a/CONTRIBUTION.md +++ b/CONTRIBUTION.md @@ -73,6 +73,38 @@ unless a change has been discussed. For bundled dependency updates, follow [the update procedure](third_party/README.md) and preserve upstream licenses and notices. +## CLI and manual documentation + +The SYNOPSIS and OPTIONS sections of the manual pages are generated from the +literal `getopt_long` declarations in `src/{local,server,tunnel,redir,manager}.c` +and the `getopts` declaration in `src/ss-nat`. Descriptions and argument names live +in `CLI_DOC` source comments: common C options in `src/utils.c`, program-specific +overrides in the corresponding C file, and shell options in `src/ss-nat`. +Each entry has an AsciiDoc term such as `--mtu ::` followed by its description. +The generator checks option coverage and argument arity across platform variants; +describe platform or feature restrictions in the comment. Cipher lists come from +the C cipher tables. Keep explanatory sections and examples in `doc/*.asciidoc`. + +After changing a parser or its documentation comments, regenerate the checked-in +pages and run the generator tests: + +```sh +python3 scripts/gen_cli_docs.py +python3 scripts/gen_cli_docs.py --check +python3 -m unittest discover -s tests -p test_gen_cli_docs.py +``` + +To render the manuals, install Python 3, AsciiDoc, and xmlto, then run: + +```sh +cmake -S . -B build-docs -DWITH_DOC_MAN=ON -DWITH_DOC_HTML=ON +cmake --build build-docs --target doc-man doc-html --parallel +``` + +The build generates pages in the build directory without modifying source files +or executing target binaries, so it also works when cross-compiling. CI checks +that committed pages are current and renders both man and HTML output. + ## Pull requests Open your pull request against `master`. Explain the problem, what changes for diff --git a/README.md b/README.md index 32de874d8..0233bcb79 100644 --- a/README.md +++ b/README.md @@ -145,7 +145,7 @@ client mode and build options. Existing Snap packages still use the The default build uses pinned sources included in this repository. It needs a C11 compiler, CMake 3.20+, and Make or Ninja. No Git submodules, dependency package installations, or network access are needed for configuration/build. -Python is used only by integration tests; documentation generation is optional. +Python is used by integration tests and optional documentation generation. ```sh cmake -S . -B build -DCMAKE_BUILD_TYPE=Release @@ -157,6 +157,10 @@ cmake --install build --prefix /your/install/prefix Programs are in `build/bin/`. Bundled binaries link to platform runtime libraries; they do not require separately installed third-party libraries. +Man pages and HTML documentation derive their CLI sections from the source +option parsers. See [the documentation workflow](CONTRIBUTION.md#cli-and-manual-documentation) +for regeneration and rendering commands. + For a smaller build, use `-DSS_MINIMAL=ON`. It excludes PCRE2 regex, plugin subprocesses, the manager, and legacy stream ciphers. Minimal ACLs support IPv4/IPv6 CIDRs, `full:example.com` for exact domains, and diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index b996f9eef..f7f06c54b 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -10,18 +10,53 @@ if(WITH_DOC_MAN AND NOT XMLTO_EXECUTABLE) message(FATAL_ERROR "Manpage generation requires xmlto") endif() -# NOTE For brew user, we have to setup this env var. see `brew info asciidoc' +# Homebrew catalogs are outside libxml's default search path. Respect an +# explicitly configured catalog and support both Apple Silicon and Intel Macs. set(XMLTO_ENV) -set(XMLTO_CATALOG_DIR_MACOS /usr/local/etc/xml/catalog) -if (EXISTS ${XMLTO_CATALOG_DIR_MACOS}) - set(XMLTO_ENV XML_CATALOG_FILES=${XMLTO_CATALOG_DIR_MACOS}) - message(STATUS "Detect xmlto catalog dir ${XMLTO_CATALOG_DIR_MACOS}") -endif () +if(APPLE AND "$ENV{XML_CATALOG_FILES}" STREQUAL "") + foreach(catalog /opt/homebrew/etc/xml/catalog /usr/local/etc/xml/catalog) + if(EXISTS ${catalog}) + set(XMLTO_ENV XML_CATALOG_FILES=${catalog}) + break() + endif() + endforeach() +endif() set(CMAKE_MANPAGE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/man) set(CMAKE_HTML_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/html) set(DOC_DIR ${PROJECT_SOURCE_DIR}/doc) +find_package(Python3 COMPONENTS Interpreter QUIET) +if((WITH_DOC_MAN OR WITH_DOC_HTML) AND NOT Python3_Interpreter_FOUND) + message(FATAL_ERROR "Documentation generation requires Python 3") +endif() + +# Read source declarations, never execute target binaries (including cross builds). +# Keep checked-in pages for readers and for builds with documentation disabled. +set(CLI_DOC_DIR ${DOC_DIR}) +set(CLI_DOC_FILES) +if(Python3_Interpreter_FOUND) + set(CLI_DOC_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated) + set(CLI_DOC_INPUTS ${PROJECT_SOURCE_DIR}/src/utils.c + ${PROJECT_SOURCE_DIR}/src/aead.c ${PROJECT_SOURCE_DIR}/src/stream.c + ${PROJECT_SOURCE_DIR}/src/ss-nat) + foreach(module local server tunnel redir manager) + list(APPEND CLI_DOC_INPUTS ${PROJECT_SOURCE_DIR}/src/${module}.c) + endforeach() + foreach(name ss-local ss-server ss-tunnel ss-redir ss-manager ss-nat shadowsocks-c) + list(APPEND CLI_DOC_INPUTS ${DOC_DIR}/${name}.asciidoc) + list(APPEND CLI_DOC_FILES ${CLI_DOC_DIR}/${name}.asciidoc) + endforeach() + add_custom_command(OUTPUT ${CLI_DOC_FILES} + COMMAND ${Python3_EXECUTABLE} ${PROJECT_SOURCE_DIR}/scripts/gen_cli_docs.py + --output-dir ${CLI_DOC_DIR} + COMMAND ${CMAKE_COMMAND} -E touch ${CLI_DOC_FILES} + DEPENDS ${CLI_DOC_INPUTS} ${PROJECT_SOURCE_DIR}/scripts/gen_cli_docs.py + COMMENT "Generating CLI documentation from source" + VERBATIM) + add_custom_target(doc-cli DEPENDS ${CLI_DOC_FILES}) +endif() + set(XMLTO_OPTS -m ${DOC_DIR}/manpage-normal.xsl -m ${DOC_DIR}/manpage-bold-literal.xsl man) set(ASCIIDOC_XML_OPTS -b docbook -d manpage -f ${DOC_DIR}/asciidoc.conf -aversion=${PROJECT_VERSION}) set(ASCIIDOC_HTML_OPTS -b html4 -d article -f ${DOC_DIR}/asciidoc.conf -aversion=${PROJECT_VERSION}) @@ -38,14 +73,15 @@ foreach (manfile IN LISTS MAN_NAMES) set(manfile ${CMAKE_MANPAGE_OUTPUT_DIRECTORY}/${manfile}) set(htmlfile ${CMAKE_HTML_OUTPUT_DIRECTORY}/${htmlfile}) - set(docfile ${DOC_DIR}/${docfile}) + set(docfile ${CLI_DOC_DIR}/${docfile}) add_custom_command(OUTPUT ${manfile} COMMAND ${ASCIIDOC_EXECUTABLE} ${ASCIIDOC_XML_OPTS} -o ${xmlfile} ${docfile} COMMAND ${CMAKE_COMMAND} -E env ${XMLTO_ENV} ${XMLTO_EXECUTABLE} ${XMLTO_OPTS} ${xmlfile} # After we built the manpage, the xmlfile is nolongger needed COMMAND ${CMAKE_COMMAND} -E remove ${xmlfile} - DEPENDS ${docfile} + DEPENDS ${docfile} ${DOC_DIR}/asciidoc.conf + ${DOC_DIR}/manpage-normal.xsl ${DOC_DIR}/manpage-bold-literal.xsl WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/man COMMENT "Building manpage ${manfile}" VERBATIM) @@ -53,7 +89,7 @@ foreach (manfile IN LISTS MAN_NAMES) add_custom_command(OUTPUT ${htmlfile} COMMAND ${ASCIIDOC_EXECUTABLE} ${ASCIIDOC_HTML_OPTS} -o ${htmlfile} ${docfile} - DEPENDS ${docfile} + DEPENDS ${docfile} ${DOC_DIR}/asciidoc.conf WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/html COMMENT "Building htmlfile ${htmlfile}" VERBATIM) @@ -62,6 +98,10 @@ endforeach () add_custom_target(doc-man ALL DEPENDS ${MAN_FILES}) add_custom_target(doc-html ALL DEPENDS ${HTML_FILES}) +if(TARGET doc-cli) + add_dependencies(doc-man doc-cli) + add_dependencies(doc-html doc-cli) +endif() if (NOT WITH_DOC_MAN) diff --git a/doc/asciidoc.conf b/doc/asciidoc.conf index 906f6a55e..6a6d2d74e 100644 --- a/doc/asciidoc.conf +++ b/doc/asciidoc.conf @@ -24,9 +24,9 @@ template::[header-declarations] {mantitle} {manvolnum} -Shadowsocks-libev +shadowsocks-c {version} -Shadowsocks-libev Manual +shadowsocks-c Manual {manname} diff --git a/doc/shadowsocks-c.asciidoc b/doc/shadowsocks-c.asciidoc index 9ca5d9fd6..c2d184243 100644 --- a/doc/shadowsocks-c.asciidoc +++ b/doc/shadowsocks-c.asciidoc @@ -7,15 +7,25 @@ shadowsocks-c - a lightweight and secure socks5 proxy SYNOPSIS -------- -*ss-local*|*ss-redir*|*ss-server*|*ss-tunnel*|*ss-manager* - [-s ] [-p ] [-l ] [-k ] - [-m ] [-f ] [-t ] [-c ] +// Generated by scripts/gen_cli_docs.py; do not edit this section. + +*ss-local* [options] + +*ss-server* [options] + +*ss-tunnel* [options] + +*ss-redir* [options] + +*ss-manager* [options] + +*ss-nat* [options] DESCRIPTION ----------- *shadowsocks-c* is a lightweight and secure socks5 proxy. It is a port of the original shadowsocks created by clowwindy. -*shadowsocks-c* is written in pure C and takes advantage of *libev* +*shadowsocks-c* is written in pure C and takes advantage of *libuv* to achieve both high performance and low resource consumption. *shadowsocks-c* consists of five components. One is `ss-server`(1) @@ -35,182 +45,25 @@ About the details of this API, please refer to the 'PROTOCOL' section. OPTIONS ------- +// Generated by scripts/gen_cli_docs.py; do not edit this section. + +`ss-local`(1):: +See this command's generated SYNOPSIS and OPTIONS for its accepted arguments. + +`ss-server`(1):: +See this command's generated SYNOPSIS and OPTIONS for its accepted arguments. + +`ss-tunnel`(1):: +See this command's generated SYNOPSIS and OPTIONS for its accepted arguments. + +`ss-redir`(1):: +See this command's generated SYNOPSIS and OPTIONS for its accepted arguments. + +`ss-manager`(1):: +See this command's generated SYNOPSIS and OPTIONS for its accepted arguments. --s :: -Set the server's hostname or IP. - --l :: -Set the local port number. -+ -Not available in server nor manager mode. - --k :: ---password :: -Set the password. The server and the client should use the same password. - ---key :: -Set the key directly. The key should be encoded with URL-safe Base64. -+ -Not available in manager mode. - --m :: -Set the cipher. -+ -*shadowsocks-c* accepts 22 different ciphers: -+ -2022-blake3-aes-128-gcm, 2022-blake3-aes-256-gcm, -2022-blake3-chacha20-poly1305, -aes-128-gcm, aes-192-gcm, aes-256-gcm, -rc4-md5, aes-128-cfb, aes-192-cfb, aes-256-cfb, -aes-128-ctr, aes-192-ctr, aes-256-ctr, bf-cfb, -camellia-128-cfb, camellia-192-cfb, camellia-256-cfb, -chacha20-ietf-poly1305, xchacha20-ietf-poly1305, -salsa20, chacha20 and chacha20-ietf. -+ -The default cipher is 'chacha20-ietf-poly1305'. -+ -The '2022-blake3-*' ciphers implement Shadowsocks 2022 (SIP022) and are -the recommended choice: they provide full replay protection and -session-based UDP relay. They require a base64-encoded pre-shared key of -exactly the cipher's key size (16 bytes for 2022-blake3-aes-128-gcm, 32 -bytes for the others) given with *-k*; passwords are not accepted and are -never stretched into a key. Generate one with `openssl rand -base64 32`. -+ -If built with PolarSSL or custom OpenSSL libraries, some of -these ciphers may not work. - --a :: -Run as a specific user. - --f :: -Start shadowsocks as a daemon with specific pid file. - --t :: -Set the socket timeout in seconds. The default value is 60. - --c :: -Use a configuration file. - --n :: -Specify max number of open files. -+ -Not available in manager mode. -+ -Only available on Linux. - --i :: -Send traffic through specific network interface. -+ -For example, there are three interfaces in your device, which is -lo (127.0.0.1), eth0 (192.168.0.1) and eth1 (192.168.0.2). -Meanwhile, you configure *shadowsocks-libev* to listen on 0.0.0.0:8388 -and bind to eth1. That results the traffic go out through eth1, -but not lo nor eth0. This option is useful to control traffic in -multi-interface environment. -+ -Not available in redir mode. - --b :: -Specify the local address to bind. -+ -For servers: Specify the local address to use while this server is making -outbound connections to remote servers on behalf of the clients. -+ -For clients: Specify the local address to use while this client is making -outbound connections to the server. -+ -Not available in manager mode. - --u:: -Enable UDP relay. -+ -TPROXY is required in redir mode. You may need root permission. - --U:: -Enable UDP relay and disable TCP relay. -+ -Not available in local mode. - --T:: -Use tproxy instead of redirect (for tcp). -+ -Only available in redir mode. - --L :: -Specify destination server address and port for local port forwarding. -+ -Only available in tunnel mode. - --d :: -Setup name servers for internal DNS resolver (libc-ares). -The default server is fetched from /etc/resolv.conf. -+ -Only available in server and manager mode. - ---fast-open:: -Enable TCP fast open. -+ -Not available in redir nor tunnel mode, with Linux kernel > 3.7.0. - ---reuse-port:: -Enable port reuse. -+ -Only available with Linux kernel > 3.9.0. - ---no-delay:: -Enable TCP_NODELAY. - ---tcp-incoming-sndbuf :: -Set TCP send buffer size for incoming connections. -+ -Not available in manager mode. - ---tcp-incoming-rcvbuf :: -Set TCP receive buffer size for incoming connections. -+ -Not available in manager mode. - ---tcp-outgoing-sndbuf :: -Set TCP send buffer size for outgoing connections. -+ -Not available in manager mode. - ---tcp-outgoing-rcvbuf :: -Set TCP receive buffer size for outgoing connections. -+ -Not available in manager mode. - ---acl :: -Enable ACL (Access Control List) and specify config file. -+ -Not available in redir nor tunnel mode. - ---manager-address :: -Specify UNIX domain socket address. -+ -Only available in server and manager mode. - ---executable :: -Specify the executable path of `ss-server`. -+ -Only available in manager mode. - --D :: ---workdir :: -Specify the working directory of ss-manager. -+ -Only available in manager mode. - ---nftables-sets :: -Specify nftables sets for reporting malicious IPs. -+ -Only available in server mode, when built with nftables support. - --v:: -Enable verbose mode. - --h|--help:: -Print help message. +`ss-nat`(1):: +See this command's generated SYNOPSIS and OPTIONS for its accepted arguments. CONFIG FILE ----------- diff --git a/doc/ss-local.asciidoc b/doc/ss-local.asciidoc index 3def3f07a..7bb2233d7 100644 --- a/doc/ss-local.asciidoc +++ b/doc/ss-local.asciidoc @@ -3,35 +3,42 @@ ss-local(1) NAME ---- -ss-local - shadowsocks client as socks5 proxy, libev port +ss-local - shadowsocks client as socks5 proxy, C implementation SYNOPSIS -------- -*ss-local* - [-uv6] [-h|--help] - [-s ] [-p ] [-l ] - [-k ] [-m ] [-f ] - [-t ] [-c ] [-i ] - [-a ] [-b ] [-n ] - [--fast-open] [--reuse-port] [--acl ] - [--mtu ] [--no-delay] - [--plugin ] [--plugin-opts ] - [--password ] [--key ] - [--server-url ] +// Generated by scripts/gen_cli_docs.py from src/local.c; do not edit this section. + +*ss-local* [-f ] [-s ] [-p ] [-l + ] [-k ] [-t ] [-m ] [-i + ] [-c ] [-b ] [-a ] [-n + ] [-S ] [-h] [-u] [-U] [-v] [-V] [-6] [-A] [--reuse-port] + [--tcp-incoming-sndbuf ] [--tcp-incoming-rcvbuf ] + [--tcp-outgoing-sndbuf ] [--tcp-outgoing-rcvbuf ] [--fast-open] + [--no-delay] [--acl ] [--mtu ] [--mptcp] [--plugin + ] [--plugin-opts ] [--password ] + [--key ] [--server-url ] [--help] DESCRIPTION ----------- -*Shadowsocks-libev* is a lightweight and secure socks5 proxy. +*shadowsocks-c* is a lightweight and secure socks5 proxy. It is a port of the original shadowsocks created by clowwindy. -*Shadowsocks-libev* is written in pure C and takes advantage of libev to +*shadowsocks-c* is written in pure C and takes advantage of libuv to achieve both high performance and low resource consumption. -*Shadowsocks-libev* consists of five components. `ss-local`(1) works as a standard +*shadowsocks-c* consists of five components. `ss-local`(1) works as a standard socks5 proxy on local machines to proxy TCP traffic. For more information, check out `shadowsocks-libev`(8). OPTIONS ------- +// Generated by scripts/gen_cli_docs.py from src/local.c; do not edit this section. + +This section lists options across supported builds. Platform and feature +restrictions are noted below; not every option is effective on every platform. + +-f :: +Start shadowsocks as a daemon with specific pid file. -s :: Set the server's hostname or IP. @@ -43,77 +50,49 @@ Set the server's port number. Set the local port number. -k :: ---password :: Set the password. The server and the client should use the same password. ---key :: -Set the key directly. The key should be encoded with URL-safe Base64. - ---server-url :: -Take the server address, port, cipher, password and any SIP003 plugin -from a single 'ss://' URL, as produced by most clients and by -*shadowsocks-rust*'s `ssurl`. Both the SIP002 form -('ss://base64(method:password)@host:port/?plugin=...#tag') and the older -'ss://base64(method:password@host:port)' form are accepted. Options given -later on the command line override the values taken from the URL. +-t :: +Set the socket timeout in seconds. The default value is 60. -m :: -Set the cipher. -+ -*Shadowsocks-libev* accepts 22 different ciphers: +Set the cipher. The default is 'chacha20-ietf-poly1305'. + -2022-blake3-aes-128-gcm, 2022-blake3-aes-256-gcm, -2022-blake3-chacha20-poly1305, -aes-128-gcm, aes-192-gcm, aes-256-gcm, -rc4-md5, aes-128-cfb, aes-192-cfb, aes-256-cfb, -aes-128-ctr, aes-192-ctr, aes-256-ctr, bf-cfb, -camellia-128-cfb, camellia-192-cfb, camellia-256-cfb, -chacha20-ietf-poly1305, xchacha20-ietf-poly1305, -salsa20, chacha20 and chacha20-ietf. +AEAD cipher names from the source (availability depends on the build): +aes-128-gcm, aes-192-gcm, aes-256-gcm, 2022-blake3-aes-128-gcm, 2022-blake3-aes-256-gcm, chacha20-ietf-poly1305, 2022-blake3-chacha20-poly1305, xchacha20-ietf-poly1305. + -The default cipher is 'chacha20-ietf-poly1305'. +Legacy stream cipher names recognized by the source (disabled in minimal builds; +some require backend support): table, rc4, rc4-md5, aes-128-cfb, aes-192-cfb, aes-256-cfb, aes-128-ctr, aes-192-ctr, aes-256-ctr, bf-cfb, camellia-128-cfb, camellia-192-cfb, camellia-256-cfb, cast5-cfb, des-cfb, idea-cfb, rc2-cfb, seed-cfb, salsa20, chacha20, chacha20-ietf. + -The '2022-blake3-*' ciphers implement Shadowsocks 2022 (SIP022) and are -the recommended choice: they provide full replay protection and -session-based UDP relay. They require a base64-encoded pre-shared key of -exactly the cipher's key size (16 bytes for 2022-blake3-aes-128-gcm, 32 -bytes for the others) given with *-k*; passwords are not accepted and are -never stretched into a key. Generate one with `openssl rand -base64 32`. -+ -If built with PolarSSL or custom OpenSSL libraries, some of -these ciphers may not work. - --a :: -Run as a specific user. - --f :: -Start shadowsocks as a daemon with specific pid file. +The '2022-blake3-*' ciphers implement Shadowsocks 2022 (SIP022). They require +a base64-encoded pre-shared key supplied with *-k*: 16 bytes for +2022-blake3-aes-128-gcm and 32 bytes for the other 2022 ciphers. +Generate a 32-byte key with `openssl rand -base64 32`. +Passwords are not stretched into keys for these ciphers. --t :: -Set the socket timeout in seconds. The default value is 60. +-i :: +Send outbound traffic through the specified network interface where supported by the platform. -c :: Use a configuration file. + -Refer to `shadowsocks-libev`(8) 'CONFIG FILE' section for more details. +Refer to `shadowsocks-c`(8) 'CONFIG FILE' section for more details. + +-b :: +Specify the local address to use while this client is making outbound +connections to the server. + +-a :: +Run as a specific user. -n :: -Specify max number of open files. -+ -Only available on Linux. +Specify the maximum number of open files. Requires a platform with setrlimit support. --i :: -Send traffic through specific network interface. -+ -For example, there are three interfaces in your device, -which is lo (127.0.0.1), eth0 (192.168.0.1) and eth1 (192.168.0.2). -Meanwhile, you configure `ss-local` to listen on 0.0.0.0:8388 and bind to eth1. -That results the traffic go out through eth1, but not lo nor eth0. -This option is useful to control traffic in multi-interface environment. +-S :: +Android only: UNIX socket path for traffic statistics. --b :: -Specify the local address to use while this client is making outbound -connections to the server. +-h:: +Print help message. -u:: Enable UDP relay. @@ -121,32 +100,20 @@ Enable UDP relay. -U:: Enable UDP relay and disable TCP relay. +-v:: +Enable verbose mode. + +-V:: +Android only: enable VPN socket protection. + -6:: Resolve hostname to IPv6 address first. ---fast-open:: -Enable TCP fast open. -+ -Only available with Linux kernel > 3.7.0. +-A:: +Deprecated one-time authentication option. Exits with an error; use AEAD ciphers instead. --reuse-port:: -Enable port reuse. -+ -Only available with Linux kernel > 3.9.0. - ---acl :: -Enable ACL (Access Control List) and specify config file. - ---mtu :: -Specify the MTU of your network interface. - ---mptcp:: -Enable Multipath TCP. -+ -Only available with MPTCP enabled Linux kernel. - ---no-delay:: -Enable TCP_NODELAY. +Enable port reuse where supported by the operating system. --tcp-incoming-sndbuf :: Set TCP send buffer size for incoming connections. @@ -160,16 +127,44 @@ Set TCP send buffer size for outgoing connections. --tcp-outgoing-rcvbuf :: Set TCP receive buffer size for outgoing connections. +--fast-open:: +Enable TCP Fast Open where supported by the operating system. + +--no-delay:: +Enable TCP_NODELAY. + +--acl :: +Enable ACL (Access Control List) and specify config file. + +--mtu :: +Specify the MTU of your network interface. + +--mptcp:: +Enable Multipath TCP. ++ +Only available with MPTCP enabled Linux kernel. + --plugin :: Enable SIP003 plugin. (Experimental) --plugin-opts :: Set SIP003 plugin options. (Experimental) --v:: -Enable verbose mode. +--password :: +Set the password. The server and the client should use the same password. + +--key :: +Set the key directly. The key should be encoded with URL-safe Base64. + +--server-url :: +Take the server address, port, cipher, password and any SIP003 plugin +from a single 'ss://' URL, as produced by most clients and by +*shadowsocks-rust*'s `ssurl`. Both the SIP002 form +('ss://base64(method:password)@host:port/?plugin=...#tag') and the older +'ss://base64(method:password@host:port)' form are accepted. Options given +later on the command line override the values taken from the URL. --h|--help:: +--help:: Print help message. EXAMPLE diff --git a/doc/ss-manager.asciidoc b/doc/ss-manager.asciidoc index 81701b1af..15f20b7d6 100644 --- a/doc/ss-manager.asciidoc +++ b/doc/ss-manager.asciidoc @@ -7,25 +7,24 @@ ss-manager - ss-server controller for multi-user management and traffic statisti SYNOPSIS -------- -*ss-manager* - [-AuUv] [-h|--help] - [-s ] [-p ] [-l ] - [-k ] [-m ] [-f ] - [-t ] [-c ] [-i ] - [-b ] [-a ] [-D ] - [--manager-address ] - [--executable ] - [--fast-open] [--reuse-port] - [--plugin ] [--plugin-opts ] +// Generated by scripts/gen_cli_docs.py from src/manager.c; do not edit this section. + +*ss-manager* [-f ] [-s ] [-l ] [-k + ] [-t ] [-m ] [-c ] [-i + ] [-d ] [-a ] [-n ] [-D ] [-6] [-h] + [-u] [-U] [-v] [-A] [--fast-open] [--no-delay] [--reuse-port] [--acl + ] [--manager-address
] [--executable ] [--mtu + ] [--plugin ] [--plugin-opts ] [--password + ] [--workdir ] [--help] DESCRIPTION ----------- -*Shadowsocks-libev* is a lightweight and secure socks5 proxy. +*shadowsocks-c* is a lightweight and secure socks5 proxy. It is a port of the original shadowsocks created by clowwindy. -*Shadowsocks-libev* is written in pure C and takes advantage of libev to +*shadowsocks-c* is written in pure C and takes advantage of libuv to achieve both high performance and low resource consumption. -*Shadowsocks-libev* consists of five components. +*shadowsocks-c* consists of five components. `ss-manager`(1) is a controller for multi-user management and traffic statistics, using UNIX domain socket to talk with `ss-server`(1). Also, it provides a UNIX domain socket or IP based API for other software. @@ -34,103 +33,97 @@ section. OPTIONS ------- +// Generated by scripts/gen_cli_docs.py from src/manager.c; do not edit this section. + +This section lists options across supported builds. Platform and feature +restrictions are noted below; not every option is effective on every platform. + +-f :: +Start shadowsocks as a daemon with specific pid file. + -s :: -Set the server's hostname or IP. +Set a server listening hostname or IP address. May be repeated. + +-l :: +Accepted for compatibility but ignored by this program; it does not configure a local listener. -k :: Set the password. The server and the client should use the same password. +-t :: +Set the socket timeout in seconds. The default value is 60. + -m :: -Set the cipher. -+ -*Shadowsocks-libev* accepts 22 different ciphers: -+ -2022-blake3-aes-128-gcm, 2022-blake3-aes-256-gcm, -2022-blake3-chacha20-poly1305, -aes-128-gcm, aes-192-gcm, aes-256-gcm, -rc4-md5, aes-128-cfb, aes-192-cfb, aes-256-cfb, -aes-128-ctr, aes-192-ctr, aes-256-ctr, bf-cfb, -camellia-128-cfb, camellia-192-cfb, camellia-256-cfb, -chacha20-ietf-poly1305, xchacha20-ietf-poly1305, -salsa20, chacha20 and chacha20-ietf. +Set the cipher. The default is 'chacha20-ietf-poly1305'. + -The default cipher is 'chacha20-ietf-poly1305'. +AEAD cipher names from the source (availability depends on the build): +aes-128-gcm, aes-192-gcm, aes-256-gcm, 2022-blake3-aes-128-gcm, 2022-blake3-aes-256-gcm, chacha20-ietf-poly1305, 2022-blake3-chacha20-poly1305, xchacha20-ietf-poly1305. + -The '2022-blake3-*' ciphers implement Shadowsocks 2022 (SIP022) and are -the recommended choice: they provide full replay protection and -session-based UDP relay. They require a base64-encoded pre-shared key of -exactly the cipher's key size (16 bytes for 2022-blake3-aes-128-gcm, 32 -bytes for the others) given with *-k*; passwords are not accepted and are -never stretched into a key. Generate one with `openssl rand -base64 32`. +Legacy stream cipher names recognized by the source (disabled in minimal builds; +some require backend support): table, rc4, rc4-md5, aes-128-cfb, aes-192-cfb, aes-256-cfb, aes-128-ctr, aes-192-ctr, aes-256-ctr, bf-cfb, camellia-128-cfb, camellia-192-cfb, camellia-256-cfb, cast5-cfb, des-cfb, idea-cfb, rc2-cfb, seed-cfb, salsa20, chacha20, chacha20-ietf. + -If built with PolarSSL or custom OpenSSL libraries, some of -these ciphers may not work. +The '2022-blake3-*' ciphers implement Shadowsocks 2022 (SIP022). They require +a base64-encoded pre-shared key supplied with *-k*: 16 bytes for +2022-blake3-aes-128-gcm and 32 bytes for the other 2022 ciphers. +Generate a 32-byte key with `openssl rand -base64 32`. +Passwords are not stretched into keys for these ciphers. + +-c :: +Use a JSON configuration file. The "port_password" field can start multiple ss-server instances. + +-i :: +Send outbound traffic through the specified network interface where supported by the platform. + +-d :: +Configure name servers for the internal c-ares DNS resolver. By default it uses the system resolver configuration. -a :: Run as a specific user. --f :: -Start shadowsocks as a daemon with specific pid file. +-n :: +Specify the maximum number of open files. Requires a platform with setrlimit support. --t :: -Set the socket timeout in seconds. The default value is 60. +-D :: +Set the working directory of ss-manager. --c :: -Use a configuration file. -+ -You may use "port_password" field inside this configuration file to bring up -multiple ss-server instances together. +-6:: +Resolve hostname to IPv6 address first. --i :: -Send traffic through specific network interface. -+ -For example, there are three interfaces in your device, -which is lo (127.0.0.1), eth0 (192.168.0.1) and eth1 (192.168.0.2). -Meanwhile, you configure `ss-local` to listen on 0.0.0.0:8388 and bind to eth1. -That results the traffic go out through eth1, but not lo nor eth0. -This option is useful to control traffic in multi-interface environment. +-h:: +Print help message. -u:: - Enable UDP relay. +Enable UDP relay. -U:: Enable UDP relay and disable TCP relay. --A:: -Enable onetime authentication. +-v:: +Enable verbose mode. --d :: -Setup name servers for internal DNS resolver (libc-ares). -The default server is fetched from `/etc/resolv.conf`. +-A:: +Deprecated one-time authentication option. Exits with an error; use AEAD ciphers instead. --fast-open:: -Enable TCP fast open. -+ -Only available with Linux kernel > 3.7.0. +Enable TCP Fast Open where supported by the operating system. + +--no-delay:: +Enable TCP_NODELAY. --reuse-port:: -Enable port reuse. -+ -Only available with Linux kernel > 3.9.0. +Enable port reuse where supported by the operating system. --acl :: Enable ACL (Access Control List) and specify config file. ---manager-address :: -Specify UNIX domain socket address for the communication between ss-manager(1) and ss-server(1). -+ -Only available in server and manager mode. +--manager-address
:: +Set the manager control address: a UNIX domain socket path or an IP address and port. ---executable :: -Specify the executable path of ss-server. -+ -Only available in manager mode. +--executable :: +Set the executable path of ss-server used by ss-manager. --D :: ---workdir :: -Specify the working directory of ss-manager. -+ -Only available in manager mode. +--mtu :: +Specify the MTU of your network interface. --plugin :: Enable SIP003 plugin. (Experimental) @@ -138,10 +131,13 @@ Enable SIP003 plugin. (Experimental) --plugin-opts :: Set SIP003 plugin options. (Experimental) --v:: -Enable verbose mode. +--password :: +Set the password. The server and the client should use the same password. + +--workdir :: +Set the working directory of ss-manager (alias for *-D*). --h|--help:: +--help:: Print help message. PROTOCOL diff --git a/doc/ss-nat.asciidoc b/doc/ss-nat.asciidoc index 355083a5e..9c4e43a5a 100644 --- a/doc/ss-nat.asciidoc +++ b/doc/ss-nat.asciidoc @@ -7,17 +7,17 @@ ss-nat - helper script to setup NAT rules for transparent proxy SYNOPSIS -------- -*ss-nat* - [-ouUfh] - [-s ] [-S ] [-l ] - [-L ] [-i ] [-a ] - [-b ] [-w ] [-e ] +// Generated by scripts/gen_cli_docs.py from src/ss-nat; do not edit this section. + +*ss-nat* [-s ] [-l ] [-S ] [-L ] + [-i ] [-I ] [-e ] [-a ] [-b + ] [-w ] [-o] [-u] [-U] [-f] [-h] DESCRIPTION ----------- -*Shadowsocks-libev* is a lightweight and secure socks5 proxy. +*shadowsocks-c* is a lightweight and secure socks5 proxy. It is a port of the original shadowsocks created by clowwindy. -*Shadowsocks-libev* is written in pure C and takes advantage of libev to +*shadowsocks-c* is written in pure C and takes advantage of libuv to achieve both high performance and low resource consumption. `ss-nat`(1) sets up NAT rules for `ss-redir`(1) to provide traffic redirection. @@ -27,6 +27,11 @@ For more information, check out `shadowsocks-libev`(8) and the following OPTIONS ------- +// Generated by scripts/gen_cli_docs.py from src/ss-nat; do not edit this section. + +This section lists options across supported builds. Platform and feature +restrictions are noted below; not every option is effective on every platform. + -s :: IP address of shadowsocks remote server @@ -42,6 +47,12 @@ Port number of shadowsocks local UDP server -i :: a file whose content is bypassed ip list +-I :: +Set the LAN interface for NAT rules. The default is eth0. + +-e :: +Extra options for iptables + -a :: LAN IP of access control, need a prefix to define access control mode @@ -51,9 +62,6 @@ WAN IP of will be bypassed -w :: WAN IP of will be forwarded --e :: -Extra options for iptables - -o:: Apply the rules to the OUTPUT chain diff --git a/doc/ss-redir.asciidoc b/doc/ss-redir.asciidoc index aa40c4675..ce0c408d5 100644 --- a/doc/ss-redir.asciidoc +++ b/doc/ss-redir.asciidoc @@ -3,27 +3,29 @@ ss-redir(1) NAME ---- -ss-redir - shadowsocks client as transparent proxy, libev port +ss-redir - shadowsocks client as transparent proxy, C implementation SYNOPSIS -------- -*ss-redir* - [-uUv6] [-h|--help] - [-s ] [-p ] [-l ] - [-k ] [-m ] [-f ] - [-t ] [-c ] [-b ] - [-a ] [-n ] [--mtu ] [--no-delay] - [--plugin ] [--plugin-opts ] - [--password ] [--key ] +// Generated by scripts/gen_cli_docs.py from src/redir.c; do not edit this section. + +*ss-redir* [-f ] [-s ] [-p ] [-l + ] [-k ] [-t ] [-m ] [-c + ] [-b ] [-a ] [-n ] [-h] [-u] + [-U] [-T] [-v] [-6] [-A] [--fast-open] [--mtu ] [--mptcp] [--plugin + ] [--plugin-opts ] [--reuse-port] + [--tcp-incoming-sndbuf ] [--tcp-incoming-rcvbuf ] + [--tcp-outgoing-sndbuf ] [--tcp-outgoing-rcvbuf ] [--no-delay] + [--password ] [--key ] [--help] DESCRIPTION ----------- -*Shadowsocks-libev* is a lightweight and secure socks5 proxy. +*shadowsocks-c* is a lightweight and secure socks5 proxy. It is a port of the original shadowsocks created by clowwindy. -*Shadowsocks-libev* is written in pure C and takes advantage of libev to +*shadowsocks-c* is written in pure C and takes advantage of libuv to achieve both high performance and low resource consumption. -*Shadowsocks-libev* consists of five components. +*shadowsocks-c* consists of five components. `ss-redir`(1) works as a transparent proxy on local machines to proxy TCP traffic and requires netfilter's NAT module. For more information, check out `shadowsocks-libev`(8) and the following @@ -31,6 +33,14 @@ For more information, check out `shadowsocks-libev`(8) and the following OPTIONS ------- +// Generated by scripts/gen_cli_docs.py from src/redir.c; do not edit this section. + +This section lists options across supported builds. Platform and feature +restrictions are noted below; not every option is effective on every platform. + +-f :: +Start shadowsocks as a daemon with specific pid file. + -s :: Set the server's hostname or IP. @@ -41,75 +51,65 @@ Set the server's port number. Set the local port number. -k :: ---password :: Set the password. The server and the client should use the same password. ---key :: -Set the key directly. The key should be encoded with URL-safe Base64. +-t :: +Set the socket timeout in seconds. The default value is 60. -m :: -Set the cipher. -+ -*Shadowsocks-libev* accepts 22 different ciphers: +Set the cipher. The default is 'chacha20-ietf-poly1305'. + -2022-blake3-aes-128-gcm, 2022-blake3-aes-256-gcm, -2022-blake3-chacha20-poly1305, -aes-128-gcm, aes-192-gcm, aes-256-gcm, -rc4-md5, aes-128-cfb, aes-192-cfb, aes-256-cfb, -aes-128-ctr, aes-192-ctr, aes-256-ctr, bf-cfb, -camellia-128-cfb, camellia-192-cfb, camellia-256-cfb, -chacha20-ietf-poly1305, xchacha20-ietf-poly1305, -salsa20, chacha20 and chacha20-ietf. +AEAD cipher names from the source (availability depends on the build): +aes-128-gcm, aes-192-gcm, aes-256-gcm, 2022-blake3-aes-128-gcm, 2022-blake3-aes-256-gcm, chacha20-ietf-poly1305, 2022-blake3-chacha20-poly1305, xchacha20-ietf-poly1305. + -The default cipher is 'chacha20-ietf-poly1305'. +Legacy stream cipher names recognized by the source (disabled in minimal builds; +some require backend support): table, rc4, rc4-md5, aes-128-cfb, aes-192-cfb, aes-256-cfb, aes-128-ctr, aes-192-ctr, aes-256-ctr, bf-cfb, camellia-128-cfb, camellia-192-cfb, camellia-256-cfb, cast5-cfb, des-cfb, idea-cfb, rc2-cfb, seed-cfb, salsa20, chacha20, chacha20-ietf. + -The '2022-blake3-*' ciphers implement Shadowsocks 2022 (SIP022) and are -the recommended choice: they provide full replay protection and -session-based UDP relay. They require a base64-encoded pre-shared key of -exactly the cipher's key size (16 bytes for 2022-blake3-aes-128-gcm, 32 -bytes for the others) given with *-k*; passwords are not accepted and are -never stretched into a key. Generate one with `openssl rand -base64 32`. -+ -If built with PolarSSL or custom OpenSSL libraries, some of -these ciphers may not work. - --a :: -Run as a specific user. - --f :: -Start shadowsocks as a daemon with specific pid file. - --t :: -Set the socket timeout in seconds. The default value is 60. +The '2022-blake3-*' ciphers implement Shadowsocks 2022 (SIP022). They require +a base64-encoded pre-shared key supplied with *-k*: 16 bytes for +2022-blake3-aes-128-gcm and 32 bytes for the other 2022 ciphers. +Generate a 32-byte key with `openssl rand -base64 32`. +Passwords are not stretched into keys for these ciphers. -c :: Use a configuration file. + -Refer to `shadowsocks-libev`(8) 'CONFIG FILE' section for more details. - --n :: -Specify max number of open files. -+ -Only available on Linux. +Refer to `shadowsocks-c`(8) 'CONFIG FILE' section for more details. -b :: -Specify the local address to use while this client is making outbound +Specify the local address to use while this client is making outbound connections to the server. +-a :: +Run as a specific user. + +-n :: +Specify the maximum number of open files. Requires a platform with setrlimit support. + +-h:: +Print help message. + -u:: -Enable UDP relay. -+ -TPROXY is required in redir mode. You may need root permission. +Enable UDP relay. Requires Linux TPROXY support and permission to configure transparent proxying. -U:: Enable UDP relay and disable TCP relay. -T:: -Use tproxy instead of redirect. (for tcp) +Use TPROXY instead of REDIRECT for TCP traffic. Requires Linux TPROXY support. + +-v:: +Enable verbose mode. -6:: Resolve hostname to IPv6 address first. +-A:: +Deprecated one-time authentication option. Exits with an error; use AEAD ciphers instead. + +--fast-open:: +Enable TCP Fast Open where supported by the operating system. + --mtu :: Specify the MTU of your network interface. @@ -118,13 +118,14 @@ Enable Multipath TCP. + Only available with MPTCP enabled Linux kernel. ---reuse-port:: -Enable port reuse. -+ -Only available with Linux kernel > 3.9.0. +--plugin :: +Enable SIP003 plugin. (Experimental) ---no-delay:: -Enable TCP_NODELAY. +--plugin-opts :: +Set SIP003 plugin options. (Experimental) + +--reuse-port:: +Enable port reuse where supported by the operating system. --tcp-incoming-sndbuf :: Set TCP send buffer size for incoming connections. @@ -138,16 +139,16 @@ Set TCP send buffer size for outgoing connections. --tcp-outgoing-rcvbuf :: Set TCP receive buffer size for outgoing connections. ---plugin :: -Enable SIP003 plugin. (Experimental) +--no-delay:: +Enable TCP_NODELAY. ---plugin-opts :: -Set SIP003 plugin options. (Experimental) +--password :: +Set the password. The server and the client should use the same password. --v:: -Enable verbose mode. +--key :: +Set the key directly. The key should be encoded with URL-safe Base64. --h|--help:: +--help:: Print help message. EXAMPLE diff --git a/doc/ss-server.asciidoc b/doc/ss-server.asciidoc index 2df14be1e..b6c001fa5 100644 --- a/doc/ss-server.asciidoc +++ b/doc/ss-server.asciidoc @@ -3,103 +3,95 @@ ss-server(1) NAME ---- -ss-server - shadowsocks server, libev port +ss-server - shadowsocks server, C implementation SYNOPSIS -------- -*ss-server* - [-uUv] [-h|--help] - [-s ] [-p ] [-l ] - [-k ] [-m ] [-f ] - [-t ] [-c ] [-i ] - [-a ] [-d ] [-n ] - [-b ] [--fast-open] [--reuse-port] - [--mptcp] [--acl ] [--mtu ] [--no-delay] - [--manager-address ] - [--plugin ] [--plugin-opts ] - [--password ] [--key ] +// Generated by scripts/gen_cli_docs.py from src/server.c; do not edit this section. + +*ss-server* [-f ] [-s ] [-p ] [-l + ] [-k ] [-t ] [-m ] [-b + ] [-c ] [-i ] [-d ] [-a + ] [-n ] [-h] [-u] [-U] [-v] [-6] [-A] [--fast-open] + [--reuse-port] [--tcp-incoming-sndbuf ] [--tcp-incoming-rcvbuf ] + [--tcp-outgoing-sndbuf ] [--tcp-outgoing-rcvbuf ] [--no-delay] + [--acl ] [--manager-address
] [--mtu ] [--help] + [--plugin ] [--plugin-opts ] [--password + ] [--key ] [--mptcp] [--nftables-sets ] DESCRIPTION ----------- -*Shadowsocks-libev* is a lightweight and secure socks5 proxy. +*shadowsocks-c* is a lightweight and secure socks5 proxy. It is a port of the original shadowsocks created by clowwindy. -*Shadowsocks-libev* is written in pure C and takes advantage of libev to +*shadowsocks-c* is written in pure C and takes advantage of libuv to achieve both high performance and low resource consumption. -*Shadowsocks-libev* consists of five components. +*shadowsocks-c* consists of five components. `ss-server`(1) runs on a remote server to provide secured tunnel service. For more information, check out `shadowsocks-libev`(8). OPTIONS ------- +// Generated by scripts/gen_cli_docs.py from src/server.c; do not edit this section. + +This section lists options across supported builds. Platform and feature +restrictions are noted below; not every option is effective on every platform. + +-f :: +Start shadowsocks as a daemon with specific pid file. + -s :: -Set the server's hostname or IP. +Set a server listening hostname or IP address. May be repeated. -p :: -Set the server's port number. +Set the server listening port. + +-l :: +Accepted for compatibility but ignored by this program; it does not configure a local listener. -k :: ---password :: Set the password. The server and the client should use the same password. ---key :: -Set the key directly. The key should be encoded with URL-safe Base64. +-t :: +Set the socket timeout in seconds. The default value is 60. -m :: -Set the cipher. +Set the cipher. The default is 'chacha20-ietf-poly1305'. + -*Shadowsocks-libev* accepts 22 different ciphers: +AEAD cipher names from the source (availability depends on the build): +aes-128-gcm, aes-192-gcm, aes-256-gcm, 2022-blake3-aes-128-gcm, 2022-blake3-aes-256-gcm, chacha20-ietf-poly1305, 2022-blake3-chacha20-poly1305, xchacha20-ietf-poly1305. + -2022-blake3-aes-128-gcm, 2022-blake3-aes-256-gcm, -2022-blake3-chacha20-poly1305, -aes-128-gcm, aes-192-gcm, aes-256-gcm, -rc4-md5, aes-128-cfb, aes-192-cfb, aes-256-cfb, -aes-128-ctr, aes-192-ctr, aes-256-ctr, bf-cfb, -camellia-128-cfb, camellia-192-cfb, camellia-256-cfb, -chacha20-ietf-poly1305, xchacha20-ietf-poly1305, -salsa20, chacha20 and chacha20-ietf. +Legacy stream cipher names recognized by the source (disabled in minimal builds; +some require backend support): table, rc4, rc4-md5, aes-128-cfb, aes-192-cfb, aes-256-cfb, aes-128-ctr, aes-192-ctr, aes-256-ctr, bf-cfb, camellia-128-cfb, camellia-192-cfb, camellia-256-cfb, cast5-cfb, des-cfb, idea-cfb, rc2-cfb, seed-cfb, salsa20, chacha20, chacha20-ietf. + -The '2022-blake3-*' ciphers implement Shadowsocks 2022 (SIP022) and are -the recommended choice: they provide full replay protection and -session-based UDP relay. They require a base64-encoded pre-shared key of -exactly the cipher's key size (16 bytes for 2022-blake3-aes-128-gcm, 32 -bytes for the others) given with *-k*; passwords are not accepted and are -never stretched into a key. Generate one with `openssl rand -base64 32`. -+ -If built with PolarSSL or custom OpenSSL libraries, some of -these ciphers may not work. - --a :: -Run as a specific user. +The '2022-blake3-*' ciphers implement Shadowsocks 2022 (SIP022). They require +a base64-encoded pre-shared key supplied with *-k*: 16 bytes for +2022-blake3-aes-128-gcm and 32 bytes for the other 2022 ciphers. +Generate a 32-byte key with `openssl rand -base64 32`. +Passwords are not stretched into keys for these ciphers. --f :: -Start shadowsocks as a daemon with specific pid file. - --t :: -Set the socket timeout in seconds. The default value is 60. +-b :: +Set the local address for outbound connections to destination servers. -c :: Use a configuration file. + -Refer to `shadowsocks-libev`(8) 'CONFIG FILE' section for more details. - --n :: -Specify max number of open files. -+ -Only available on Linux. +Refer to `shadowsocks-c`(8) 'CONFIG FILE' section for more details. -i :: -Send traffic through specific network interface. -+ -For example, there are three interfaces in your device, -which is lo (127.0.0.1), eth0 (192.168.0.1) and eth1 (192.168.0.2). -Meanwhile, you configure `ss-server` to listen on 0.0.0.0:8388 and bind to eth1. -That results the traffic go out through eth1, but not lo nor eth0. -This option is useful to control traffic in multi-interface environment. +Send outbound traffic through the specified network interface where supported by the platform. --b :: -Specify the local address to use while this server is making outbound -connections to remote servers on behalf of the clients. +-d :: +Configure name servers for the internal c-ares DNS resolver. By default it uses the system resolver configuration. + +-a :: +Run as a specific user. + +-n :: +Specify the maximum number of open files. Requires a platform with setrlimit support. + +-h:: +Print help message. -u:: Enable UDP relay. @@ -107,25 +99,20 @@ Enable UDP relay. -U:: Enable UDP relay and disable TCP relay. +-v:: +Enable verbose mode. + -6:: Resolve hostname to IPv6 address first. --d :: -Setup name servers for internal DNS resolver (libc-ares). -The default server is fetched from '/etc/resolv.conf'. +-A:: +Deprecated one-time authentication option. Exits with an error; use AEAD ciphers instead. --fast-open:: -Enable TCP fast open. -+ -Only available with Linux kernel > 3.7.0. +Enable TCP Fast Open where supported by the operating system. --reuse-port:: -Enable port reuse. -+ -Only available with Linux kernel > 3.9.0. - ---no-delay:: -Enable TCP_NODELAY. +Enable port reuse where supported by the operating system. --tcp-incoming-sndbuf :: Set TCP send buffer size for incoming connections. @@ -139,21 +126,20 @@ Set TCP send buffer size for outgoing connections. --tcp-outgoing-rcvbuf :: Set TCP receive buffer size for outgoing connections. +--no-delay:: +Enable TCP_NODELAY. + --acl :: Enable ACL (Access Control List) and specify config file. ---manager-address :: -Specify UNIX domain socket address for the communication between ss-manager(1) and ss-server(1). -+ -Only available in server and manager mode. +--manager-address
:: +Set the manager control address: a UNIX domain socket path or an IP address and port. --mtu :: Specify the MTU of your network interface. ---mptcp:: -Enable Multipath TCP. -+ -Only available with MPTCP enabled Linux kernel. +--help:: +Print help message. --plugin :: Enable SIP003 plugin. (Experimental) @@ -161,11 +147,19 @@ Enable SIP003 plugin. (Experimental) --plugin-opts :: Set SIP003 plugin options. (Experimental) --v:: -Enable verbose mode. +--password :: +Set the password. The server and the client should use the same password. --h|--help:: -Print help message. +--key :: +Set the key directly. The key should be encoded with URL-safe Base64. + +--mptcp:: +Enable Multipath TCP. ++ +Only available with MPTCP enabled Linux kernel. + +--nftables-sets :: +Linux builds with USE_NFTABLES only: add malicious IP addresses to nftables sets. Format: `[:][,[:]...]`. EXAMPLE ------- diff --git a/doc/ss-tunnel.asciidoc b/doc/ss-tunnel.asciidoc index 40df87afc..485a68e29 100644 --- a/doc/ss-tunnel.asciidoc +++ b/doc/ss-tunnel.asciidoc @@ -3,34 +3,44 @@ ss-tunnel(1) NAME ---- -ss-tunnel - shadowsocks tools for local port forwarding, libev port +ss-tunnel - shadowsocks tools for local port forwarding, C implementation SYNOPSIS -------- -*ss-tunnel* - [-uUv6] [-h|--help] - [-s ] [-p ] [-l ] - [-k ] [-m ] [-f ] - [-t ] [-c ] [-i ] - [-b ] [-a ] [-n ] - [-L addr:port] [--mtu ] [--mptcp] [--reuse-port] [--no-delay] - [--plugin ] [--plugin-opts ] - [--key ] +// Generated by scripts/gen_cli_docs.py from src/tunnel.c; do not edit this section. + +*ss-tunnel* [-f ] [-s ] [-p ] [-l + ] [-k ] [-t ] [-m ] [-i + ] [-c ] [-b ] [-L ] [-a + ] [-n ] [-h] [-u] [-U] [-v] [-V] [-6] [-A] [--fast-open] + [--mtu ] [--no-delay] [--mptcp] [--plugin ] [--plugin-opts + ] [--reuse-port] [--tcp-incoming-sndbuf ] + [--tcp-incoming-rcvbuf ] [--tcp-outgoing-sndbuf ] + [--tcp-outgoing-rcvbuf ] [--password ] [--key + ] [--help] DESCRIPTION ----------- -*Shadowsocks-libev* is a lightweight and secure socks5 proxy. +*shadowsocks-c* is a lightweight and secure socks5 proxy. It is a port of the original shadowsocks created by clowwindy. -*Shadowsocks-libev* is written in pure C and takes advantage of libev to +*shadowsocks-c* is written in pure C and takes advantage of libuv to achieve both high performance and low resource consumption. -*Shadowsocks-libev* consists of five components. +*shadowsocks-c* consists of five components. `ss-tunnel`(1) is a tool for local port forwarding. See 'OPTIONS' section for special option needed by `ss-tunnel`(1). For more information, check out `shadowsocks-libev`(8). OPTIONS ------- +// Generated by scripts/gen_cli_docs.py from src/tunnel.c; do not edit this section. + +This section lists options across supported builds. Platform and feature +restrictions are noted below; not every option is effective on every platform. + +-f :: +Start shadowsocks as a daemon with specific pid file. + -s :: Set the server's hostname or IP. @@ -41,99 +51,90 @@ Set the server's port number. Set the local port number. -k :: ---password :: Set the password. The server and the client should use the same password. ---key :: -Set the key directly. The key should be encoded with URL-safe Base64. +-t :: +Set the socket timeout in seconds. The default value is 60. -m :: -Set the cipher. -+ -*Shadowsocks-libev* accepts 22 different ciphers: +Set the cipher. The default is 'chacha20-ietf-poly1305'. + -2022-blake3-aes-128-gcm, 2022-blake3-aes-256-gcm, -2022-blake3-chacha20-poly1305, -aes-128-gcm, aes-192-gcm, aes-256-gcm, -rc4-md5, aes-128-cfb, aes-192-cfb, aes-256-cfb, -aes-128-ctr, aes-192-ctr, aes-256-ctr, bf-cfb, -camellia-128-cfb, camellia-192-cfb, camellia-256-cfb, -chacha20-ietf-poly1305, xchacha20-ietf-poly1305, -salsa20, chacha20 and chacha20-ietf. +AEAD cipher names from the source (availability depends on the build): +aes-128-gcm, aes-192-gcm, aes-256-gcm, 2022-blake3-aes-128-gcm, 2022-blake3-aes-256-gcm, chacha20-ietf-poly1305, 2022-blake3-chacha20-poly1305, xchacha20-ietf-poly1305. + -The default cipher is 'chacha20-ietf-poly1305'. +Legacy stream cipher names recognized by the source (disabled in minimal builds; +some require backend support): table, rc4, rc4-md5, aes-128-cfb, aes-192-cfb, aes-256-cfb, aes-128-ctr, aes-192-ctr, aes-256-ctr, bf-cfb, camellia-128-cfb, camellia-192-cfb, camellia-256-cfb, cast5-cfb, des-cfb, idea-cfb, rc2-cfb, seed-cfb, salsa20, chacha20, chacha20-ietf. + -The '2022-blake3-*' ciphers implement Shadowsocks 2022 (SIP022) and are -the recommended choice: they provide full replay protection and -session-based UDP relay. They require a base64-encoded pre-shared key of -exactly the cipher's key size (16 bytes for 2022-blake3-aes-128-gcm, 32 -bytes for the others) given with *-k*; passwords are not accepted and are -never stretched into a key. Generate one with `openssl rand -base64 32`. -+ -If built with PolarSSL or custom OpenSSL libraries, some of -these ciphers may not work. - --a :: -Run as a specific user. - --f :: -Start shadowsocks as a daemon with specific pid file. +The '2022-blake3-*' ciphers implement Shadowsocks 2022 (SIP022). They require +a base64-encoded pre-shared key supplied with *-k*: 16 bytes for +2022-blake3-aes-128-gcm and 32 bytes for the other 2022 ciphers. +Generate a 32-byte key with `openssl rand -base64 32`. +Passwords are not stretched into keys for these ciphers. --t :: -Set the socket timeout in seconds. The default value is 60. +-i :: +Send outbound traffic through the specified network interface where supported by the platform. -c :: Use a configuration file. + -Refer to `shadowsocks-libev`(8) 'CONFIG FILE' section for more details. - --n :: -Specify max number of open files. -+ -Only available on Linux. - --i :: -Send traffic through specific network interface. -+ -For example, there are three interfaces in your device, -which is lo (127.0.0.1), eth0 (192.168.0.1) and eth1 (192.168.0.2). -Meanwhile, you configure `ss-tunnel` to listen on 0.0.0.0:8388 and bind to eth1. -That results the traffic go out through eth1, but not lo nor eth0. -This option is useful to control traffic in multi-interface environment. +Refer to `shadowsocks-c`(8) 'CONFIG FILE' section for more details. -b :: -Specify the local address to use while this client is making outbound +Specify the local address to use while this client is making outbound connections to the server. +-L :: +Destination server address and port for local port forwarding. + +-a :: +Run as a specific user. + +-n :: +Specify the maximum number of open files. Requires a platform with setrlimit support. + +-h:: +Print help message. + -u:: Enable UDP relay. -U:: Enable UDP relay and disable TCP relay. +-v:: +Enable verbose mode. + +-V:: +Android only: enable VPN socket protection. + -6:: Resolve hostname to IPv6 address first. --L :: -Specify destination server address and port for local port forwarding. -+ -Only used and available in tunnel mode. +-A:: +Deprecated one-time authentication option. Exits with an error; use AEAD ciphers instead. + +--fast-open:: +Enable TCP Fast Open where supported by the operating system. --mtu :: Specify the MTU of your network interface. +--no-delay:: +Enable TCP_NODELAY. + --mptcp:: Enable Multipath TCP. + Only available with MPTCP enabled Linux kernel. ---reuse-port:: -Enable port reuse. -+ -Only available with Linux kernel > 3.9.0. +--plugin :: +Enable SIP003 plugin. (Experimental) ---no-delay:: -Enable TCP_NODELAY. +--plugin-opts :: +Set SIP003 plugin options. (Experimental) + +--reuse-port:: +Enable port reuse where supported by the operating system. --tcp-incoming-sndbuf :: Set TCP send buffer size for incoming connections. @@ -147,16 +148,13 @@ Set TCP send buffer size for outgoing connections. --tcp-outgoing-rcvbuf :: Set TCP receive buffer size for outgoing connections. ---plugin :: -Enable SIP003 plugin. (Experimental) - ---plugin-opts :: -Set SIP003 plugin options. (Experimental) +--password :: +Set the password. The server and the client should use the same password. --v:: -Enable verbose mode. +--key :: +Set the key directly. The key should be encoded with URL-safe Base64. --h|--help:: +--help:: Print help message. EXAMPLE diff --git a/scripts/gen_cli_docs.py b/scripts/gen_cli_docs.py new file mode 100644 index 000000000..732200327 --- /dev/null +++ b/scripts/gen_cli_docs.py @@ -0,0 +1,194 @@ +#!/usr/bin/env python3 +"""Generate man-page CLI sections from getopt declarations and CLI_DOC comments. + +This intentionally parses a small, checked subset of C, not arbitrary expressions. +Unsupported parser declarations fail instead of silently dropping options. Source +is read without preprocessing so cross-builds document all platform variants. +""" + +import argparse +from pathlib import Path +import re +import sys +import textwrap + + +ROOT = Path(__file__).resolve().parents[1] +PROGRAMS = ("local", "server", "tunnel", "redir", "manager", "nat") +C_COMMENT = re.compile(r'/\*.*?\*/|//[^\n]*', re.S) +TERM = re.compile(r'(-{1,2}[A-Za-z0-9][A-Za-z0-9-]*)(?: (<[^>]+>))?::') + + +def descriptions(source): + blocks = re.findall(r'/\* CLI_DOC\n(.*?)\*/', source, re.S) + for block in re.findall(r'^# CLI_DOC\n(.*?)^# END_CLI_DOC$', source, re.M | re.S): + blocks.append(re.sub(r'^# ?', '', block, flags=re.M)) + result = {} + for block in blocks: + for entry in re.split(r'\n\s*\n(?=-)', block.strip()): + term, separator, body = entry.partition('\n') + match = TERM.fullmatch(term) + if not match or not separator or not body.strip(): + raise ValueError(f"Invalid CLI_DOC entry: {term}") + flag, argument = match.groups() + if flag in result: + raise ValueError(f"Duplicate CLI_DOC entry: {flag}") + result[flag] = (argument, body.strip()) + return result + + +def short_options(spec): + result = {} + spec = spec.lstrip(':+-') + while spec: + match = re.match(r'([A-Za-z0-9])(:?)', spec) + if not match: + raise ValueError(f"Unsupported getopt string: {spec}") + flag, argument = match.groups() + if '-' + flag in result: + raise ValueError(f"Duplicate short option: {flag}") + result['-' + flag] = bool(argument) + spec = spec[match.end():] + return result + + +def options(source, shell=False): + result = {} + if shell: + specs = re.findall(r'^while getopts "([^"]+)" \w+; do$', source, re.M) + if len(specs) != 1: + raise ValueError("Expected one literal shell getopts declaration") + else: + source = C_COMMENT.sub('', source) + specs = re.findall(r'getopt_long\(argc,\s*argv,\s*"([^"]+)"\s*,\s*long_options,\s*NULL\)', source) + if not specs or len(specs) != len(re.findall(r'\bgetopt_long\s*\(', source)): + raise ValueError("Expected literal getopt_long declarations") + for spec in specs: + for flag, argument in short_options(spec).items(): + if flag in result and result[flag] != argument: + raise ValueError(f"Inconsistent argument across platform variants: {flag}") + result[flag] = argument + if shell: + return result + tables = re.findall(r'static struct option long_options\[\]\s*=\s*\{(.*?)\};', source, re.S) + if len(tables) != 1: + raise ValueError("Expected one long_options table") + table = re.sub(r'^\s*#.*$', '', tables[0], flags=re.M) + entry = re.compile(r'\{\s*"([a-z0-9-]+)"\s*,\s*(no_argument|required_argument)\s*,\s*NULL\s*,\s*GETOPT_VAL_[A-Z0-9_]+\s*\}\s*,', re.S) + for match in entry.finditer(table): + flag, argument = match.groups() + if '--' + flag in result: + raise ValueError(f"Duplicate long option: {flag}") + result['--' + flag] = argument == 'required_argument' + remainder = entry.sub('', table) + if not re.fullmatch(r'\s*\{\s*NULL\s*,\s*0\s*,\s*NULL\s*,\s*0\s*\}\s*', remainder): + raise ValueError(f"Unsupported long_options entry: {remainder.strip()}") + return result + + +def cipher_names(root, kind): + source = C_COMMENT.sub('', (root / f'src/{kind}.c').read_text()) + match = re.search(r'const char \*supported_' + kind + r'_ciphers\[[^]]+\]\s*=\s*\{(.*?)\};', source, re.S) + if not match: + raise ValueError(f"Missing {kind} cipher table") + table = re.sub(r'^\s*#.*$', '', match[1], flags=re.M) + names = re.findall(r'"([a-z0-9-]+)"', table) + if not names or re.sub(r'"[a-z0-9-]+"|[\s,]', '', table): + raise ValueError(f"Unsupported {kind} cipher table") + return ', '.join(names) + + +def sections(program, declared, docs): + missing = declared.keys() - docs.keys() + if missing: + raise ValueError(f"{program}: missing CLI_DOC descriptions: {', '.join(sorted(missing))}") + synopsis = [f'*{program}*'] + entries = [] + for flag, takes_argument in declared.items(): + argument, body = docs[flag] + if bool(argument) != takes_argument: + raise ValueError(f"{program}: argument mismatch for {flag}") + term = flag + (' ' + argument if argument else '') + synopsis.append(f'[{term}]') + entries.append(f'{term}::\n{body}') + return (textwrap.fill(' '.join(synopsis), width=78, subsequent_indent=' ', + break_long_words=False, break_on_hyphens=False), + '\n\n'.join(entries)) + + +def replace_section(document, heading, body): + pattern = re.compile(r'(^' + heading + r'\n-+\n).*?(?=^[A-Z][A-Z /-]*\n-+\n|\Z)', re.M | re.S) + document, count = pattern.subn(lambda m: m[1] + body + '\n\n', document) + if count != 1: + raise ValueError(f"Expected one {heading} section") + return document + + +def generate(root): + shared = descriptions((root / 'src/utils.c').read_text()) + replacements = {f'{{cli-{kind}-ciphers}}': cipher_names(root, kind) + for kind in ('aead', 'stream')} + result = {} + all_options = set() + summary = [] + for module in PROGRAMS: + program = 'ss-' + module + path = 'src/ss-nat' if module == 'nat' else f'src/{module}.c' + source = (root / path).read_text() + declared = options(source, shell=module == 'nat') + local = descriptions(source) + if local.keys() - declared.keys(): + raise ValueError(f"{program}: stale CLI_DOC descriptions: {local.keys() - declared.keys()}") + docs = local if module == 'nat' else {**shared, **local} + if module != 'nat': + all_options.update(declared) + synopsis, body = sections(program, declared, docs) + note = (f'// Generated by scripts/gen_cli_docs.py from {path}; do not edit this section.\n\n') + document = (root / f'doc/{program}.asciidoc').read_text() + document = replace_section(document, 'SYNOPSIS', note + synopsis) + body = ('This section lists options across supported builds. Platform and feature\n' + 'restrictions are noted below; not every option is effective on every platform.\n\n' + body) + document = replace_section(document, 'OPTIONS', note + body) + for key, value in replacements.items(): + document = document.replace(key, value) + result[f'{program}.asciidoc'] = document + summary.append(f'`{program}`(1)::\nSee this command\'s generated SYNOPSIS and OPTIONS for its accepted arguments.') + if shared.keys() - all_options: + raise ValueError(f"Stale shared CLI_DOC descriptions: {shared.keys() - all_options}") + document = (root / 'doc/shadowsocks-c.asciidoc').read_text() + note = '// Generated by scripts/gen_cli_docs.py; do not edit this section.\n\n' + document = replace_section(document, 'SYNOPSIS', note + '\n\n'.join(f'*ss-{p}* [options]' for p in PROGRAMS)) + document = replace_section(document, 'OPTIONS', note + '\n\n'.join(summary)) + result['shadowsocks-c.asciidoc'] = document + return result + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--root', type=Path, default=ROOT) + mode = parser.add_mutually_exclusive_group() + mode.add_argument('--check', action='store_true', help='fail if checked-in pages are stale') + mode.add_argument('--output-dir', type=Path, help='write assembled pages here instead of doc/') + args = parser.parse_args() + try: + generated = generate(args.root) + if args.check: + stale = [name for name, text in generated.items() + if (args.root / 'doc' / name).read_text() != text] + if stale: + raise ValueError('Stale CLI docs: ' + ', '.join(stale) + + '; run python3 scripts/gen_cli_docs.py') + else: + output = args.output_dir or args.root / 'doc' + output.mkdir(parents=True, exist_ok=True) + for name, text in generated.items(): + path = output / name + if not path.exists() or path.read_text() != text: + path.write_text(text) + except (ValueError, OSError) as error: + parser.exit(1, f'{error}\n') + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/src/manager.c b/src/manager.c index 162c8162c..019e89240 100644 --- a/src/manager.c +++ b/src/manager.c @@ -1271,6 +1271,16 @@ main(int argc, char **argv) jconf_t *conf = NULL; + /* CLI_DOC +-s :: +Set a server listening hostname or IP address. May be repeated. + +-l :: +Accepted for compatibility but ignored by this program; it does not configure a local listener. + +-c :: +Use a JSON configuration file. The "port_password" field can start multiple ss-server instances. + */ static struct option long_options[] = { { "fast-open", no_argument, NULL, GETOPT_VAL_FAST_OPEN }, { "no-delay", no_argument, NULL, GETOPT_VAL_NODELAY }, diff --git a/src/redir.c b/src/redir.c index 6b4989035..4d37514e0 100644 --- a/src/redir.c +++ b/src/redir.c @@ -922,6 +922,10 @@ main(int argc, char **argv) memset(remote_addr, 0, sizeof(ss_addr_t) * MAX_REMOTE_NUM); + /* CLI_DOC +-u:: +Enable UDP relay. Requires Linux TPROXY support and permission to configure transparent proxying. + */ static struct option long_options[] = { { "fast-open", no_argument, NULL, GETOPT_VAL_FAST_OPEN }, { "mtu", required_argument, NULL, GETOPT_VAL_MTU }, diff --git a/src/server.c b/src/server.c index 57d85a8a5..40a26399c 100644 --- a/src/server.c +++ b/src/server.c @@ -1838,6 +1838,19 @@ main(int argc, char **argv) memset(&local_addr_v4, 0, sizeof(struct sockaddr_storage)); memset(&local_addr_v6, 0, sizeof(struct sockaddr_storage)); + /* CLI_DOC +-s :: +Set a server listening hostname or IP address. May be repeated. + +-l :: +Accepted for compatibility but ignored by this program; it does not configure a local listener. + +-p :: +Set the server listening port. + +-b :: +Set the local address for outbound connections to destination servers. + */ static struct option long_options[] = { { "fast-open", no_argument, NULL, GETOPT_VAL_FAST_OPEN }, { "reuse-port", no_argument, NULL, GETOPT_VAL_REUSE_PORT }, diff --git a/src/ss-nat b/src/ss-nat index 2aa3980eb..3894fcc2a 100755 --- a/src/ss-nat +++ b/src/ss-nat @@ -168,6 +168,52 @@ EOF return $? } +# CLI_DOC +# -s :: +# IP address of shadowsocks remote server +# +# -l :: +# Port number of shadowsocks local server +# +# -S :: +# IP address of shadowsocks remote UDP server +# +# -L :: +# Port number of shadowsocks local UDP server +# +# -i :: +# a file whose content is bypassed ip list +# +# -a :: +# LAN IP of access control, need a prefix to define access control mode +# +# -b :: +# WAN IP of will be bypassed +# +# -w :: +# WAN IP of will be forwarded +# +# -e :: +# Extra options for iptables +# +# -o:: +# Apply the rules to the OUTPUT chain +# +# -u:: +# Enable udprelay mode, TPROXY is required +# +# -U:: +# Enable udprelay mode, using different IP and ports for TCP and UDP +# +# -f:: +# Flush the rules +# +# -h:: +# Show this help message and exit +# +# -I :: +# Set the LAN interface for NAT rules. The default is eth0. +# END_CLI_DOC while getopts ":s:l:S:L:i:I:e:a:b:w:ouUfh" arg; do case "$arg" in s) diff --git a/src/utils.c b/src/utils.c index 5d22ff619..d208a7a22 100644 --- a/src/utils.c +++ b/src/utils.c @@ -317,6 +317,162 @@ ss_is_ipv6addr(const char *addr) return strcmp(addr, ":") > 0; } +/* CLI_DOC +-s :: +Set the server's hostname or IP. + +-p :: +Set the server's port number. + +-l :: +Set the local port number. + +-k :: +Set the password. The server and the client should use the same password. + +--password :: +Set the password. The server and the client should use the same password. + +--key :: +Set the key directly. The key should be encoded with URL-safe Base64. + +--server-url :: +Take the server address, port, cipher, password and any SIP003 plugin +from a single 'ss://' URL, as produced by most clients and by +*shadowsocks-rust*'s `ssurl`. Both the SIP002 form +('ss://base64(method:password)@host:port/?plugin=...#tag') and the older +'ss://base64(method:password@host:port)' form are accepted. Options given +later on the command line override the values taken from the URL. + +-m :: +Set the cipher. The default is 'chacha20-ietf-poly1305'. ++ +AEAD cipher names from the source (availability depends on the build): +{cli-aead-ciphers}. ++ +Legacy stream cipher names recognized by the source (disabled in minimal builds; +some require backend support): {cli-stream-ciphers}. ++ +The '2022-blake3-*' ciphers implement Shadowsocks 2022 (SIP022). They require +a base64-encoded pre-shared key supplied with *-k*: 16 bytes for +2022-blake3-aes-128-gcm and 32 bytes for the other 2022 ciphers. +Generate a 32-byte key with `openssl rand -base64 32`. +Passwords are not stretched into keys for these ciphers. + +-a :: +Run as a specific user. + +-f :: +Start shadowsocks as a daemon with specific pid file. + +-t :: +Set the socket timeout in seconds. The default value is 60. + +-c :: +Use a configuration file. ++ +Refer to `shadowsocks-c`(8) 'CONFIG FILE' section for more details. + +-n :: +Specify the maximum number of open files. Requires a platform with setrlimit support. + +-i :: +Send outbound traffic through the specified network interface where supported by the platform. + +-b :: +Specify the local address to use while this client is making outbound +connections to the server. + +-u:: +Enable UDP relay. + +-U:: +Enable UDP relay and disable TCP relay. + +-6:: +Resolve hostname to IPv6 address first. + +--fast-open:: +Enable TCP Fast Open where supported by the operating system. + +--reuse-port:: +Enable port reuse where supported by the operating system. + +--acl :: +Enable ACL (Access Control List) and specify config file. + +--mtu :: +Specify the MTU of your network interface. + +--mptcp:: +Enable Multipath TCP. ++ +Only available with MPTCP enabled Linux kernel. + +--no-delay:: +Enable TCP_NODELAY. + +--tcp-incoming-sndbuf :: +Set TCP send buffer size for incoming connections. + +--tcp-incoming-rcvbuf :: +Set TCP receive buffer size for incoming connections. + +--tcp-outgoing-sndbuf :: +Set TCP send buffer size for outgoing connections. + +--tcp-outgoing-rcvbuf :: +Set TCP receive buffer size for outgoing connections. + +--plugin :: +Enable SIP003 plugin. (Experimental) + +--plugin-opts :: +Set SIP003 plugin options. (Experimental) + +-v:: +Enable verbose mode. + +-h:: +Print help message. + +--help:: +Print help message. + +-A:: +Deprecated one-time authentication option. Exits with an error; use AEAD ciphers instead. + +-S :: +Android only: UNIX socket path for traffic statistics. + +-V:: +Android only: enable VPN socket protection. + +-L :: +Destination server address and port for local port forwarding. + +-T:: +Use TPROXY instead of REDIRECT for TCP traffic. Requires Linux TPROXY support. + +-d :: +Configure name servers for the internal c-ares DNS resolver. By default it uses the system resolver configuration. + +-D :: +Set the working directory of ss-manager. + +--workdir :: +Set the working directory of ss-manager (alias for *-D*). + +--manager-address
:: +Set the manager control address: a UNIX domain socket path or an IP address and port. + +--executable :: +Set the executable path of ss-server used by ss-manager. + +--nftables-sets :: +Linux builds with USE_NFTABLES only: add malicious IP addresses to nftables sets. Format: `[:][,[:]...]`. +*/ + void usage() { diff --git a/tests/test_gen_cli_docs.py b/tests/test_gen_cli_docs.py new file mode 100644 index 000000000..0572633ca --- /dev/null +++ b/tests/test_gen_cli_docs.py @@ -0,0 +1,107 @@ +"""Regression checks for parser/documentation drift and cross-platform extraction.""" + +import importlib.util +from pathlib import Path +import shutil +import subprocess +import sys +import tempfile +import unittest + + +ROOT = Path(__file__).resolve().parents[1] +SPEC = importlib.util.spec_from_file_location('gen_cli_docs', ROOT / 'scripts/gen_cli_docs.py') +GEN = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(GEN) + + +class CliDocsTests(unittest.TestCase): + def setUp(self): + self.temp = tempfile.TemporaryDirectory() + self.addCleanup(self.temp.cleanup) + self.root = Path(self.temp.name) + for directory in ('src', 'doc'): + (self.root / directory).mkdir() + for name in ('utils.c', 'local.c', 'server.c', 'tunnel.c', 'redir.c', + 'manager.c', 'ss-nat', 'aead.c', 'stream.c'): + shutil.copyfile(ROOT / 'src' / name, self.root / 'src' / name) + for path in (ROOT / 'doc').glob('*.asciidoc'): + shutil.copyfile(path, self.root / 'doc' / path.name) + + def change(self, filename, old, new): + path = self.root / filename + source = path.read_text() + self.assertIn(old, source) + path.write_text(source.replace(old, new)) + + def test_checked_in_pages_and_idempotence(self): + generated = GEN.generate(self.root) + self.assertEqual(len(generated), 7) + for name, text in generated.items(): + self.assertEqual(text, (self.root / 'doc' / name).read_text()) + (self.root / 'doc' / name).write_text(text) + self.assertEqual(generated, GEN.generate(self.root)) + + def test_platform_options_aliases_and_program_specific_flags(self): + pages = GEN.generate(self.root) + self.assertIn('-S ::\nAndroid only', pages['ss-local.asciidoc']) + self.assertIn('-V::\nAndroid only', pages['ss-tunnel.asciidoc']) + self.assertIn('--nftables-sets ::\nLinux builds', pages['ss-server.asciidoc']) + self.assertIn('-I ::', pages['ss-nat.asciidoc']) + self.assertIn('--password ::', pages['ss-tunnel.asciidoc']) + self.assertIn('--workdir ::', pages['ss-manager.asciidoc']) + self.assertNotIn('[-p ', pages['ss-manager.asciidoc']) + self.assertNotIn('--tcp-incoming-sndbuf', pages['ss-manager.asciidoc']) + self.assertIn('-A::\nDeprecated', pages['ss-manager.asciidoc']) + + def test_new_option_requires_description(self): + self.change('src/local.c', '"reuse-port",', '"new-option",') + with self.assertRaisesRegex(ValueError, 'missing CLI_DOC.*new-option'): + GEN.generate(self.root) + + def test_argument_arity_is_checked(self): + self.change('src/local.c', '"reuse-port", no_argument', + '"reuse-port", required_argument') + with self.assertRaisesRegex(ValueError, 'argument mismatch.*reuse-port'): + GEN.generate(self.root) + + def test_unknown_table_syntax_fails_closed(self): + self.change('src/local.c', '"reuse-port", no_argument', + '"reuse-port", optional_argument') + with self.assertRaisesRegex(ValueError, 'Unsupported long_options'): + GEN.generate(self.root) + + def test_nonliteral_short_options_fail_closed(self): + self.change('src/local.c', '"f:s:p:l:k:t:m:i:c:b:a:n:huUv6A"', 'SHORT_OPTIONS') + with self.assertRaisesRegex(ValueError, 'literal getopt_long'): + GEN.generate(self.root) + + def test_shell_arity_and_new_flags(self): + self.change('src/ss-nat', ':s:l:S:L:i:I:e:a:b:w:ouUfh', ':s:l:S:L:i:I:e:a:b:w:ouUfhz') + with self.assertRaisesRegex(ValueError, 'missing CLI_DOC.*-z'): + GEN.generate(self.root) + + def test_stale_override_is_rejected(self): + self.change('src/server.c', '-p ::', '--unused ::') + with self.assertRaisesRegex(ValueError, 'stale CLI_DOC'): + GEN.generate(self.root) + + def test_cipher_table_changes_propagate(self): + self.change('src/aead.c', '"aes-128-gcm",', '"new-aead-cipher",') + self.assertIn('new-aead-cipher', GEN.generate(self.root)['ss-local.asciidoc']) + + def test_check_detects_stale_docs_and_build_output_preserves_source(self): + self.change('doc/ss-local.asciidoc', '--mtu ::', '--mtu ::') + command = [sys.executable, str(ROOT / 'scripts/gen_cli_docs.py'), + '--root', str(self.root)] + checked = subprocess.run(command + ['--check'], capture_output=True, text=True) + self.assertNotEqual(checked.returncode, 0) + self.assertIn('Stale CLI docs', checked.stderr) + output = self.root / 'generated' + subprocess.run(command + ['--output-dir', str(output)], check=True) + self.assertIn('--mtu ::', (output / 'ss-local.asciidoc').read_text()) + self.assertIn('--mtu ::', (self.root / 'doc/ss-local.asciidoc').read_text()) + + +if __name__ == '__main__': + unittest.main()