diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 453054152..f2a25a4f4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -43,7 +43,7 @@ jobs: sudo apt-get update sudo apt-get install -y libpcre2-dev libmbedtls-dev libsodium-dev libuv1-dev libc-ares-dev sudo apt-get install -y --no-install-recommends \ - build-essential cmake debhelper dpkg-dev fakeroot asciidoc-base xmlto pkg-config + build-essential cmake debhelper dpkg-dev fakeroot doxygen pkg-config - name: Debian package build test run: bash tests/test_deb_build.sh diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 000000000..00b217f5c --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,63 @@ +name: documentation + +on: + pull_request: + branches: [master] + push: + branches: [master] + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: documentation-${{ github.ref }} + cancel-in-progress: false + +jobs: + docs: + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@v6 + - name: Install Doxygen + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends doxygen + - name: Build and validate documentation + run: | + python3 scripts/check_cli_docs.py + python3 -m unittest discover -s tests -p test_cli_docs.py + cmake -S . -B build-docs -DWITH_DOC_MAN=ON -DWITH_DOC_HTML=ON + cmake --build build-docs --target doc-man doc-html --parallel 2 + python3 scripts/check_cli_docs.py --rendered build-docs + test -s build-docs/html/index.html + - name: Package downloadable man pages + run: | + tar -czf build-docs/html/man-pages.tar.gz -C build-docs \ + man/ss-local.1 man/ss-server.1 man/ss-tunnel.1 man/ss-redir.1 \ + man/ss-manager.1 man/ss-nat.1 man/shadowsocks-c.8 man/shadowsocks-libev.8 + tar -tzf build-docs/html/man-pages.tar.gz + - name: Upload GitHub Pages site + if: github.repository == 'shadowsocks/shadowsocks-c' && github.ref == 'refs/heads/master' && github.event_name != 'pull_request' + uses: actions/upload-pages-artifact@v5 + with: + path: build-docs/html + + deploy: + needs: docs + if: github.repository == 'shadowsocks/shadowsocks-c' && github.ref == 'refs/heads/master' && github.event_name != 'pull_request' + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + pages: write + id-token: write + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Configure GitHub Pages + uses: actions/configure-pages@v6 + - name: Deploy documentation + id: deployment + uses: actions/deploy-pages@v5 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 49513d8f0..fb6fdfaec 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -26,27 +26,10 @@ 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 + - name: Check Doxygen 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 + python3 scripts/check_cli_docs.py + python3 -m unittest discover -s tests -p test_cli_docs.py tests: strategy: diff --git a/CONTRIBUTION.md b/CONTRIBUTION.md index 4481d2149..4f39d86d8 100644 --- a/CONTRIBUTION.md +++ b/CONTRIBUTION.md @@ -75,35 +75,39 @@ 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: +Doxygen 1.9.4 or newer renders HTML and man pages directly from native source +snippets. Each CLI parser has a `cli-options` snippet containing Doxygen +`\snippet{doc}` references to its option descriptions. Shared descriptions live +in `src/utils.c`; command-specific descriptions live beside the parser. The +`ss-nat` script keeps its snippets in a quoted no-op heredoc so documentation +cannot execute shell substitutions. Cipher tables are included directly with +Doxygen code snippets. + +When adding or changing an option, update its parser, source snippet, and the +parser's `cli-options` list. Keep argument names and platform restrictions in the +source description. Edit narrative sections and examples in `doc/*.md`. There +are no generated documentation files to commit. + +Check that documented flags and argument arity match every platform variant: ```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 +python3 scripts/check_cli_docs.py +python3 -m unittest discover -s tests -p test_cli_docs.py ``` -To render the manuals, install Python 3, AsciiDoc, and xmlto, then run: +Install Doxygen, then render both formats: ```sh cmake -S . -B build-docs -DWITH_DOC_MAN=ON -DWITH_DOC_HTML=ON cmake --build build-docs --target doc-man doc-html --parallel +python3 scripts/check_cli_docs.py --rendered build-docs ``` -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. +Open `build-docs/html/index.html` for the CLI reference. Man pages are written to +`build-docs/man/`, retaining the six command names and the `shadowsocks-c(8)` and +`shadowsocks-libev(8)` overview lookups. Builds read source snippets without +executing target binaries, including when cross-compiling. Python is needed only +for validation; Doxygen alone renders the documentation. ## Pull requests @@ -114,3 +118,13 @@ requests and exclude generated build output, credentials, and local configuratio Respond to review feedback and keep the branch current with `master`. Maintainers will review the implementation and relevant CI results before merging. + +## Published documentation + +The `documentation` workflow builds and validates Doxygen output on pull requests. +After a push to `master`, it publishes `build-docs/html` through GitHub Pages using +GitHub Actions, including a downloadable `man-pages.tar.gz` archive. You can also +run the workflow manually on `master` to redeploy. +Deployment is limited to the canonical repository's `master` branch; pull requests +and forks only validate the documentation. The `github-pages` environment records +the deployed site URL and deployment history. diff --git a/README.md b/README.md index 0233bcb79..457f61f8e 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,9 @@ created by [@clowwindy](https://github.com/clowwindy), and maintained by Current version: 3.3.6 | [Changelog](debian/changelog) +[CLI reference and configuration guide](https://shadowsocks.github.io/shadowsocks-c/) +are generated from the source with Doxygen and published after updates to `master`. + ## Community See the [contribution guide](CONTRIBUTION.md) for development setup, testing, @@ -145,7 +148,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 by integration tests and optional documentation generation. +Python is used by tests; optional documentation builds require Doxygen 1.9.4+. ```sh cmake -S . -B build -DCMAKE_BUILD_TYPE=Release @@ -157,9 +160,8 @@ 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. +Doxygen renders man pages and HTML documentation from CLI source comments. See [the documentation workflow](CONTRIBUTION.md#cli-and-manual-documentation) +for validation 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 @@ -180,7 +182,7 @@ Use `-DCMAKE_PREFIX_PATH=/opt/homebrew/opt/mbedtls@3` when needed on macOS. | `SS_BUILD_SHARED_LIBRARY` | `ON` | Shared embedding library | | `SS_MINIMAL` | `OFF` | Disable regex, plugins, manager, and legacy stream ciphers | | `SS_ENABLE_REGEX` / `SS_ENABLE_PLUGINS` / `SS_ENABLE_LEGACY` | `ON` | Individual compatibility features | -| `WITH_DOC_MAN` / `WITH_DOC_HTML` | `OFF` | Generate documentation (requires asciidoc; man pages also need xmlto) | +| `WITH_DOC_MAN` / `WITH_DOC_HTML` | `OFF` | Generate documentation (requires Doxygen 1.9.4+) | | `SS_INSTALL_TOOLS` | `OFF` | Install platform shell helpers | | `ENABLE_SANITIZERS` | `OFF` | AddressSanitizer and UndefinedBehaviorSanitizer | | `ENABLE_CONNMARKTOS` / `ENABLE_NFTABLES` | `OFF` | Optional Linux firewall integrations | @@ -273,7 +275,7 @@ sudo cmake --install build Distribution packagers can install `libpcre2-dev libuv1-dev libc-ares-dev libmbedtls-dev libsodium-dev` and select `-DSS_DEPENDENCY_MODE=system --DWITH_STATIC=OFF`. Documentation additionally needs asciidoc and xmlto. +-DWITH_STATIC=OFF`. Documentation additionally needs Doxygen 1.9.4 or newer. ### FreeBSD #### Install diff --git a/README_pt_BR.md b/README_pt_BR.md index 790abb55d..d2c1713ba 100644 --- a/README_pt_BR.md +++ b/README_pt_BR.md @@ -173,7 +173,7 @@ Se você estiver usando o CentOS 7, precisará instalar estes pré-requisitos pa ```bash yum install epel-release -y -yum install gcc gettext autoconf libtool automake make pcre-devel asciidoc xmlto c-ares-devel libev-devel libsodium-devel mbedtls-devel -y +yum install gcc gettext autoconf libtool automake make pcre-devel doxygen c-ares-devel libev-devel libsodium-devel mbedtls-devel -y ``` ### Archlinux & Manjaro @@ -208,8 +208,7 @@ Em geral, você precisa das seguintes dependências de compilação: * libpcre3 (antiga biblioteca pcre) * libev * libc-ares -* asciidoc (somente para documentação) -* xmlto (apenas para documentação) +* Doxygen 1.9.4+ (somente para documentação) Notas: Fedora 26 libsodium versão >= 1.0.12, então você pode instalar via dnf install libsodium em vez de compilar a partir da fonte. @@ -222,11 +221,11 @@ Para algumas das distribuições, você pode instalar dependências de compilaç ```bash # Instalação de dependências básicas de compilação ## Debian / Ubuntu -sudo apt-get install --no-install-recommends gettext build-essential autoconf libtool libpcre3-dev asciidoc xmlto libev-dev libc-ares-dev automake libmbedtls-dev libsodium-dev pkg-config +sudo apt-get install --no-install-recommends gettext build-essential autoconf libtool libpcre3-dev doxygen libev-dev libc-ares-dev automake libmbedtls-dev libsodium-dev pkg-config ## CentOS / Fedora / RHEL -sudo yum install gettext gcc autoconf libtool automake make asciidoc xmlto c-ares-devel libev-devel +sudo yum install gettext gcc autoconf libtool automake make doxygen c-ares-devel libev-devel ## Arch -sudo pacman -S gettext gcc autoconf libtool automake make asciidoc xmlto c-ares libev +sudo pacman -S gettext gcc autoconf libtool automake make doxygen c-ares libev # Instalação do libsodium export LIBSODIUM_VER=1.0.16 diff --git a/debian/control b/debian/control index afccd13e7..c8ffa8cfc 100644 --- a/debian/control +++ b/debian/control @@ -8,15 +8,14 @@ Uploaders: Roger Shimizu Build-Depends: cmake (>= 3.20), - asciidoc-base | asciidoc, + doxygen (>= 1.9.4), debhelper (>= 10), libc-ares-dev, libuv1-dev, libmbedtls-dev, libpcre2-dev, libsodium-dev (>= 1.0.12), - pkg-config, - xmlto + pkg-config Standards-Version: 4.1.1 Rules-Requires-Root: no Homepage: https://www.shadowsocks.org diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index f7f06c54b..6b3d0dceb 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -1,131 +1,85 @@ -find_program(XMLTO_EXECUTABLE NAMES xmlto) -find_program(ASCIIDOC_EXECUTABLE NAMES asciidoc asciidoc.py) - -option(WITH_DOC_MAN "Build manpage documentation" OFF) -option(WITH_DOC_HTML "Build HTML documentation" OFF) -if((WITH_DOC_MAN OR WITH_DOC_HTML) AND NOT ASCIIDOC_EXECUTABLE) - message(FATAL_ERROR "Documentation generation requires asciidoc") -endif() -if(WITH_DOC_MAN AND NOT XMLTO_EXECUTABLE) - message(FATAL_ERROR "Manpage generation requires xmlto") -endif() - -# 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) -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") +option(WITH_DOC_MAN "Build manpage documentation with Doxygen" OFF) +option(WITH_DOC_HTML "Build HTML documentation with Doxygen" OFF) + +find_package(Doxygen 1.9.4 QUIET) +if(NOT DOXYGEN_FOUND) + if(WITH_DOC_MAN OR WITH_DOC_HTML) + message(FATAL_ERROR "Documentation generation requires Doxygen 1.9.4 or newer") + endif() + return() 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) +set(DOC_NAMES ss-local ss-server ss-tunnel ss-redir ss-manager ss-nat shadowsocks-c) +set(DOC_INPUTS) +foreach(name IN LISTS DOC_NAMES) + list(APPEND DOC_INPUTS "${CMAKE_CURRENT_SOURCE_DIR}/${name}.md") +endforeach() +set(DOC_SOURCES) +foreach(name local server tunnel redir manager utils aead stream) + list(APPEND DOC_SOURCES "${PROJECT_SOURCE_DIR}/src/${name}.c") +endforeach() +list(APPEND DOC_SOURCES "${PROJECT_SOURCE_DIR}/src/ss-nat") + +# Doxygen reads native documentation snippets directly from source. No target +# binary or Python/AsciiDoc conversion step is needed, including in cross builds. +foreach(format html man1 man8) + set(DOXYGEN_HTML NO) + set(DOXYGEN_MAN NO) + set(DOXYGEN_MAN_EXTENSION .1) + set(format_inputs ${DOC_INPUTS}) + set(outputs) + if(format STREQUAL "html") + set(DOXYGEN_HTML YES) + list(APPEND format_inputs "${CMAKE_CURRENT_SOURCE_DIR}/index.md") + list(APPEND outputs "${PROJECT_BINARY_DIR}/html/index.html") + foreach(name IN LISTS DOC_NAMES) + list(APPEND outputs "${PROJECT_BINARY_DIR}/html/${name}.html") + endforeach() + elseif(format STREQUAL "man1") + set(DOXYGEN_MAN YES) + list(REMOVE_ITEM format_inputs "${CMAKE_CURRENT_SOURCE_DIR}/shadowsocks-c.md") + foreach(name ss-local ss-server ss-tunnel ss-redir ss-manager ss-nat) + list(APPEND outputs "${PROJECT_BINARY_DIR}/man/${name}.1") + endforeach() + else() + set(DOXYGEN_MAN YES) + set(DOXYGEN_MAN_EXTENSION .8) + set(format_inputs "${CMAKE_CURRENT_SOURCE_DIR}/shadowsocks-c.md") + list(APPEND outputs "${PROJECT_BINARY_DIR}/man/shadowsocks-c.8") + endif() + set(DOXYGEN_INPUT) + foreach(path IN LISTS format_inputs) + string(APPEND DOXYGEN_INPUT " \"${path}\"") 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" + set(config "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile-${format}") + configure_file(Doxyfile.in "${config}" @ONLY) + set(stamp "${CMAKE_CURRENT_BINARY_DIR}/${format}.stamp") + add_custom_command(OUTPUT "${stamp}" ${outputs} + COMMAND "${DOXYGEN_EXECUTABLE}" "${config}" + COMMAND ${CMAKE_COMMAND} -E touch "${stamp}" + DEPENDS "${config}" ${format_inputs} ${DOC_SOURCES} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + COMMENT "Generating ${format} documentation with Doxygen" 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}) - + set(DOC_${format}_OUTPUTS "${stamp}" ${outputs}) +endforeach() -set(MAN_NAMES ss-local.1 ss-manager.1 ss-nat.1 ss-redir.1 ss-server.1 ss-tunnel.1 shadowsocks-c.8) -set(MAN_FILES) -set(HTML_FILES) - -foreach (manfile IN LISTS MAN_NAMES) - string(REGEX REPLACE \\.. .xml xmlfile ${manfile}) - string(REGEX REPLACE \\.. .asciidoc docfile ${manfile}) - string(REGEX REPLACE \\.. .html htmlfile ${manfile}) - - set(manfile ${CMAKE_MANPAGE_OUTPUT_DIRECTORY}/${manfile}) - set(htmlfile ${CMAKE_HTML_OUTPUT_DIRECTORY}/${htmlfile}) - 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} ${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) - list(APPEND MAN_FILES ${manfile}) - - add_custom_command(OUTPUT ${htmlfile} - COMMAND ${ASCIIDOC_EXECUTABLE} ${ASCIIDOC_HTML_OPTS} -o ${htmlfile} ${docfile} - DEPENDS ${docfile} ${DOC_DIR}/asciidoc.conf - WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/html - COMMENT "Building htmlfile ${htmlfile}" - VERBATIM) - list(APPEND HTML_FILES ${htmlfile}) -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) +add_custom_target(doc-man ALL DEPENDS ${DOC_man1_OUTPUTS} ${DOC_man8_OUTPUTS}) +add_custom_target(doc-html ALL DEPENDS ${DOC_html_OUTPUTS}) +if(NOT WITH_DOC_MAN) set_target_properties(doc-man PROPERTIES EXCLUDE_FROM_ALL TRUE) -else () - install(DIRECTORY ${PROJECT_BINARY_DIR}/man/ - DESTINATION share/man/man1 - FILES_MATCHING PATTERN "*.1" - ) - install(DIRECTORY ${PROJECT_BINARY_DIR}/man/ - DESTINATION share/man/man8 - FILES_MATCHING PATTERN "*.8" - ) -endif () -if (NOT WITH_DOC_HTML) +else() + foreach(name ss-local ss-server ss-tunnel ss-redir ss-manager ss-nat) + install(FILES "${PROJECT_BINARY_DIR}/man/${name}.1" DESTINATION share/man/man1) + endforeach() + install(FILES "${PROJECT_BINARY_DIR}/man/shadowsocks-c.8" + "${PROJECT_BINARY_DIR}/man/shadowsocks-libev.8" DESTINATION share/man/man8) +endif() +if(NOT WITH_DOC_HTML) set_target_properties(doc-html PROPERTIES EXCLUDE_FROM_ALL TRUE) -else () - install(DIRECTORY ${PROJECT_BINARY_DIR}/html/ - DESTINATION share/doc/${PROJECT_NAME}) -endif () - -# This is required for custom command -file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/man) -file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/html) +else() + install(DIRECTORY "${PROJECT_BINARY_DIR}/html/" DESTINATION share/doc/${PROJECT_NAME}) +endif() -# Retain the former manual lookup name. +file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/man") file(WRITE "${PROJECT_BINARY_DIR}/man/shadowsocks-libev.8" ".so man8/shadowsocks-c.8\n") diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in new file mode 100644 index 000000000..0f4802f87 --- /dev/null +++ b/doc/Doxyfile.in @@ -0,0 +1,26 @@ +PROJECT_NAME = "shadowsocks-c" +PROJECT_NUMBER = "@PROJECT_VERSION@" +OUTPUT_DIRECTORY = "@PROJECT_BINARY_DIR@" +INPUT = @DOXYGEN_INPUT@ +INPUT_ENCODING = UTF-8 +EXAMPLE_PATH = "@PROJECT_SOURCE_DIR@/src" +EXAMPLE_PATTERNS = *.c ss-nat +RECURSIVE = NO +MARKDOWN_SUPPORT = YES +AUTOLINK_SUPPORT = NO +EXTRACT_ALL = NO +QUIET = YES +WARNINGS = YES +WARN_IF_UNDOCUMENTED = NO +WARN_AS_ERROR = YES +GENERATE_HTML = @DOXYGEN_HTML@ +HTML_OUTPUT = html +GENERATE_TREEVIEW = YES +GENERATE_MAN = @DOXYGEN_MAN@ +MAN_OUTPUT = man +MAN_SUBDIR = . +MAN_EXTENSION = @DOXYGEN_MAN_EXTENSION@ +MAN_LINKS = NO +GENERATE_LATEX = NO +HAVE_DOT = NO +STRIP_CODE_COMMENTS = YES diff --git a/doc/asciidoc.conf b/doc/asciidoc.conf deleted file mode 100644 index 6a6d2d74e..000000000 --- a/doc/asciidoc.conf +++ /dev/null @@ -1,36 +0,0 @@ -[tags] -bracket-emphasis={1?[{1}]}<|> - -[quotes] -<|>=#bracket-emphasis - -[attributes] -asterisk=* -plus=+ -caret=^ -startsb=[ -endsb=] -backslash=\ -tilde=~ -apostrophe=' -backtick=` -litdd=-- - -ifdef::doctype-manpage[] -ifdef::backend-docbook[] -[header] -template::[header-declarations] - - -{mantitle} -{manvolnum} -shadowsocks-c -{version} -shadowsocks-c Manual - - - {manname} - {manpurpose} - -endif::backend-docbook[] -endif::doctype-manpage[] diff --git a/doc/index.md b/doc/index.md new file mode 100644 index 000000000..9beb7a335 --- /dev/null +++ b/doc/index.md @@ -0,0 +1,16 @@ +\mainpage shadowsocks-c CLI reference + +Command-line reference and configuration guide, generated by Doxygen from the +source documentation. + +- \ref ss-local "ss-local: SOCKS5 client" +- \ref ss-server "ss-server: proxy server" +- \ref ss-tunnel "ss-tunnel: local port forwarding" +- \ref ss-redir "ss-redir: transparent proxy" +- \ref ss-manager "ss-manager: multi-user management" +- \ref ss-nat "ss-nat: NAT setup helper" +- \ref shadowsocks-c "Configuration, examples, and management protocol" + +[Download the generated man pages](https://shadowsocks.github.io/shadowsocks-c/man-pages.tar.gz) +from the published site. The archive includes the six CLI manuals and both +overview lookup names. diff --git a/doc/manpage-base.xsl b/doc/manpage-base.xsl deleted file mode 100644 index a264fa616..000000000 --- a/doc/manpage-base.xsl +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - sp - - - - - - - - br - - - diff --git a/doc/manpage-bold-literal.xsl b/doc/manpage-bold-literal.xsl deleted file mode 100644 index 608eb5df6..000000000 --- a/doc/manpage-bold-literal.xsl +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - fB - - - fR - - - diff --git a/doc/manpage-normal.xsl b/doc/manpage-normal.xsl deleted file mode 100644 index a48f5b11f..000000000 --- a/doc/manpage-normal.xsl +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - -\ -. - - diff --git a/doc/shadowsocks-c.asciidoc b/doc/shadowsocks-c.asciidoc deleted file mode 100644 index c2d184243..000000000 --- a/doc/shadowsocks-c.asciidoc +++ /dev/null @@ -1,172 +0,0 @@ -shadowsocks-c(8) -================ - -NAME ----- -shadowsocks-c - a lightweight and secure socks5 proxy - -SYNOPSIS --------- -// 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 *libuv* -to achieve both high performance and low resource consumption. - -*shadowsocks-c* consists of five components. One is `ss-server`(1) -that runs on a remote server to provide secured tunnel service. -`ss-local`(1) and `ss-redir`(1) are clients on your local machines to proxy -traffic(TCP/UDP or both). -`ss-tunnel`(1) is a tool for local port forwarding. - -While `ss-local`(1) works as a standard socks5 proxy, `ss-redir`(1) works -as a transparent proxy and requires netfilter's NAT module. For more -information, check out the 'EXAMPLE' section. - -`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. -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. - -`ss-nat`(1):: -See this command's generated SYNOPSIS and OPTIONS for its accepted arguments. - -CONFIG FILE ------------ -The config file is written in JSON and easy to edit. - -The config file equivalent of command line options is listed as example below. -[frame="topbot",options="header"] -|========================================================================== -| Command line | JSON -| -s some.server.net | "server": "some.server.net" -| -s some.server.net -p 1234 (client) | "server": "some.server.net:1234" -| -p 1234 | "server_port": "1234" -| -b 0.0.0.0 | "local_address": "0.0.0.0" -| -b 10.0.0.2 | "local_ipv4_address": "10.0.0.2" -| -b 2620:129:35::33 | "local_ipv6_address": "2620:129:35::33" -| -l 4321 | "local_port": "4321" -| -k "PasSworD" | "password": "PasSworD" -| -m "aes-256-cfb" | "method": "aes-256-cfb" -| -t 60 | "timeout": 60 -| -a nobody | "user": "nobody" -| --acl "/path/to/acl" | "acl": "/path/to/acl" -| --fast-open | "fast_open": true -| --reuse-port | "reuse_port": true -| --no-delay | "no_delay": true -| --plugin "obfs-server" | "plugin": "obfs-server" -| --plugin-opts "obfs=http" | "plugin_opts": "obfs=http" -| -6 | "ipv6_first": true -| -n "/etc/nofile" | "nofile": "/etc/nofile" -| -d "8.8.8.8" | "nameserver": "8.8.8.8" -| -L "somedns.net:53" | "tunnel_address": "somedns.net:53" -| -u | "mode": "tcp_and_udp" -| -U | "mode": "udp_only" -| no "-u" nor "-U" options (default) | "mode": "tcp_only" -| -T | "tcp_tproxy": true -| (only in ss-manager's config) | "port_password": {"1234":"PasSworD"} -|============================================================================ - -EXAMPLE -------- -`ss-redir` requires netfilter's NAT function. Here is an example: - -.... -# Create new chain -iptables -t nat -N SHADOWSOCKS -iptables -t mangle -N SHADOWSOCKS - -# Ignore your shadowsocks server's addresses -# It's very IMPORTANT, just be careful. -iptables -t nat -A SHADOWSOCKS -d 123.123.123.123 -j RETURN - -# Ignore LANs and any other addresses you'd like to bypass the proxy -# See Wikipedia and RFC5735 for full list of reserved networks. -# See ashi009/bestroutetb for a highly optimized CHN route list. -iptables -t nat -A SHADOWSOCKS -d 0.0.0.0/8 -j RETURN -iptables -t nat -A SHADOWSOCKS -d 10.0.0.0/8 -j RETURN -iptables -t nat -A SHADOWSOCKS -d 127.0.0.0/8 -j RETURN -iptables -t nat -A SHADOWSOCKS -d 169.254.0.0/16 -j RETURN -iptables -t nat -A SHADOWSOCKS -d 172.16.0.0/12 -j RETURN -iptables -t nat -A SHADOWSOCKS -d 192.168.0.0/16 -j RETURN -iptables -t nat -A SHADOWSOCKS -d 224.0.0.0/4 -j RETURN -iptables -t nat -A SHADOWSOCKS -d 240.0.0.0/4 -j RETURN - -# Anything else should be redirected to shadowsocks's local port -iptables -t nat -A SHADOWSOCKS -p tcp -j REDIRECT --to-ports 12345 - -# Add any UDP rules -ip rule add fwmark 0x01/0x01 table 100 -ip route add local 0.0.0.0/0 dev lo table 100 -iptables -t mangle -A SHADOWSOCKS -p udp --dport 53 -j TPROXY --on-port 12345 --tproxy-mark 0x01/0x01 - -# Apply the rules -iptables -t nat -A PREROUTING -p tcp -j SHADOWSOCKS -iptables -t mangle -A PREROUTING -j SHADOWSOCKS - -# Start the shadowsocks-redir -ss-redir -u -c /etc/config/shadowsocks.json -f /var/run/shadowsocks.pid -.... - -PROTOCOL --------- -`ss-manager`(1) provides several APIs through UDP protocol:: - -Send UDP commands in the following format to the manager-address provided to ss-manager(1): :::: - command: [JSON data] - -To add a port: :::: - add: {"server_port": 8001, "password":"7cd308cc059"} - -To remove a port: :::: - remove: {"server_port": 8001} - -To receive a pong: :::: - ping - -Then `ss-manager`(1) will send back the traffic statistics: :::: - stat: {"8001":11370} - -SEE ALSO --------- -`ss-local`(1), -`ss-server`(1), -`ss-tunnel`(1), -`ss-redir`(1), -`ss-manager`(1), -`iptables`(8), -/etc/shadowsocks-libev/config.json diff --git a/doc/shadowsocks-c.md b/doc/shadowsocks-c.md new file mode 100644 index 000000000..7f0480ab8 --- /dev/null +++ b/doc/shadowsocks-c.md @@ -0,0 +1,177 @@ +\page shadowsocks-c shadowsocks-c + +\brief a lightweight and secure socks5 proxy + +\section overview_synopsis SYNOPSIS + +*ss-local* [options] + +*ss-server* [options] + +*ss-tunnel* [options] + +*ss-redir* [options] + +*ss-manager* [options] + +*ss-nat* [options] + +\section overview_description 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 *libuv* +to achieve both high performance and low resource consumption. + +*shadowsocks-c* consists of five components. One is ss-server(1) +that runs on a remote server to provide secured tunnel service. +ss-local(1) and ss-redir(1) are clients on your local machines to proxy +traffic(TCP/UDP or both). +ss-tunnel(1) is a tool for local port forwarding. + +While ss-local(1) works as a standard socks5 proxy, ss-redir(1) works +as a transparent proxy and requires netfilter's NAT module. For more +information, check out the `EXAMPLE` section. + +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. +About the details of this API, please refer to the `PROTOCOL` section. + +\section overview_options OPTIONS + +**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. + +**ss-nat(1)** +See this command's generated SYNOPSIS and OPTIONS for its accepted arguments. + +\section overview_config_file CONFIG FILE + +The config file is written in JSON and easy to edit. + +The config file equivalent of command line options is listed as examples below. + +- `-s some.server.net`: `"server": "some.server.net"` +- `-s some.server.net -p 1234 (client)`: `"server": "some.server.net:1234"` +- `-p 1234`: `"server_port": "1234"` +- `-b 0.0.0.0`: `"local_address": "0.0.0.0"` +- `-b 10.0.0.2`: `"local_ipv4_address": "10.0.0.2"` +- `-b 2620:129:35::33`: `"local_ipv6_address": "2620:129:35::33"` +- `-l 4321`: `"local_port": "4321"` +- `-k "PasSworD"`: `"password": "PasSworD"` +- `-m "aes-256-cfb"`: `"method": "aes-256-cfb"` +- `-t 60`: `"timeout": 60` +- `-a nobody`: `"user": "nobody"` +- `--acl "/path/to/acl"`: `"acl": "/path/to/acl"` +- `--fast-open`: `"fast_open": true` +- `--reuse-port`: `"reuse_port": true` +- `--no-delay`: `"no_delay": true` +- `--plugin "obfs-server"`: `"plugin": "obfs-server"` +- `--plugin-opts "obfs=http"`: `"plugin_opts": "obfs=http"` +- `-6`: `"ipv6_first": true` +- `-n 1024`: `"nofile": 1024` +- `-d "8.8.8.8"`: `"nameserver": "8.8.8.8"` +- `-L "somedns.net:53"`: `"tunnel_address": "somedns.net:53"` +- `-u`: `"mode": "tcp_and_udp"` +- `-U`: `"mode": "udp_only"` +- `no "-u" nor "-U" options (default)`: `"mode": "tcp_only"` +- `-T`: `"tcp_tproxy": true` +- `(only in ss-manager's config)`: `"port_password": {"1234":"PasSworD"}` + | +\section overview_example EXAMPLE + +`ss-redir` requires netfilter's NAT function. Here is an example: + +``` +# Create new chain +iptables -t nat -N SHADOWSOCKS +iptables -t mangle -N SHADOWSOCKS + +# Ignore your shadowsocks server's addresses +# It's very IMPORTANT, just be careful. +iptables -t nat -A SHADOWSOCKS -d 123.123.123.123 -j RETURN + +# Ignore LANs and any other addresses you'd like to bypass the proxy +# See Wikipedia and RFC5735 for full list of reserved networks. +# See ashi009/bestroutetb for a highly optimized CHN route list. +iptables -t nat -A SHADOWSOCKS -d 0.0.0.0/8 -j RETURN +iptables -t nat -A SHADOWSOCKS -d 10.0.0.0/8 -j RETURN +iptables -t nat -A SHADOWSOCKS -d 127.0.0.0/8 -j RETURN +iptables -t nat -A SHADOWSOCKS -d 169.254.0.0/16 -j RETURN +iptables -t nat -A SHADOWSOCKS -d 172.16.0.0/12 -j RETURN +iptables -t nat -A SHADOWSOCKS -d 192.168.0.0/16 -j RETURN +iptables -t nat -A SHADOWSOCKS -d 224.0.0.0/4 -j RETURN +iptables -t nat -A SHADOWSOCKS -d 240.0.0.0/4 -j RETURN + +# Anything else should be redirected to shadowsocks's local port +iptables -t nat -A SHADOWSOCKS -p tcp -j REDIRECT --to-ports 12345 + +# Add any UDP rules +ip rule add fwmark 0x01/0x01 table 100 +ip route add local 0.0.0.0/0 dev lo table 100 +iptables -t mangle -A SHADOWSOCKS -p udp --dport 53 -j TPROXY --on-port 12345 --tproxy-mark 0x01/0x01 + +# Apply the rules +iptables -t nat -A PREROUTING -p tcp -j SHADOWSOCKS +iptables -t mangle -A PREROUTING -j SHADOWSOCKS + +# Start the shadowsocks-redir +ss-redir -u -c /etc/config/shadowsocks.json -f /var/run/shadowsocks.pid +``` + +\section overview_protocol PROTOCOL + +**ss-manager(1) provides several APIs through UDP protocol** + +**Send UDP commands in the following format to the manager-address provided to ss-manager(1):** + +```text +command: [JSON data] +``` + +**To add a port:** + +```text +add: {"server_port": 8001, "password":"7cd308cc059"} +``` + +**To remove a port:** + +```text +remove: {"server_port": 8001} +``` + +**To receive a pong:** + +```text +ping +``` + +**Then ss-manager(1) will send back the traffic statistics:** + +```text +stat: {"8001":11370} +``` + +\section overview_see_also SEE ALSO + +ss-local(1), +ss-server(1), +ss-tunnel(1), +ss-redir(1), +ss-manager(1), +iptables(8), +/etc/shadowsocks-libev/config.json diff --git a/doc/ss-local.asciidoc b/doc/ss-local.asciidoc deleted file mode 100644 index 7bb2233d7..000000000 --- a/doc/ss-local.asciidoc +++ /dev/null @@ -1,188 +0,0 @@ -ss-local(1) -=========== - -NAME ----- -ss-local - shadowsocks client as socks5 proxy, C implementation - -SYNOPSIS --------- -// 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-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 libuv to -achieve both high performance and low resource consumption. - -*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. - --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. - --t :: -Set the socket timeout in seconds. The default value is 60. - --m :: -Set the cipher. The default 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. -+ -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). 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. - --i :: -Send outbound traffic through the specified network interface where supported by the platform. - --c :: -Use a configuration file. -+ -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 the maximum number of open files. Requires a platform with setrlimit support. - --S :: -Android only: UNIX socket path for traffic statistics. - --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. - --A:: -Deprecated one-time authentication option. Exits with an error; use AEAD ciphers instead. - ---reuse-port:: -Enable port reuse where supported by the operating system. - ---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. - ---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) - ---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. - ---help:: -Print help message. - -EXAMPLE -------- -`ss-local`(1) can be started from command line and run in foreground. -Here is an example: -.... -# Start ss-local with given parameters -ss-local -s example.com -p 12345 -l 1080 -k foobar -m aes-256-cfb -.... - -SEE ALSO --------- -`ss-server`(1), -`ss-tunnel`(1), -`ss-redir`(1), -`ss-manager`(1), -`shadowsocks-libev`(8), -`iptables`(8), -/etc/shadowsocks-libev/config.json - diff --git a/doc/ss-local.md b/doc/ss-local.md new file mode 100644 index 000000000..7f6655937 --- /dev/null +++ b/doc/ss-local.md @@ -0,0 +1,43 @@ +\page ss-local ss-local + +\brief shadowsocks client as socks5 proxy, C implementation + +\section ss_local_synopsis SYNOPSIS + +`ss-local [options]` + +\section ss_local_description 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 libuv to +achieve both high performance and low resource consumption. + +*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). + +\section ss_local_options OPTIONS + +Options include all supported platform variants; restrictions are noted below. + +\snippet{doc} local.c cli-options + +\section ss_local_example EXAMPLE + +ss-local(1) can be started from command line and run in foreground. +Here is an example: +``` +# Start ss-local with given parameters +ss-local -s example.com -p 12345 -l 1080 -k foobar -m aes-256-cfb +``` + +\section ss_local_see_also SEE ALSO + +ss-server(1), +ss-tunnel(1), +ss-redir(1), +ss-manager(1), +shadowsocks-libev(8), +iptables(8), +/etc/shadowsocks-libev/config.json diff --git a/doc/ss-manager.asciidoc b/doc/ss-manager.asciidoc deleted file mode 100644 index 15f20b7d6..000000000 --- a/doc/ss-manager.asciidoc +++ /dev/null @@ -1,192 +0,0 @@ -ss-manager(1) -============= - -NAME ----- -ss-manager - ss-server controller for multi-user management and traffic statistics - -SYNOPSIS --------- -// 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-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 libuv to -achieve both high performance and low resource consumption. - -*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. -About the details of this API, please refer to the following 'PROTOCOL' -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 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. The default 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. -+ -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). 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. - --n :: -Specify the maximum number of open files. Requires a platform with setrlimit support. - --D :: -Set the working directory of ss-manager. - --6:: -Resolve hostname to IPv6 address first. - --h:: -Print help message. - --u:: -Enable UDP relay. - --U:: -Enable UDP relay and disable TCP relay. - --v:: -Enable verbose 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. - ---no-delay:: -Enable TCP_NODELAY. - ---reuse-port:: -Enable port reuse where supported by the operating system. - ---acl :: -Enable ACL (Access Control List) and specify config file. - ---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. - ---mtu :: -Specify the MTU of your network interface. - ---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. - ---workdir :: -Set the working directory of ss-manager (alias for *-D*). - ---help:: -Print help message. - -PROTOCOL --------- -`ss-manager`(1) provides several APIs through UDP protocol: - -Send UDP commands in the following format to the manager-address provided to ss-manager(1): :::: - command: [JSON data] - -To add a port: :::: - add: {"server_port": 8001, "password":"7cd308cc059"} - -To remove a port: :::: - remove: {"server_port": 8001} - -To receive the traffic statistics: :::: - ping - -The format of the traffic statistics: :::: - stat: {"8001":11370} - -There is no way to reset the traffic statistics, unless you remove the port and add it again - -EXAMPLE -------- -To use `ss-manager`(1), First start it and specify necessary information. - -Then communicate with `ss-manager`(1) through UNIX Domain Socket using UDP -protocol: - -.... -# Start the manager. Arguments for ss-server will be passed to generated -# ss-server process(es) respectively. -ss-manager --manager-address /tmp/manager.sock --executable $(which ss-server) -s example.com -m aes-256-cfb -c /path/to/config.json - -# Connect to the socket. Using netcat-openbsd as an example. -# You should use scripts or other programs for further management. -nc -Uu /tmp/manager.sock -.... - -After that, you may communicate with `ss-manager`(1) as described above in the -'PROTOCOL' section. - -SEE ALSO --------- -`ss-local`(1), -`ss-server`(1), -`ss-tunnel`(1), -`ss-redir`(1), -`shadowsocks-libev`(8), -`iptables`(8), -/etc/shadowsocks-libev/config.json diff --git a/doc/ss-manager.md b/doc/ss-manager.md new file mode 100644 index 000000000..a67d15cf4 --- /dev/null +++ b/doc/ss-manager.md @@ -0,0 +1,93 @@ +\page ss-manager ss-manager + +\brief ss-server controller for multi-user management and traffic statistics + +\section ss_manager_synopsis SYNOPSIS + +`ss-manager [options]` + +\section ss_manager_description 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 libuv to +achieve both high performance and low resource consumption. + +*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. +About the details of this API, please refer to the following `PROTOCOL` +section. + +\section ss_manager_options OPTIONS + +Options include all supported platform variants; restrictions are noted below. + +\snippet{doc} manager.c cli-options + +\section ss_manager_protocol PROTOCOL + +ss-manager(1) provides several APIs through UDP protocol: + +**Send UDP commands in the following format to the manager-address provided to ss-manager(1):** + +```text +command: [JSON data] +``` + +**To add a port:** + +```text +add: {"server_port": 8001, "password":"7cd308cc059"} +``` + +**To remove a port:** + +```text +remove: {"server_port": 8001} +``` + +**To receive the traffic statistics:** + +```text +ping +``` + +**The format of the traffic statistics:** + +```text +stat: {"8001":11370} +``` + +There is no way to reset the traffic statistics, unless you remove the port and add it again + +\section ss_manager_example EXAMPLE + +To use ss-manager(1), First start it and specify necessary information. + +Then communicate with ss-manager(1) through UNIX Domain Socket using UDP +protocol: + +``` +# Start the manager. Arguments for ss-server will be passed to generated +# ss-server process(es) respectively. +ss-manager --manager-address /tmp/manager.sock --executable $(which ss-server) -s example.com -m aes-256-cfb -c /path/to/config.json + +# Connect to the socket. Using netcat-openbsd as an example. +# You should use scripts or other programs for further management. +nc -Uu /tmp/manager.sock +``` + +After that, you may communicate with ss-manager(1) as described above in the +`PROTOCOL` section. + +\section ss_manager_see_also SEE ALSO + +ss-local(1), +ss-server(1), +ss-tunnel(1), +ss-redir(1), +shadowsocks-libev(8), +iptables(8), +/etc/shadowsocks-libev/config.json diff --git a/doc/ss-nat.asciidoc b/doc/ss-nat.asciidoc deleted file mode 100644 index 9c4e43a5a..000000000 --- a/doc/ss-nat.asciidoc +++ /dev/null @@ -1,103 +0,0 @@ -ss-nat(1) -========= - -NAME ----- -ss-nat - helper script to setup NAT rules for transparent proxy - -SYNOPSIS --------- -// 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-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 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. -It requires netfilter's NAT module and `iptables`(8). -For more information, check out `shadowsocks-libev`(8) and the following -'EXAMPLE' section. - -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 - --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 - --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 - --b :: -WAN IP of will be bypassed - --w :: -WAN IP of will be forwarded - --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 - -EXAMPLE -------- -`ss-nat` requires `iptables`(8). Here is an example: - -.... -# Enable NAT rules for shadowsocks, -# with both TCP and UDP redirection enabled, -# and applied for both PREROUTING and OUTPUT chains -root@Wrt:~# ss-nat -s 192.168.1.100 -l 1080 -u -o - -# Disable and flush all NAT rules for shadowsocks -root@Wrt:~# ss-nat -f -.... - -SEE ALSO --------- -`ss-local`(1), -`ss-server`(1), -`ss-tunnel`(1), -`ss-manager`(1), -`shadowsocks-libev`(8), -`iptables`(8), -/etc/shadowsocks-libev/config.json - diff --git a/doc/ss-nat.md b/doc/ss-nat.md new file mode 100644 index 000000000..f465be74f --- /dev/null +++ b/doc/ss-nat.md @@ -0,0 +1,49 @@ +\page ss-nat ss-nat + +\brief helper script to setup NAT rules for transparent proxy + +\section ss_nat_synopsis SYNOPSIS + +`ss-nat [options]` + +\section ss_nat_description 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 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. +It requires netfilter's NAT module and iptables(8). +For more information, check out shadowsocks-libev(8) and the following +`EXAMPLE` section. + +\section ss_nat_options OPTIONS + +Options include all supported platform variants; restrictions are noted below. + +\snippet{doc} ss-nat cli-options + +\section ss_nat_example EXAMPLE + +`ss-nat` requires iptables(8). Here is an example: + +``` +# Enable NAT rules for shadowsocks, +# with both TCP and UDP redirection enabled, +# and applied for both PREROUTING and OUTPUT chains +root@Wrt:~# ss-nat -s 192.168.1.100 -l 1080 -u -o + +# Disable and flush all NAT rules for shadowsocks +root@Wrt:~# ss-nat -f +``` + +\section ss_nat_see_also SEE ALSO + +ss-local(1), +ss-server(1), +ss-tunnel(1), +ss-manager(1), +shadowsocks-libev(8), +iptables(8), +/etc/shadowsocks-libev/config.json diff --git a/doc/ss-redir.asciidoc b/doc/ss-redir.asciidoc deleted file mode 100644 index ce0c408d5..000000000 --- a/doc/ss-redir.asciidoc +++ /dev/null @@ -1,203 +0,0 @@ -ss-redir(1) -=========== - -NAME ----- -ss-redir - shadowsocks client as transparent proxy, C implementation - -SYNOPSIS --------- -// 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-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 libuv to -achieve both high performance and low resource consumption. - -*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 -'EXAMPLE' section. - -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. - --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. - --t :: -Set the socket timeout in seconds. The default value is 60. - --m :: -Set the cipher. The default 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. -+ -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). 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-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 the maximum number of open files. Requires a platform with setrlimit support. - --h:: -Print help message. - --u:: -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 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. - ---mptcp:: -Enable Multipath TCP. -+ -Only available with MPTCP enabled Linux kernel. - ---plugin :: -Enable SIP003 plugin. (Experimental) - ---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. - ---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. - ---no-delay:: -Enable TCP_NODELAY. - ---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. - ---help:: -Print help message. - -EXAMPLE -------- -ss-redir requires netfilter's NAT function. Here is an example: - -.... -# Create new chain -iptables -t nat -N SHADOWSOCKS -iptables -t mangle -N SHADOWSOCKS - -# Ignore your shadowsocks server's addresses -# It's very IMPORTANT, just be careful. -iptables -t nat -A SHADOWSOCKS -d 123.123.123.123 -j RETURN - -# Ignore LANs and any other addresses you'd like to bypass the proxy -# See Wikipedia and RFC5735 for full list of reserved networks. -# See ashi009/bestroutetb for a highly optimized CHN route list. -iptables -t nat -A SHADOWSOCKS -d 0.0.0.0/8 -j RETURN -iptables -t nat -A SHADOWSOCKS -d 10.0.0.0/8 -j RETURN -iptables -t nat -A SHADOWSOCKS -d 127.0.0.0/8 -j RETURN -iptables -t nat -A SHADOWSOCKS -d 169.254.0.0/16 -j RETURN -iptables -t nat -A SHADOWSOCKS -d 172.16.0.0/12 -j RETURN -iptables -t nat -A SHADOWSOCKS -d 192.168.0.0/16 -j RETURN -iptables -t nat -A SHADOWSOCKS -d 224.0.0.0/4 -j RETURN -iptables -t nat -A SHADOWSOCKS -d 240.0.0.0/4 -j RETURN - -# Anything else should be redirected to shadowsocks's local port -iptables -t nat -A SHADOWSOCKS -p tcp -j REDIRECT --to-ports 12345 - -# Add any UDP rules -ip route add local default dev lo table 100 -ip rule add fwmark 1 lookup 100 -iptables -t mangle -A SHADOWSOCKS -p udp --dport 53 -j TPROXY --on-port 12345 --tproxy-mark 0x01/0x01 - -# Apply the rules -iptables -t nat -A PREROUTING -p tcp -j SHADOWSOCKS -iptables -t mangle -A PREROUTING -j SHADOWSOCKS - -# Start the shadowsocks-redir -ss-redir -u -c /etc/config/shadowsocks.json -f /var/run/shadowsocks.pid -.... - -SEE ALSO --------- -`ss-local`(1), -`ss-server`(1), -`ss-tunnel`(1), -`ss-manager`(1), -`shadowsocks-libev`(8), -`iptables`(8), -/etc/shadowsocks-libev/config.json diff --git a/doc/ss-redir.md b/doc/ss-redir.md new file mode 100644 index 000000000..008d89e6b --- /dev/null +++ b/doc/ss-redir.md @@ -0,0 +1,77 @@ +\page ss-redir ss-redir + +\brief shadowsocks client as transparent proxy, C implementation + +\section ss_redir_synopsis SYNOPSIS + +`ss-redir [options]` + +\section ss_redir_description 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 libuv to +achieve both high performance and low resource consumption. + +*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 +`EXAMPLE` section. + +\section ss_redir_options OPTIONS + +Options include all supported platform variants; restrictions are noted below. + +\snippet{doc} redir.c cli-options + +\section ss_redir_example EXAMPLE + +ss-redir requires netfilter's NAT function. Here is an example: + +``` +# Create new chain +iptables -t nat -N SHADOWSOCKS +iptables -t mangle -N SHADOWSOCKS + +# Ignore your shadowsocks server's addresses +# It's very IMPORTANT, just be careful. +iptables -t nat -A SHADOWSOCKS -d 123.123.123.123 -j RETURN + +# Ignore LANs and any other addresses you'd like to bypass the proxy +# See Wikipedia and RFC5735 for full list of reserved networks. +# See ashi009/bestroutetb for a highly optimized CHN route list. +iptables -t nat -A SHADOWSOCKS -d 0.0.0.0/8 -j RETURN +iptables -t nat -A SHADOWSOCKS -d 10.0.0.0/8 -j RETURN +iptables -t nat -A SHADOWSOCKS -d 127.0.0.0/8 -j RETURN +iptables -t nat -A SHADOWSOCKS -d 169.254.0.0/16 -j RETURN +iptables -t nat -A SHADOWSOCKS -d 172.16.0.0/12 -j RETURN +iptables -t nat -A SHADOWSOCKS -d 192.168.0.0/16 -j RETURN +iptables -t nat -A SHADOWSOCKS -d 224.0.0.0/4 -j RETURN +iptables -t nat -A SHADOWSOCKS -d 240.0.0.0/4 -j RETURN + +# Anything else should be redirected to shadowsocks's local port +iptables -t nat -A SHADOWSOCKS -p tcp -j REDIRECT --to-ports 12345 + +# Add any UDP rules +ip route add local default dev lo table 100 +ip rule add fwmark 1 lookup 100 +iptables -t mangle -A SHADOWSOCKS -p udp --dport 53 -j TPROXY --on-port 12345 --tproxy-mark 0x01/0x01 + +# Apply the rules +iptables -t nat -A PREROUTING -p tcp -j SHADOWSOCKS +iptables -t mangle -A PREROUTING -j SHADOWSOCKS + +# Start the shadowsocks-redir +ss-redir -u -c /etc/config/shadowsocks.json -f /var/run/shadowsocks.pid +``` + +\section ss_redir_see_also SEE ALSO + +ss-local(1), +ss-server(1), +ss-tunnel(1), +ss-manager(1), +shadowsocks-libev(8), +iptables(8), +/etc/shadowsocks-libev/config.json diff --git a/doc/ss-server.asciidoc b/doc/ss-server.asciidoc deleted file mode 100644 index b6c001fa5..000000000 --- a/doc/ss-server.asciidoc +++ /dev/null @@ -1,202 +0,0 @@ -ss-server(1) -============ - -NAME ----- -ss-server - shadowsocks server, C implementation - -SYNOPSIS --------- -// 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-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 libuv to -achieve both high performance and low resource consumption. - -*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 a server listening hostname or IP address. May be repeated. - --p :: -Set the server listening port. - --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. The default 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. -+ -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). 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. - --b :: -Set the local address for outbound connections to destination servers. - --c :: -Use a configuration file. -+ -Refer to `shadowsocks-c`(8) 'CONFIG FILE' section for more details. - --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. - --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. - --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. - ---reuse-port:: -Enable port reuse where supported by the operating system. - ---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. - ---no-delay:: -Enable TCP_NODELAY. - ---acl :: -Enable ACL (Access Control List) and specify config file. - ---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. - ---help:: -Print help message. - ---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. - ---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 -------- -It is recommended to use a config file when starting `ss-server`(1). - -The config file is written in JSON and is easy to edit. -Check out the 'SEE ALSO' section for the default path of config file. - -.... -# Start the ss-server -ss-server -c /etc/shadowsocks-libev/config.json -.... - -INCOMPATIBILITY ---------------- -The config file of `shadowsocks-libev`(8) is slightly different from original -shadowsocks. - -In order to listen to both IPv4/IPv6 address, use the following grammar in -your config json file: -.... -{ -"server":["::0","0.0.0.0"], -... -} -.... - -`ss-server`(1) also does not understand "port_password" field in config file. -If you want to start up multiple server instances with a single config file, -please try ss-manager tool. See `ss-manager`(8) for details. - -SEE ALSO --------- -`ss-local`(1), -`ss-tunnel`(1), -`ss-redir`(1), -`ss-manager`(1), -`shadowsocks-libev`(8), -`iptables`(8), -/etc/shadowsocks-libev/config.json diff --git a/doc/ss-server.md b/doc/ss-server.md new file mode 100644 index 000000000..e814bc050 --- /dev/null +++ b/doc/ss-server.md @@ -0,0 +1,64 @@ +\page ss-server ss-server + +\brief shadowsocks server, C implementation + +\section ss_server_synopsis SYNOPSIS + +`ss-server [options]` + +\section ss_server_description 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 libuv to +achieve both high performance and low resource consumption. + +*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). + +\section ss_server_options OPTIONS + +Options include all supported platform variants; restrictions are noted below. + +\snippet{doc} server.c cli-options + +\section ss_server_example EXAMPLE + +It is recommended to use a config file when starting ss-server(1). + +The config file is written in JSON and is easy to edit. +Check out the `SEE ALSO` section for the default path of config file. + +``` +# Start the ss-server +ss-server -c /etc/shadowsocks-libev/config.json +``` + +\section ss_server_incompatibility INCOMPATIBILITY + +The config file of shadowsocks-libev(8) is slightly different from original +shadowsocks. + +In order to listen to both IPv4/IPv6 address, use the following grammar in +your config json file: +``` +{ +"server":["::0","0.0.0.0"], +... +} +``` + +ss-server(1) also does not understand "port_password" field in config file. +If you want to start up multiple server instances with a single config file, +please try ss-manager tool. See ss-manager(8) for details. + +\section ss_server_see_also SEE ALSO + +ss-local(1), +ss-tunnel(1), +ss-redir(1), +ss-manager(1), +shadowsocks-libev(8), +iptables(8), +/etc/shadowsocks-libev/config.json diff --git a/doc/ss-tunnel.asciidoc b/doc/ss-tunnel.asciidoc deleted file mode 100644 index 485a68e29..000000000 --- a/doc/ss-tunnel.asciidoc +++ /dev/null @@ -1,181 +0,0 @@ -ss-tunnel(1) -============ - -NAME ----- -ss-tunnel - shadowsocks tools for local port forwarding, C implementation - -SYNOPSIS --------- -// 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-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 libuv to -achieve both high performance and low resource consumption. - -*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. - --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. - --t :: -Set the socket timeout in seconds. The default value is 60. - --m :: -Set the cipher. The default 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. -+ -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). 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. - --i :: -Send outbound traffic through the specified network interface where supported by the platform. - --c :: -Use a configuration file. -+ -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. - --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. - --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. - ---plugin :: -Enable SIP003 plugin. (Experimental) - ---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. - ---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. - ---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. - ---help:: -Print help message. - -EXAMPLE -------- -`ss-tunnel`(1) can be used to forward DNS queries to a remote DNS server -through the shadowsocks tunnel. Here is an example: - -.... -# Forward local UDP port 5353 to 8.8.8.8:53 through the ss-server -ss-tunnel -s example.com -p 12345 -l 5353 -k foobar -m aes-256-cfb -L 8.8.8.8:53 -u - -# Then configure your system to use 127.0.0.1:5353 as the DNS server -dig @127.0.0.1 -p 5353 www.google.com -.... - -SEE ALSO --------- -`ss-local`(1), -`ss-server`(1), -`ss-redir`(1), -`ss-manager`(1), -`shadowsocks-libev`(8), -`iptables`(8), -/etc/shadowsocks-libev/config.json diff --git a/doc/ss-tunnel.md b/doc/ss-tunnel.md new file mode 100644 index 000000000..16c411b24 --- /dev/null +++ b/doc/ss-tunnel.md @@ -0,0 +1,48 @@ +\page ss-tunnel ss-tunnel + +\brief shadowsocks tools for local port forwarding, C implementation + +\section ss_tunnel_synopsis SYNOPSIS + +`ss-tunnel [options]` + +\section ss_tunnel_description 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 libuv to +achieve both high performance and low resource consumption. + +*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). + +\section ss_tunnel_options OPTIONS + +Options include all supported platform variants; restrictions are noted below. + +\snippet{doc} tunnel.c cli-options + +\section ss_tunnel_example EXAMPLE + +ss-tunnel(1) can be used to forward DNS queries to a remote DNS server +through the shadowsocks tunnel. Here is an example: + +``` +# Forward local UDP port 5353 to 8.8.8.8:53 through the ss-server +ss-tunnel -s example.com -p 12345 -l 5353 -k foobar -m aes-256-cfb -L 8.8.8.8:53 -u + +# Then configure your system to use 127.0.0.1:5353 as the DNS server +dig @127.0.0.1 -p 5353 www.google.com +``` + +\section ss_tunnel_see_also SEE ALSO + +ss-local(1), +ss-server(1), +ss-redir(1), +ss-manager(1), +shadowsocks-libev(8), +iptables(8), +/etc/shadowsocks-libev/config.json diff --git a/scripts/check_cli_docs.py b/scripts/check_cli_docs.py new file mode 100644 index 000000000..9ac4c43a5 --- /dev/null +++ b/scripts/check_cli_docs.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python3 +"""Check native Doxygen CLI snippets against source getopt declarations. + +This is validation only. Doxygen reads source snippets and renders the manuals; +no generated documentation or intermediate markup is written by this script. +""" + +import argparse +from pathlib import Path +import re + +ROOT = Path(__file__).resolve().parents[1] +PROGRAMS = ("local", "server", "tunnel", "redir", "manager", "nat") +C_COMMENT = re.compile(r'/\*.*?\*/|//[^\n]*', re.S) + + +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 snippets(source): + result = {} + for match in re.finditer(r'(?:/\*|//) \[([\w-]+)\]\n(.*?)\n\[\1\](?: \*/)?', source, re.S): + name, body = match.groups() + if name in result: + raise ValueError(f"Duplicate snippet: {name}") + result[name] = body + return result + + +def documented_options(source_name, sources): + inventory = snippets(sources[source_name]).get('cli-options') + if not inventory: + raise ValueError(f"{source_name}: missing cli-options snippet") + result = {} + for line in inventory.splitlines(): + match = re.fullmatch(r'\\snippet\{doc\} ([\w.-]+) ([\w-]+)', line) + if not match: + raise ValueError(f"Invalid option snippet inclusion: {line}") + filename, identifier = match.groups() + body = snippets(sources.get(filename, '')).get(identifier) + if not body: + raise ValueError(f"Missing snippet: {filename} {identifier}") + heading, _, description = body.partition('\n') + term = re.fullmatch(r'\\par `(-{1,2}[A-Za-z0-9][A-Za-z0-9-]*)(?: (<[^>]+>))?`', heading) + if not term or not description.strip(): + raise ValueError(f"Invalid option documentation: {filename} {identifier}") + flag, argument = term.groups() + if flag in result: + raise ValueError(f"Duplicate documented option: {flag}") + result[flag] = bool(argument) + return result + + +def check(root=ROOT, rendered=None): + sources = {path.name: path.read_text(encoding='utf-8') + for path in (root / 'src').glob('*.c')} + sources['ss-nat'] = (root / 'src/ss-nat').read_text(encoding='utf-8') + for module in PROGRAMS: + program = 'ss-' + module + filename = 'ss-nat' if module == 'nat' else module + '.c' + declared = options(sources[filename], shell=module == 'nat') + documented = documented_options(filename, sources) + missing = declared.keys() - documented.keys() + extra = documented.keys() - declared.keys() + if missing or extra: + raise ValueError(f"{program}: missing documentation {sorted(missing)}; stale documentation {sorted(extra)}") + for flag in declared: + if declared[flag] != documented[flag]: + raise ValueError(f"{program}: argument mismatch for {flag}") + page = (root / f'doc/{program}.md').read_text(encoding='utf-8') + if page.count(f'\\snippet{{doc}} {filename} cli-options') != 1: + raise ValueError(f"{program}: manual must include its source cli-options exactly once") + if rendered: + man = (rendered / 'man' / (program + '.1')).read_text(encoding='utf-8') + man = re.sub(r'\\f[A-Z]', '', man).replace('\\-', '-') + for flag in declared: + if not re.search(r'(?]+>))?::') - - -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/aead.c b/src/aead.c index 6b1fa22a2..5d194f91e 100644 --- a/src/aead.c +++ b/src/aead.c @@ -93,6 +93,7 @@ */ const char *supported_aead_ciphers[AEAD_CIPHER_NUM] = { + // [cli-aead-ciphers] "aes-128-gcm", "aes-192-gcm", "aes-256-gcm", @@ -103,6 +104,7 @@ const char *supported_aead_ciphers[AEAD_CIPHER_NUM] = { #ifdef FS_HAVE_XCHACHA20IETF "xchacha20-ietf-poly1305" #endif + // [cli-aead-ciphers] }; /* diff --git a/src/local.c b/src/local.c index 4b006a348..200ef90da 100644 --- a/src/local.c +++ b/src/local.c @@ -1512,6 +1512,44 @@ main(int argc, char **argv) memset(remote_addr, 0, sizeof(ss_addr_t) * MAX_REMOTE_NUM); +/* [cli-options] +\snippet{doc} utils.c cli_short_f +\snippet{doc} utils.c cli_short_s +\snippet{doc} utils.c cli_short_p +\snippet{doc} utils.c cli_short_l +\snippet{doc} utils.c cli_short_k +\snippet{doc} utils.c cli_short_t +\snippet{doc} utils.c cli_short_m +\snippet{doc} utils.c cli_short_i +\snippet{doc} utils.c cli_short_c +\snippet{doc} utils.c cli_short_b +\snippet{doc} utils.c cli_short_a +\snippet{doc} utils.c cli_short_n +\snippet{doc} utils.c cli_short_S +\snippet{doc} utils.c cli_short_h +\snippet{doc} utils.c cli_short_u +\snippet{doc} utils.c cli_short_U +\snippet{doc} utils.c cli_short_v +\snippet{doc} utils.c cli_short_V +\snippet{doc} utils.c cli_short_6 +\snippet{doc} utils.c cli_short_A +\snippet{doc} utils.c cli_long_reuse_port +\snippet{doc} utils.c cli_long_tcp_incoming_sndbuf +\snippet{doc} utils.c cli_long_tcp_incoming_rcvbuf +\snippet{doc} utils.c cli_long_tcp_outgoing_sndbuf +\snippet{doc} utils.c cli_long_tcp_outgoing_rcvbuf +\snippet{doc} utils.c cli_long_fast_open +\snippet{doc} utils.c cli_long_no_delay +\snippet{doc} utils.c cli_long_acl +\snippet{doc} utils.c cli_long_mtu +\snippet{doc} utils.c cli_long_mptcp +\snippet{doc} utils.c cli_long_plugin +\snippet{doc} utils.c cli_long_plugin_opts +\snippet{doc} utils.c cli_long_password +\snippet{doc} utils.c cli_long_key +\snippet{doc} utils.c cli_long_server_url +\snippet{doc} utils.c cli_long_help +[cli-options] */ static struct option long_options[] = { { "reuse-port", no_argument, NULL, GETOPT_VAL_REUSE_PORT }, { "tcp-incoming-sndbuf", required_argument, NULL, GETOPT_VAL_TCP_INCOMING_SNDBUF }, diff --git a/src/manager.c b/src/manager.c index 019e89240..5517ccdb1 100644 --- a/src/manager.c +++ b/src/manager.c @@ -1271,16 +1271,53 @@ main(int argc, char **argv) jconf_t *conf = NULL; - /* CLI_DOC --s :: +/* [cli_short_s] +\par `-s ` Set a server listening hostname or IP address. May be repeated. +[cli_short_s] */ --l :: +/* [cli_short_l] +\par `-l ` Accepted for compatibility but ignored by this program; it does not configure a local listener. +[cli_short_l] */ --c :: +/* [cli_short_c] +\par `-c ` Use a JSON configuration file. The "port_password" field can start multiple ss-server instances. - */ +[cli_short_c] */ + +/* [cli-options] +\snippet{doc} utils.c cli_short_f +\snippet{doc} manager.c cli_short_s +\snippet{doc} manager.c cli_short_l +\snippet{doc} utils.c cli_short_k +\snippet{doc} utils.c cli_short_t +\snippet{doc} utils.c cli_short_m +\snippet{doc} manager.c cli_short_c +\snippet{doc} utils.c cli_short_i +\snippet{doc} utils.c cli_short_d +\snippet{doc} utils.c cli_short_a +\snippet{doc} utils.c cli_short_n +\snippet{doc} utils.c cli_short_D +\snippet{doc} utils.c cli_short_6 +\snippet{doc} utils.c cli_short_h +\snippet{doc} utils.c cli_short_u +\snippet{doc} utils.c cli_short_U +\snippet{doc} utils.c cli_short_v +\snippet{doc} utils.c cli_short_A +\snippet{doc} utils.c cli_long_fast_open +\snippet{doc} utils.c cli_long_no_delay +\snippet{doc} utils.c cli_long_reuse_port +\snippet{doc} utils.c cli_long_acl +\snippet{doc} utils.c cli_long_manager_address +\snippet{doc} utils.c cli_long_executable +\snippet{doc} utils.c cli_long_mtu +\snippet{doc} utils.c cli_long_plugin +\snippet{doc} utils.c cli_long_plugin_opts +\snippet{doc} utils.c cli_long_password +\snippet{doc} utils.c cli_long_workdir +\snippet{doc} utils.c cli_long_help +[cli-options] */ 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 4d37514e0..71e50649e 100644 --- a/src/redir.c +++ b/src/redir.c @@ -922,10 +922,45 @@ main(int argc, char **argv) memset(remote_addr, 0, sizeof(ss_addr_t) * MAX_REMOTE_NUM); - /* CLI_DOC --u:: +/* [cli_short_u] +\par `-u` Enable UDP relay. Requires Linux TPROXY support and permission to configure transparent proxying. - */ +[cli_short_u] */ + +/* [cli-options] +\snippet{doc} utils.c cli_short_f +\snippet{doc} utils.c cli_short_s +\snippet{doc} utils.c cli_short_p +\snippet{doc} utils.c cli_short_l +\snippet{doc} utils.c cli_short_k +\snippet{doc} utils.c cli_short_t +\snippet{doc} utils.c cli_short_m +\snippet{doc} utils.c cli_short_c +\snippet{doc} utils.c cli_short_b +\snippet{doc} utils.c cli_short_a +\snippet{doc} utils.c cli_short_n +\snippet{doc} utils.c cli_short_h +\snippet{doc} redir.c cli_short_u +\snippet{doc} utils.c cli_short_U +\snippet{doc} utils.c cli_short_T +\snippet{doc} utils.c cli_short_v +\snippet{doc} utils.c cli_short_6 +\snippet{doc} utils.c cli_short_A +\snippet{doc} utils.c cli_long_fast_open +\snippet{doc} utils.c cli_long_mtu +\snippet{doc} utils.c cli_long_mptcp +\snippet{doc} utils.c cli_long_plugin +\snippet{doc} utils.c cli_long_plugin_opts +\snippet{doc} utils.c cli_long_reuse_port +\snippet{doc} utils.c cli_long_tcp_incoming_sndbuf +\snippet{doc} utils.c cli_long_tcp_incoming_rcvbuf +\snippet{doc} utils.c cli_long_tcp_outgoing_sndbuf +\snippet{doc} utils.c cli_long_tcp_outgoing_rcvbuf +\snippet{doc} utils.c cli_long_no_delay +\snippet{doc} utils.c cli_long_password +\snippet{doc} utils.c cli_long_key +\snippet{doc} utils.c cli_long_help +[cli-options] */ 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 40a26399c..550bda116 100644 --- a/src/server.c +++ b/src/server.c @@ -1838,19 +1838,64 @@ 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 :: +/* [cli_short_s] +\par `-s ` Set a server listening hostname or IP address. May be repeated. +[cli_short_s] */ --l :: -Accepted for compatibility but ignored by this program; it does not configure a local listener. - --p :: +/* [cli_short_p] +\par `-p ` Set the server listening port. +[cli_short_p] */ + +/* [cli_short_l] +\par `-l ` +Accepted for compatibility but ignored by this program; it does not configure a local listener. +[cli_short_l] */ --b :: +/* [cli_short_b] +\par `-b ` Set the local address for outbound connections to destination servers. - */ +[cli_short_b] */ + +/* [cli-options] +\snippet{doc} utils.c cli_short_f +\snippet{doc} server.c cli_short_s +\snippet{doc} server.c cli_short_p +\snippet{doc} server.c cli_short_l +\snippet{doc} utils.c cli_short_k +\snippet{doc} utils.c cli_short_t +\snippet{doc} utils.c cli_short_m +\snippet{doc} server.c cli_short_b +\snippet{doc} utils.c cli_short_c +\snippet{doc} utils.c cli_short_i +\snippet{doc} utils.c cli_short_d +\snippet{doc} utils.c cli_short_a +\snippet{doc} utils.c cli_short_n +\snippet{doc} utils.c cli_short_h +\snippet{doc} utils.c cli_short_u +\snippet{doc} utils.c cli_short_U +\snippet{doc} utils.c cli_short_v +\snippet{doc} utils.c cli_short_6 +\snippet{doc} utils.c cli_short_A +\snippet{doc} utils.c cli_long_fast_open +\snippet{doc} utils.c cli_long_reuse_port +\snippet{doc} utils.c cli_long_tcp_incoming_sndbuf +\snippet{doc} utils.c cli_long_tcp_incoming_rcvbuf +\snippet{doc} utils.c cli_long_tcp_outgoing_sndbuf +\snippet{doc} utils.c cli_long_tcp_outgoing_rcvbuf +\snippet{doc} utils.c cli_long_no_delay +\snippet{doc} utils.c cli_long_acl +\snippet{doc} utils.c cli_long_manager_address +\snippet{doc} utils.c cli_long_mtu +\snippet{doc} utils.c cli_long_help +\snippet{doc} utils.c cli_long_plugin +\snippet{doc} utils.c cli_long_plugin_opts +\snippet{doc} utils.c cli_long_password +\snippet{doc} utils.c cli_long_key +\snippet{doc} utils.c cli_long_mptcp +\snippet{doc} utils.c cli_long_nftables_sets +[cli-options] */ 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 3894fcc2a..bdcb10752 100755 --- a/src/ss-nat +++ b/src/ss-nat @@ -168,52 +168,100 @@ 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 +: <<'DOXYGEN_CLI_DOCS' +// [cli_short_s] +\par `-s ` +IP address of shadowsocks remote server +[cli_short_s] + +// [cli_short_l] +\par `-l ` +Port number of shadowsocks local server +[cli_short_l] + +// [cli_short_S] +\par `-S ` +IP address of shadowsocks remote UDP server +[cli_short_S] + +// [cli_short_L] +\par `-L ` +Port number of shadowsocks local UDP server +[cli_short_L] + +// [cli_short_i] +\par `-i ` +a file whose content is bypassed ip list +[cli_short_i] + +// [cli_short_I] +\par `-I ` +Set the LAN interface for NAT rules. The default is eth0. +[cli_short_I] + +// [cli_short_e] +\par `-e ` +Extra options for iptables +[cli_short_e] + +// [cli_short_a] +\par `-a ` +LAN IP of access control, need a prefix to define access control mode +[cli_short_a] + +// [cli_short_b] +\par `-b ` +WAN IP of will be bypassed +[cli_short_b] + +// [cli_short_w] +\par `-w ` +WAN IP of will be forwarded +[cli_short_w] + +// [cli_short_o] +\par `-o` +Apply the rules to the OUTPUT chain +[cli_short_o] + +// [cli_short_u] +\par `-u` +Enable udprelay mode, TPROXY is required +[cli_short_u] + +// [cli_short_U] +\par `-U` +Enable udprelay mode, using different IP and ports for TCP and UDP +[cli_short_U] + +// [cli_short_f] +\par `-f` +Flush the rules +[cli_short_f] + +// [cli_short_h] +\par `-h` +Show this help message and exit +[cli_short_h] + +// [cli-options] +\snippet{doc} ss-nat cli_short_s +\snippet{doc} ss-nat cli_short_l +\snippet{doc} ss-nat cli_short_S +\snippet{doc} ss-nat cli_short_L +\snippet{doc} ss-nat cli_short_i +\snippet{doc} ss-nat cli_short_I +\snippet{doc} ss-nat cli_short_e +\snippet{doc} ss-nat cli_short_a +\snippet{doc} ss-nat cli_short_b +\snippet{doc} ss-nat cli_short_w +\snippet{doc} ss-nat cli_short_o +\snippet{doc} ss-nat cli_short_u +\snippet{doc} ss-nat cli_short_U +\snippet{doc} ss-nat cli_short_f +\snippet{doc} ss-nat cli_short_h +[cli-options] +DOXYGEN_CLI_DOCS while getopts ":s:l:S:L:i:I:e:a:b:w:ouUfh" arg; do case "$arg" in s) diff --git a/src/stream.c b/src/stream.c index 7ecff67bd..fda194e2d 100644 --- a/src/stream.c +++ b/src/stream.c @@ -96,6 +96,7 @@ #define CHACHA20IETF 20 const char *supported_stream_ciphers[STREAM_CIPHER_NUM] = { + // [cli-stream-ciphers] "table", "rc4", "rc4-md5", @@ -117,6 +118,7 @@ const char *supported_stream_ciphers[STREAM_CIPHER_NUM] = { "salsa20", "chacha20", "chacha20-ietf" + // [cli-stream-ciphers] }; static const char *supported_stream_ciphers_mbedtls[STREAM_CIPHER_NUM] = { diff --git a/src/tunnel.c b/src/tunnel.c index 60d917581..362b09fc6 100644 --- a/src/tunnel.c +++ b/src/tunnel.c @@ -957,6 +957,42 @@ main(int argc, char **argv) memset(remote_addr, 0, sizeof(ss_addr_t) * MAX_REMOTE_NUM); +/* [cli-options] +\snippet{doc} utils.c cli_short_f +\snippet{doc} utils.c cli_short_s +\snippet{doc} utils.c cli_short_p +\snippet{doc} utils.c cli_short_l +\snippet{doc} utils.c cli_short_k +\snippet{doc} utils.c cli_short_t +\snippet{doc} utils.c cli_short_m +\snippet{doc} utils.c cli_short_i +\snippet{doc} utils.c cli_short_c +\snippet{doc} utils.c cli_short_b +\snippet{doc} utils.c cli_short_L +\snippet{doc} utils.c cli_short_a +\snippet{doc} utils.c cli_short_n +\snippet{doc} utils.c cli_short_h +\snippet{doc} utils.c cli_short_u +\snippet{doc} utils.c cli_short_U +\snippet{doc} utils.c cli_short_v +\snippet{doc} utils.c cli_short_V +\snippet{doc} utils.c cli_short_6 +\snippet{doc} utils.c cli_short_A +\snippet{doc} utils.c cli_long_fast_open +\snippet{doc} utils.c cli_long_mtu +\snippet{doc} utils.c cli_long_no_delay +\snippet{doc} utils.c cli_long_mptcp +\snippet{doc} utils.c cli_long_plugin +\snippet{doc} utils.c cli_long_plugin_opts +\snippet{doc} utils.c cli_long_reuse_port +\snippet{doc} utils.c cli_long_tcp_incoming_sndbuf +\snippet{doc} utils.c cli_long_tcp_incoming_rcvbuf +\snippet{doc} utils.c cli_long_tcp_outgoing_sndbuf +\snippet{doc} utils.c cli_long_tcp_outgoing_rcvbuf +\snippet{doc} utils.c cli_long_password +\snippet{doc} utils.c cli_long_key +\snippet{doc} utils.c cli_long_help +[cli-options] */ 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/utils.c b/src/utils.c index d208a7a22..2c9ff1dca 100644 --- a/src/utils.c +++ b/src/utils.c @@ -317,161 +317,248 @@ ss_is_ipv6addr(const char *addr) return strcmp(addr, ":") > 0; } -/* CLI_DOC --s :: +/* [cli_short_s] +\par `-s ` Set the server's hostname or IP. +[cli_short_s] */ --p :: +/* [cli_short_p] +\par `-p ` Set the server's port number. +[cli_short_p] */ --l :: +/* [cli_short_l] +\par `-l ` Set the local port number. +[cli_short_l] */ --k :: +/* [cli_short_k] +\par `-k ` Set the password. The server and the client should use the same password. +[cli_short_k] */ ---password :: +/* [cli_long_password] +\par `--password ` Set the password. The server and the client should use the same password. +[cli_long_password] */ ---key :: +/* [cli_long_key] +\par `--key ` Set the key directly. The key should be encoded with URL-safe Base64. +[cli_long_key] */ ---server-url :: +/* [cli_long_server_url] +\par `--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 +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 +(`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. +[cli_long_server_url] */ + +/* [cli_short_m] +\par `-m ` +Set the cipher. The default is `chacha20-ietf-poly1305`. --m :: -Set the cipher. The default is 'chacha20-ietf-poly1305'. -+ AEAD cipher names from the source (availability depends on the build): -{cli-aead-ciphers}. -+ +\snippet aead.c 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 +some require backend support): +\snippet stream.c 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. +[cli_short_m] */ --a :: +/* [cli_short_a] +\par `-a ` Run as a specific user. +[cli_short_a] */ --f :: +/* [cli_short_f] +\par `-f ` Start shadowsocks as a daemon with specific pid file. +[cli_short_f] */ --t :: +/* [cli_short_t] +\par `-t ` Set the socket timeout in seconds. The default value is 60. +[cli_short_t] */ --c :: +/* [cli_short_c] +\par `-c ` Use a configuration file. -+ -Refer to `shadowsocks-c`(8) 'CONFIG FILE' section for more details. --n :: +Refer to `shadowsocks-c`(8) `CONFIG FILE` section for more details. +[cli_short_c] */ + +/* [cli_short_n] +\par `-n ` Specify the maximum number of open files. Requires a platform with setrlimit support. +[cli_short_n] */ --i :: +/* [cli_short_i] +\par `-i ` Send outbound traffic through the specified network interface where supported by the platform. +[cli_short_i] */ --b :: +/* [cli_short_b] +\par `-b ` Specify the local address to use while this client is making outbound connections to the server. +[cli_short_b] */ --u:: +/* [cli_short_u] +\par `-u` Enable UDP relay. +[cli_short_u] */ --U:: +/* [cli_short_U] +\par `-U` Enable UDP relay and disable TCP relay. +[cli_short_U] */ --6:: +/* [cli_short_6] +\par `-6` Resolve hostname to IPv6 address first. +[cli_short_6] */ ---fast-open:: +/* [cli_long_fast_open] +\par `--fast-open` Enable TCP Fast Open where supported by the operating system. +[cli_long_fast_open] */ ---reuse-port:: +/* [cli_long_reuse_port] +\par `--reuse-port` Enable port reuse where supported by the operating system. +[cli_long_reuse_port] */ ---acl :: +/* [cli_long_acl] +\par `--acl ` Enable ACL (Access Control List) and specify config file. +[cli_long_acl] */ ---mtu :: +/* [cli_long_mtu] +\par `--mtu ` Specify the MTU of your network interface. +[cli_long_mtu] */ ---mptcp:: +/* [cli_long_mptcp] +\par `--mptcp` Enable Multipath TCP. -+ + Only available with MPTCP enabled Linux kernel. +[cli_long_mptcp] */ ---no-delay:: +/* [cli_long_no_delay] +\par `--no-delay` Enable TCP_NODELAY. +[cli_long_no_delay] */ ---tcp-incoming-sndbuf :: +/* [cli_long_tcp_incoming_sndbuf] +\par `--tcp-incoming-sndbuf ` Set TCP send buffer size for incoming connections. +[cli_long_tcp_incoming_sndbuf] */ ---tcp-incoming-rcvbuf :: +/* [cli_long_tcp_incoming_rcvbuf] +\par `--tcp-incoming-rcvbuf ` Set TCP receive buffer size for incoming connections. +[cli_long_tcp_incoming_rcvbuf] */ ---tcp-outgoing-sndbuf :: +/* [cli_long_tcp_outgoing_sndbuf] +\par `--tcp-outgoing-sndbuf ` Set TCP send buffer size for outgoing connections. +[cli_long_tcp_outgoing_sndbuf] */ ---tcp-outgoing-rcvbuf :: +/* [cli_long_tcp_outgoing_rcvbuf] +\par `--tcp-outgoing-rcvbuf ` Set TCP receive buffer size for outgoing connections. +[cli_long_tcp_outgoing_rcvbuf] */ ---plugin :: +/* [cli_long_plugin] +\par `--plugin ` Enable SIP003 plugin. (Experimental) +[cli_long_plugin] */ ---plugin-opts :: +/* [cli_long_plugin_opts] +\par `--plugin-opts ` Set SIP003 plugin options. (Experimental) +[cli_long_plugin_opts] */ --v:: +/* [cli_short_v] +\par `-v` Enable verbose mode. +[cli_short_v] */ --h:: +/* [cli_short_h] +\par `-h` Print help message. +[cli_short_h] */ ---help:: +/* [cli_long_help] +\par `--help` Print help message. +[cli_long_help] */ --A:: +/* [cli_short_A] +\par `-A` Deprecated one-time authentication option. Exits with an error; use AEAD ciphers instead. +[cli_short_A] */ --S :: +/* [cli_short_S] +\par `-S ` Android only: UNIX socket path for traffic statistics. +[cli_short_S] */ --V:: +/* [cli_short_V] +\par `-V` Android only: enable VPN socket protection. +[cli_short_V] */ --L :: +/* [cli_short_L] +\par `-L ` Destination server address and port for local port forwarding. +[cli_short_L] */ --T:: +/* [cli_short_T] +\par `-T` Use TPROXY instead of REDIRECT for TCP traffic. Requires Linux TPROXY support. +[cli_short_T] */ --d :: +/* [cli_short_d] +\par `-d ` Configure name servers for the internal c-ares DNS resolver. By default it uses the system resolver configuration. +[cli_short_d] */ --D :: +/* [cli_short_D] +\par `-D ` Set the working directory of ss-manager. +[cli_short_D] */ ---workdir :: +/* [cli_long_workdir] +\par `--workdir ` Set the working directory of ss-manager (alias for *-D*). +[cli_long_workdir] */ ---manager-address
:: +/* [cli_long_manager_address] +\par `--manager-address
` Set the manager control address: a UNIX domain socket path or an IP address and port. +[cli_long_manager_address] */ ---executable :: +/* [cli_long_executable] +\par `--executable ` Set the executable path of ss-server used by ss-manager. +[cli_long_executable] */ ---nftables-sets :: +/* [cli_long_nftables_sets] +\par `--nftables-sets ` Linux builds with USE_NFTABLES only: add malicious IP addresses to nftables sets. Format: `[:][,[:]...]`. -*/ +[cli_long_nftables_sets] */ void usage() diff --git a/tests/test_cli_docs.py b/tests/test_cli_docs.py new file mode 100644 index 000000000..959be7a41 --- /dev/null +++ b/tests/test_cli_docs.py @@ -0,0 +1,94 @@ +"""Regression checks for drift between getopt and native Doxygen snippets.""" + +import importlib.util +from pathlib import Path +import shutil +import tempfile +import unittest + +ROOT = Path(__file__).resolve().parents[1] +SPEC = importlib.util.spec_from_file_location('check_cli_docs', ROOT / 'scripts/check_cli_docs.py') +DOCS = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(DOCS) + + +class CliDocsTests(unittest.TestCase): + def setUp(self): + self.temp = tempfile.TemporaryDirectory() + self.addCleanup(self.temp.cleanup) + self.root = Path(self.temp.name) + (self.root / 'src').mkdir() + (self.root / 'doc').mkdir() + for name in ('local.c', 'server.c', 'tunnel.c', 'redir.c', 'manager.c', 'utils.c', 'ss-nat'): + shutil.copyfile(ROOT / 'src' / name, self.root / 'src' / name) + for path in (ROOT / 'doc').glob('*.md'): + shutil.copyfile(path, self.root / 'doc' / path.name) + + def change(self, filename, old, new): + path = self.root / filename + source = path.read_text(encoding='utf-8') + self.assertIn(old, source) + path.write_text(source.replace(old, new), encoding='utf-8') + + def test_current_documentation_matches_all_variants(self): + DOCS.check(self.root) + sources = {p.name: p.read_text(encoding='utf-8') for p in (self.root / 'src').iterdir()} + local = DOCS.documented_options('local.c', sources) + self.assertIn('-S', local) + self.assertIn('-V', local) + self.assertIn('--nftables-sets', DOCS.documented_options('server.c', sources)) + self.assertIn('-I', DOCS.documented_options('ss-nat', sources)) + self.assertNotIn('-p', DOCS.documented_options('manager.c', sources)) + + def test_new_option_requires_documentation(self): + self.change('src/local.c', '"reuse-port",', '"new-option",') + with self.assertRaisesRegex(ValueError, 'missing documentation.*new-option'): + DOCS.check(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'): + DOCS.check(self.root) + + def test_unknown_parser_syntax_fails_closed(self): + self.change('src/local.c', '"reuse-port", no_argument', + '"reuse-port", optional_argument') + with self.assertRaisesRegex(ValueError, 'Unsupported long_options'): + DOCS.check(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'): + DOCS.check(self.root) + + def test_shell_options_require_documentation(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 documentation.*-z'): + DOCS.check(self.root) + + def test_missing_snippet_is_rejected(self): + self.change('src/local.c', 'utils.c cli_long_reuse_port', 'utils.c missing_snippet') + with self.assertRaisesRegex(ValueError, 'Missing snippet'): + DOCS.check(self.root) + + def test_duplicate_option_is_rejected(self): + self.change('src/local.c', 'utils.c cli_long_reuse_port', 'utils.c cli_short_f') + with self.assertRaisesRegex(ValueError, 'Duplicate documented option'): + DOCS.check(self.root) + + def test_manual_must_include_parser_options(self): + self.change('doc/ss-local.md', 'local.c cli-options', 'local.c missing-options') + with self.assertRaisesRegex(ValueError, 'manual must include'): + DOCS.check(self.root) + + def test_missing_rendered_flag_is_rejected(self): + output = self.root / 'rendered' + (output / 'man').mkdir(parents=True) + (output / 'man/ss-local.1').write_text('.TH ss-local 1\n', encoding='utf-8') + with self.assertRaisesRegex(ValueError, 'missing rendered flag'): + DOCS.check(self.root, output) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_gen_cli_docs.py b/tests/test_gen_cli_docs.py deleted file mode 100644 index 0572633ca..000000000 --- a/tests/test_gen_cli_docs.py +++ /dev/null @@ -1,107 +0,0 @@ -"""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()