From 7bb250739ef0067c62e4ffd533ec8cee2775995a Mon Sep 17 00:00:00 2001 From: Max Lv Date: Sat, 7 Feb 2026 17:06:47 +0800 Subject: [PATCH] Modernize build: switch to CMake, add unit tests, fix man pages * Modernize build: switch to CMake, add unit tests, fix man pages Remove autotools (configure.ac, Makefile.am, m4/, autogen.sh) and make CMake the sole build system. Enhance CMake with proper Find modules for MbedTLS, libsodium, PCRE, and c-ares with version/feature detection. Add support for connmarktos, nftables, stack protector, and MinGW. Add 10 unit test suites (CTest) covering base64, buffer, cache, crypto, jconf, json, netutils, ppbloom, rule, and utils modules. Add a Python stress test measuring bandwidth per cipher with memory leak detection. Fix man page typos ("Resovle" -> "Resolve"), document missing TCP buffer options, fix ss-manager duplicate --executable entry, add example section to ss-tunnel, and document --workdir and --nftables-sets options. Co-Authored-By: Claude Opus 4.6 * Migrate from PCRE to PCRE2 Replace legacy PCRE (libpcre3) with PCRE2 (libpcre2-8). PCRE1 has been end-of-life since 2021. - src/rule.h: use pcre2_code and pcre2_match_data types - src/rule.c: pcre_compile -> pcre2_compile, pcre_exec -> pcre2_match, pcre_free -> pcre2_code_free, with proper error message retrieval - cmake/FindPCRE.cmake -> cmake/FindPCRE2.cmake: find libpcre2-8 via pkg-config, pcre2-config, or manual search - Update all CMakeLists.txt, config.h.cmake, configure.cmake, README.md Co-Authored-By: Claude Opus 4.6 * Update Dockerfiles: use CMake build and pcre2-dev The alpine Dockerfile still used autotools (autogen.sh/configure) which were removed. Switch to cmake build and replace pcre-dev with pcre2-dev. Co-Authored-By: Claude Opus 4.6 * Fix Docker build: disable tests and remove git dep The tests/ directory is excluded by .dockerignore, so pass -DBUILD_TESTING=OFF to cmake. Submodules are already checked out by actions/checkout, no need for git inside the container. Co-Authored-By: Claude Opus 4.6 * Fix Docker build: disable static libs (not available in alpine) Alpine's mbedtls-dev only provides shared libraries. Disable static build since Docker only needs shared binaries. Co-Authored-By: Claude Opus 4.6 * Rewrite GitHub Actions: build and test directly on runner Replace Docker-based CI with direct cmake build on ubuntu-latest and macos-latest. Install dependencies via apt/brew, build with cmake, run unit tests with ctest. Co-Authored-By: Claude Opus 4.6 * Fix CI: use mbedtls@3 on macOS, disable static build - macOS: brew's mbedtls is now v4 (incompatible), use mbedtls@3 - Disable static build since CI runners lack static lib packages Co-Authored-By: Claude Opus 4.6 * Fix test build on Linux: add stdint.h and link libm - test_ppbloom.c: add missing #include for uint8_t - test_json: link against libm for pow() used in json.c Co-Authored-By: Claude Opus 4.6 * Fix test_ppbloom link: add libm for bloom's log() On Linux, libbloom uses log() which requires explicit -lm linkage. Co-Authored-By: Claude Opus 4.6 * Fix Linux build: link libm for test_buffer and test_crypto, fix warnings Both test_buffer and test_crypto link libbloom.so which uses log() from libm. Also fix unused variable warnings in test_json.c that would fail with -Werror. Co-Authored-By: Claude Opus 4.6 * Fix Linux build: link libm for test_jconf (json.c uses pow) Co-Authored-By: Claude Opus 4.6 * CI: add unit test and stress test steps to GitHub Actions Split the Test step into separate "Unit tests" (ctest) and "Stress test" (stress_test.py with 10MB transfer across all AEAD ciphers) steps for better visibility. Co-Authored-By: Claude Opus 4.6 --------- Co-authored-by: Claude Opus 4.6 --- .github/workflows/build.yml | 72 ++--- .travis.yml | 14 +- CMakeLists.txt | 62 +++- INSTALL | 365 ----------------------- Makefile.am | 20 -- README.md | 32 +- autogen.sh | 3 - build/.gitkeep | 0 cmake/FindCares.cmake | 44 +++ cmake/FindMbedTLS.cmake | 88 ++++++ cmake/FindPCRE2.cmake | 63 ++++ cmake/FindSodium.cmake | 57 ++++ cmake/config.h.cmake | 22 +- cmake/configure.cmake | 67 +++-- cmake/shadowsocks-libev.pc.cmake | 10 +- configure.ac | 311 -------------------- doc/Makefile.am | 60 ---- doc/shadowsocks-libev.asciidoc | 31 ++ doc/ss-local.asciidoc | 14 +- doc/ss-manager.asciidoc | 3 +- doc/ss-redir.asciidoc | 14 +- doc/ss-server.asciidoc | 14 +- doc/ss-tunnel.asciidoc | 27 +- docker/alpine/Dockerfile | 10 +- docker/build/builder.Dockerfile | 2 +- m4/ax_pthread.m4 | 485 ------------------------------- m4/ax_tls.m4 | 74 ----- m4/cares.m4 | 30 -- m4/inet_ntop.m4 | 41 --- m4/mbedtls.m4 | 114 -------- m4/pcre.m4 | 152 ---------- m4/sodium.m4 | 40 --- m4/stack-protector.m4 | 58 ---- shadowsocks-libev.pc.in | 12 - src/CMakeLists.txt | 94 ++++-- src/Makefile.am | 115 -------- src/rule.c | 21 +- src/rule.h | 10 +- tests/CMakeLists.txt | 118 ++++++++ tests/stress_test.py | 445 ++++++++++++++++++++++++++++ tests/test_base64.c | 116 ++++++++ tests/test_buffer.c | 103 +++++++ tests/test_cache.c | 129 ++++++++ tests/test_crypto.c | 164 +++++++++++ tests/test_jconf.c | 85 ++++++ tests/test_json.c | 130 +++++++++ tests/test_netutils.c | 115 ++++++++ tests/test_ppbloom.c | 74 +++++ tests/test_rule.c | 126 ++++++++ tests/test_utils.c | 76 +++++ 50 files changed, 2293 insertions(+), 2039 deletions(-) delete mode 100644 INSTALL delete mode 100644 Makefile.am delete mode 100755 autogen.sh delete mode 100644 build/.gitkeep create mode 100644 cmake/FindCares.cmake create mode 100644 cmake/FindMbedTLS.cmake create mode 100644 cmake/FindPCRE2.cmake create mode 100644 cmake/FindSodium.cmake delete mode 100755 configure.ac delete mode 100644 doc/Makefile.am delete mode 100644 m4/ax_pthread.m4 delete mode 100644 m4/ax_tls.m4 delete mode 100644 m4/cares.m4 delete mode 100644 m4/inet_ntop.m4 delete mode 100644 m4/mbedtls.m4 delete mode 100644 m4/pcre.m4 delete mode 100644 m4/sodium.m4 delete mode 100644 m4/stack-protector.m4 delete mode 100644 shadowsocks-libev.pc.in delete mode 100644 src/Makefile.am create mode 100644 tests/CMakeLists.txt create mode 100755 tests/stress_test.py create mode 100644 tests/test_base64.c create mode 100644 tests/test_buffer.c create mode 100644 tests/test_cache.c create mode 100644 tests/test_crypto.c create mode 100644 tests/test_jconf.c create mode 100644 tests/test_json.c create mode 100644 tests/test_netutils.c create mode 100644 tests/test_ppbloom.c create mode 100644 tests/test_rule.c create mode 100644 tests/test_utils.c diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bc10dee6..d671e3e9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,51 +2,43 @@ name: build on: push: - push: - branches: - - master - tags: - - v* - - latest + branches: [master] + pull_request: + branches: [master] jobs: build: - runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest] + runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v2.3.4 + - uses: actions/checkout@v4 with: - submodules: true + submodules: recursive - - uses: docker/setup-qemu-action@v1.1.0 - - uses: docker/setup-buildx-action@v1.3.0 - - uses: docker/login-action@v1.9.0 - with: - password: ${{ secrets.DOCKER_PASSWORD }} - username: ${{ secrets.DOCKER_USERNAME }} - - - name: check and set image version - id: prepare + - name: Install dependencies (Linux) + if: runner.os == 'Linux' run: | - case ${{ github.ref }} in - refs/heads/master) - echo ::set-output name=version::edge - echo ::set-output name=push::true - ;; - refs/tags/*) - echo ::set-output name=version::$(echo ${{ github.ref }} | sed -E 's|refs/tags/||') - echo ::set-output name=push::true - ;; - *) - echo ::set-output name=version::${{ github.sha }} - echo ::set-output name=push::false - ;; - esac; + sudo apt-get update + sudo apt-get install -y libpcre2-dev libmbedtls-dev libsodium-dev libev-dev libc-ares-dev - - name: build & push image - uses: docker/build-push-action@v2.4.0 - with: - context: . - file: docker/alpine/Dockerfile - platforms: linux/amd64,linux/arm64 - push: ${{ steps.prepare.outputs.push }} - tags: ${{ github.repository }}:${{ steps.prepare.outputs.version }} \ No newline at end of file + - name: Install dependencies (macOS) + if: runner.os == 'macOS' + run: brew install mbedtls@3 libsodium libev c-ares pcre2 + + - name: Build + run: | + mkdir -p build && cd build + cmake .. -DCMAKE_BUILD_TYPE=Release -DWITH_STATIC=OFF + make -j$(nproc 2>/dev/null || sysctl -n hw.ncpu) + + - name: Unit tests + run: | + cd build + ctest --output-on-failure + + - name: Stress test + run: | + python3 tests/stress_test.py --bin build/shared/bin/ --size 10 diff --git a/.travis.yml b/.travis.yml index ee3424c2..e2ca4429 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,7 @@ before_install: - | if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then # All dependencies for macOS build. Some packages has been installed by travis so use reinstall. - brew reinstall autoconf automake xmlto c-ares libev mbedtls libsodium asciidoc >> /dev/null 2>&1; + brew reinstall cmake xmlto c-ares libev mbedtls libsodium asciidoc >> /dev/null 2>&1; else wget https://github.com/jedisct1/libsodium/releases/download/$LIBSODIUM_VER/libsodium-$LIBSODIUM_VER.tar.gz; tar xvf libsodium-$LIBSODIUM_VER.tar.gz; @@ -39,19 +39,17 @@ addons: sources: - george-edison55-precise-backports # cmake 3.2.3 / doxygen 1.8.3 packages: + - cmake - libc-ares-dev - libev-dev - asciidoc - xmlto script: - - ./autogen.sh - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then - ./configure --disable-documentation --with-mbedtls=/usr/local/opt/mbedtls --with-sodium=/usr/local/opt/libsodium; - else - ./configure; - fi + - git submodule update --init --recursive + - mkdir -p build && cd build + - cmake .. - make - - cd build && cmake ../ && make + - ctest --output-on-failure branches: only: - master diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c1c2d06..6ca505f4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.2) +cmake_minimum_required(VERSION 3.5) set(PROJECT_NAME shadowsocks-libev) set(RELEASE_DATE 2020-09-15) @@ -8,9 +8,14 @@ set(PROJECT_URL "https://shadowsocks.org") set(PROJECT_ISSUES_URL "https://github.com/shadowsocks/shadowsocks-libev") project(${PROJECT_NAME} VERSION ${PROJECT_VERSION}) -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99") +include(GNUInstallDirs) + +# Compiler flags matching autotools +# Note: -Werror is applied per-target in src/CMakeLists.txt to avoid +# breaking bundled submodules (libcork, libipset, libbloom) +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -D_GNU_SOURCE") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O2 -Wall -Wno-deprecated-declarations -fno-strict-aliasing") -#set(PROJECT_BINARY_DIR ${PROJECT_SOURCE_DIR}/out) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) @@ -33,15 +38,48 @@ message(STATUS "Running cmake version ${CMAKE_VERSION}") option(WITH_STATIC "build with static libraries." ON) option(WITH_EMBEDDED_SRC "build with embedded libcork, libipset, and libbloom source." ON) - -# Will set GIT_EXECUTABLE and GIT_FOUND -# find_package(Git) +option(ENABLE_CONNMARKTOS "Enable saved connmark to IP TOS QoS feature" OFF) +option(ENABLE_NFTABLES "Report malicious IP to nftables" OFF) # When choose to not use embedded libcork, libipset and libbloom, use libs shipped by system if (NOT WITH_EMBEDDED_SRC) set(USE_SYSTEM_SHARED_LIB TRUE) endif () +# Find dependencies via Find modules +find_package(PCRE2 REQUIRED) +find_package(MbedTLS REQUIRED) +find_package(Sodium REQUIRED) +find_package(Cares REQUIRED) + +# Connmarktos support +if(ENABLE_CONNMARKTOS) + if(NOT LINUX) + message(FATAL_ERROR "connmarktos is only supported on Linux") + endif() + find_library(NETFILTER_CONNTRACK_LIB netfilter_conntrack) + find_library(NFNETLINK_LIB nfnetlink) + if(NOT NETFILTER_CONNTRACK_LIB) + message(FATAL_ERROR "--enable-connmarktos specified but libnetfilter_conntrack not found") + endif() + set(USE_NFCONNTRACK_TOS 1) + message(STATUS "Connmarktos enabled") +endif() + +# Nftables support +if(ENABLE_NFTABLES) + if(NOT LINUX) + message(FATAL_ERROR "nftables is only supported on Linux") + endif() + find_library(MNL_LIB mnl) + find_library(NFTNL_LIB nftnl) + if(NOT MNL_LIB OR NOT NFTNL_LIB) + message(FATAL_ERROR "--enable-nftables specified but libmnl or libnftnl not found") + endif() + set(USE_NFTABLES 1) + message(STATUS "Nftables enabled") +endif() + # Run platform tests include(${PROJECT_SOURCE_DIR}/cmake/configure.cmake) configure_file(${PROJECT_SOURCE_DIR}/cmake/config.h.cmake ${PROJECT_BINARY_DIR}/src/config.h) @@ -56,7 +94,7 @@ configure_file( ) install(FILES ${PROJECT_BINARY_DIR}/pkgconfig/shadowsocks-libev.pc - DESTINATION lib/pkgconfig + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig ) if (WITH_EMBEDDED_SRC) @@ -158,3 +196,13 @@ endif () add_subdirectory(src) add_subdirectory(doc) +# Testing +include(CTest) +if(BUILD_TESTING) + add_subdirectory(tests) +endif() + +# Install ss-nat on Linux +if(LINUX) + install(PROGRAMS src/ss-nat DESTINATION ${CMAKE_INSTALL_BINDIR}) +endif() diff --git a/INSTALL b/INSTALL deleted file mode 100644 index 7d1c323b..00000000 --- a/INSTALL +++ /dev/null @@ -1,365 +0,0 @@ -Installation Instructions -************************* - -Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, -2006, 2007, 2008, 2009 Free Software Foundation, Inc. - - Copying and distribution of this file, with or without modification, -are permitted in any medium without royalty provided the copyright -notice and this notice are preserved. This file is offered as-is, -without warranty of any kind. - -Basic Installation -================== - - Briefly, the shell commands `./configure; make; make install' should -configure, build, and install this package. The following -more-detailed instructions are generic; see the `README' file for -instructions specific to this package. Some packages provide this -`INSTALL' file but do not implement all of the features documented -below. The lack of an optional feature in a given package is not -necessarily a bug. More recommendations for GNU packages can be found -in *note Makefile Conventions: (standards)Makefile Conventions. - - The `configure' shell script attempts to guess correct values for -various system-dependent variables used during compilation. It uses -those values to create a `Makefile' in each directory of the package. -It may also create one or more `.h' files containing system-dependent -definitions. Finally, it creates a shell script `config.status' that -you can run in the future to recreate the current configuration, and a -file `config.log' containing compiler output (useful mainly for -debugging `configure'). - - It can also use an optional file (typically called `config.cache' -and enabled with `--cache-file=config.cache' or simply `-C') that saves -the results of its tests to speed up reconfiguring. Caching is -disabled by default to prevent problems with accidental use of stale -cache files. - - If you need to do unusual things to compile the package, please try -to figure out how `configure' could check whether to do them, and mail -diffs or instructions to the address given in the `README' so they can -be considered for the next release. If you are using the cache, and at -some point `config.cache' contains results you don't want to keep, you -may remove or edit it. - - The file `configure.ac' (or `configure.in') is used to create -`configure' by a program called `autoconf'. You need `configure.ac' if -you want to change it or regenerate `configure' using a newer version -of `autoconf'. - - The simplest way to compile this package is: - - 1. `cd' to the directory containing the package's source code and type - `./configure' to configure the package for your system. - - Running `configure' might take a while. While running, it prints - some messages telling which features it is checking for. - - 2. Type `make' to compile the package. - - 3. Optionally, type `make check' to run any self-tests that come with - the package, generally using the just-built uninstalled binaries. - - 4. Type `make install' to install the programs and any data files and - documentation. When installing into a prefix owned by root, it is - recommended that the package be configured and built as a regular - user, and only the `make install' phase executed with root - privileges. - - 5. Optionally, type `make installcheck' to repeat any self-tests, but - this time using the binaries in their final installed location. - This target does not install anything. Running this target as a - regular user, particularly if the prior `make install' required - root privileges, verifies that the installation completed - correctly. - - 6. You can remove the program binaries and object files from the - source code directory by typing `make clean'. To also remove the - files that `configure' created (so you can compile the package for - a different kind of computer), type `make distclean'. There is - also a `make maintainer-clean' target, but that is intended mainly - for the package's developers. If you use it, you may have to get - all sorts of other programs in order to regenerate files that came - with the distribution. - - 7. Often, you can also type `make uninstall' to remove the installed - files again. In practice, not all packages have tested that - uninstallation works correctly, even though it is required by the - GNU Coding Standards. - - 8. Some packages, particularly those that use Automake, provide `make - distcheck', which can by used by developers to test that all other - targets like `make install' and `make uninstall' work correctly. - This target is generally not run by end users. - -Compilers and Options -===================== - - Some systems require unusual options for compilation or linking that -the `configure' script does not know about. Run `./configure --help' -for details on some of the pertinent environment variables. - - You can give `configure' initial values for configuration parameters -by setting variables in the command line or in the environment. Here -is an example: - - ./configure CC=c99 CFLAGS=-g LIBS=-lposix - - *Note Defining Variables::, for more details. - -Compiling For Multiple Architectures -==================================== - - You can compile the package for more than one kind of computer at the -same time, by placing the object files for each architecture in their -own directory. To do this, you can use GNU `make'. `cd' to the -directory where you want the object files and executables to go and run -the `configure' script. `configure' automatically checks for the -source code in the directory that `configure' is in and in `..'. This -is known as a "VPATH" build. - - With a non-GNU `make', it is safer to compile the package for one -architecture at a time in the source code directory. After you have -installed the package for one architecture, use `make distclean' before -reconfiguring for another architecture. - - On MacOS X 10.5 and later systems, you can create libraries and -executables that work on multiple system types--known as "fat" or -"universal" binaries--by specifying multiple `-arch' options to the -compiler but only a single `-arch' option to the preprocessor. Like -this: - - ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ - CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ - CPP="gcc -E" CXXCPP="g++ -E" - - This is not guaranteed to produce working output in all cases, you -may have to build one architecture at a time and combine the results -using the `lipo' tool if you have problems. - -Installation Names -================== - - By default, `make install' installs the package's commands under -`/usr/local/bin', include files under `/usr/local/include', etc. You -can specify an installation prefix other than `/usr/local' by giving -`configure' the option `--prefix=PREFIX', where PREFIX must be an -absolute file name. - - You can specify separate installation prefixes for -architecture-specific files and architecture-independent files. If you -pass the option `--exec-prefix=PREFIX' to `configure', the package uses -PREFIX as the prefix for installing programs and libraries. -Documentation and other data files still use the regular prefix. - - In addition, if you use an unusual directory layout you can give -options like `--bindir=DIR' to specify different values for particular -kinds of files. Run `configure --help' for a list of the directories -you can set and what kinds of files go in them. In general, the -default for these options is expressed in terms of `${prefix}', so that -specifying just `--prefix' will affect all of the other directory -specifications that were not explicitly provided. - - The most portable way to affect installation locations is to pass the -correct locations to `configure'; however, many packages provide one or -both of the following shortcuts of passing variable assignments to the -`make install' command line to change installation locations without -having to reconfigure or recompile. - - The first method involves providing an override variable for each -affected directory. For example, `make install -prefix=/alternate/directory' will choose an alternate location for all -directory configuration variables that were expressed in terms of -`${prefix}'. Any directories that were specified during `configure', -but not in terms of `${prefix}', must each be overridden at install -time for the entire installation to be relocated. The approach of -makefile variable overrides for each directory variable is required by -the GNU Coding Standards, and ideally causes no recompilation. -However, some platforms have known limitations with the semantics of -shared libraries that end up requiring recompilation when using this -method, particularly noticeable in packages that use GNU Libtool. - - The second method involves providing the `DESTDIR' variable. For -example, `make install DESTDIR=/alternate/directory' will prepend -`/alternate/directory' before all installation names. The approach of -`DESTDIR' overrides is not required by the GNU Coding Standards, and -does not work on platforms that have drive letters. On the other hand, -it does better at avoiding recompilation issues, and works well even -when some directory options were not specified in terms of `${prefix}' -at `configure' time. - -Optional Features -================= - - If the package supports it, you can cause programs to be installed -with an extra prefix or suffix on their names by giving `configure' the -option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. - - Some packages pay attention to `--enable-FEATURE' options to -`configure', where FEATURE indicates an optional part of the package. -They may also pay attention to `--with-PACKAGE' options, where PACKAGE -is something like `gnu-as' or `x' (for the X Window System). The -`README' should mention any `--enable-' and `--with-' options that the -package recognizes. - - For packages that use the X Window System, `configure' can usually -find the X include and library files automatically, but if it doesn't, -you can use the `configure' options `--x-includes=DIR' and -`--x-libraries=DIR' to specify their locations. - - Some packages offer the ability to configure how verbose the -execution of `make' will be. For these packages, running `./configure ---enable-silent-rules' sets the default to minimal output, which can be -overridden with `make V=1'; while running `./configure ---disable-silent-rules' sets the default to verbose, which can be -overridden with `make V=0'. - -Particular systems -================== - - On HP-UX, the default C compiler is not ANSI C compatible. If GNU -CC is not installed, it is recommended to use the following options in -order to use an ANSI C compiler: - - ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" - -and if that doesn't work, install pre-built binaries of GCC for HP-UX. - - On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot -parse its `' header file. The option `-nodtk' can be used as -a workaround. If GNU CC is not installed, it is therefore recommended -to try - - ./configure CC="cc" - -and if that doesn't work, try - - ./configure CC="cc -nodtk" - - On Solaris, don't put `/usr/ucb' early in your `PATH'. This -directory contains several dysfunctional programs; working variants of -these programs are available in `/usr/bin'. So, if you need `/usr/ucb' -in your `PATH', put it _after_ `/usr/bin'. - - On Haiku, software installed for all users goes in `/boot/common', -not `/usr/local'. It is recommended to use the following options: - - ./configure --prefix=/boot/common - -Specifying the System Type -========================== - - There may be some features `configure' cannot figure out -automatically, but needs to determine by the type of machine the package -will run on. Usually, assuming the package is built to be run on the -_same_ architectures, `configure' can figure that out, but if it prints -a message saying it cannot guess the machine type, give it the -`--build=TYPE' option. TYPE can either be a short name for the system -type, such as `sun4', or a canonical name which has the form: - - CPU-COMPANY-SYSTEM - -where SYSTEM can have one of these forms: - - OS - KERNEL-OS - - See the file `config.sub' for the possible values of each field. If -`config.sub' isn't included in this package, then this package doesn't -need to know the machine type. - - If you are _building_ compiler tools for cross-compiling, you should -use the option `--target=TYPE' to select the type of system they will -produce code for. - - If you want to _use_ a cross compiler, that generates code for a -platform different from the build platform, you should specify the -"host" platform (i.e., that on which the generated programs will -eventually be run) with `--host=TYPE'. - -Sharing Defaults -================ - - If you want to set default values for `configure' scripts to share, -you can create a site shell script called `config.site' that gives -default values for variables like `CC', `cache_file', and `prefix'. -`configure' looks for `PREFIX/share/config.site' if it exists, then -`PREFIX/etc/config.site' if it exists. Or, you can set the -`CONFIG_SITE' environment variable to the location of the site script. -A warning: not all `configure' scripts look for a site script. - -Defining Variables -================== - - Variables not defined in a site shell script can be set in the -environment passed to `configure'. However, some packages may run -configure again during the build, and the customized values of these -variables may be lost. In order to avoid this problem, you should set -them in the `configure' command line, using `VAR=value'. For example: - - ./configure CC=/usr/local2/bin/gcc - -causes the specified `gcc' to be used as the C compiler (unless it is -overridden in the site shell script). - -Unfortunately, this technique does not work for `CONFIG_SHELL' due to -an Autoconf bug. Until the bug is fixed you can use this workaround: - - CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash - -`configure' Invocation -====================== - - `configure' recognizes the following options to control how it -operates. - -`--help' -`-h' - Print a summary of all of the options to `configure', and exit. - -`--help=short' -`--help=recursive' - Print a summary of the options unique to this package's - `configure', and exit. The `short' variant lists options used - only in the top level, while the `recursive' variant lists options - also present in any nested packages. - -`--version' -`-V' - Print the version of Autoconf used to generate the `configure' - script, and exit. - -`--cache-file=FILE' - Enable the cache: use and save the results of the tests in FILE, - traditionally `config.cache'. FILE defaults to `/dev/null' to - disable caching. - -`--config-cache' -`-C' - Alias for `--cache-file=config.cache'. - -`--quiet' -`--silent' -`-q' - Do not print messages saying which checks are being made. To - suppress all normal output, redirect it to `/dev/null' (any error - messages will still be shown). - -`--srcdir=DIR' - Look for the package's source code in directory DIR. Usually - `configure' can determine that directory automatically. - -`--prefix=DIR' - Use DIR as the installation prefix. *note Installation Names:: - for more details, including other options available for fine-tuning - the installation locations. - -`--no-create' -`-n' - Run the configure checks, but stop before creating any output - files. - -`configure' also accepts some other, not widely useful, options. Run -`configure --help' for more details. - diff --git a/Makefile.am b/Makefile.am deleted file mode 100644 index 12a0b0bd..00000000 --- a/Makefile.am +++ /dev/null @@ -1,20 +0,0 @@ -if USE_SYSTEM_SHARED_LIB -SUBDIRS = src -else -SUBDIRS = libcork libipset libbloom src -endif - -if ENABLE_DOCUMENTATION -SUBDIRS += doc -endif - -ACLOCAL_AMFLAGS = -I m4 - -pkgconfiglibdir = $(libdir)/pkgconfig -pkgconfiglib_DATA = shadowsocks-libev.pc - -EXTRA_DIST = acl Changes completions debian docker rpm scripts README.md -EXTRA_DIST += libbloom -EXTRA_DIST += libcork/include libipset/include -EXTRA_DIST += libipset/src/libipset/map/inspection-template.c.in -EXTRA_DIST += libipset/src/libipset/set/inspection-template.c.in diff --git a/README.md b/README.md index 8a874db8..327a9592 100644 --- a/README.md +++ b/README.md @@ -79,8 +79,8 @@ git submodule update --init --recursive ### Pre-build configure guide -For a complete list of available configure-time option, -try `configure --help`. +For a complete list of available configure-time options, +try `cmake -LH` from a build directory. ### Debian & Ubuntu @@ -203,12 +203,12 @@ nix-env -iA nixpkgs.shadowsocks-libev In general, you need the following build dependencies: -* autotools (autoconf, automake, libtool) -* gettext +* cmake (>= 3.2) +* a C compiler (gcc or clang) * pkg-config * libmbedtls -* libsodium -* libpcre3 (old pcre library) +* libsodium (>= 1.0.4) +* libpcre2 * libev * libc-ares * asciidoc (for documentation only) @@ -218,18 +218,18 @@ Notes: Fedora 26 libsodium version >= 1.0.12, so you can install via dnf instal If your system is too old to provide libmbedtls and libsodium (later than **v1.0.8**), you will need to either install those libraries manually or upgrade your system. -If your system provides with those libraries, you **should not** install them from source.You should jump to this section and install them from the distribution repository instead. +If your system provides with those libraries, you **should not** install them from source. You should jump to this section and install them from the distribution repository instead. For some of the distributions, you might install build dependencies like this: ```bash # Installation of basic build dependencies ## 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 build-essential cmake libpcre2-dev asciidoc xmlto libev-dev libc-ares-dev 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 gcc cmake make asciidoc xmlto c-ares-devel libev-devel ## Arch -sudo pacman -S gettext gcc autoconf libtool automake make asciidoc xmlto c-ares libev +sudo pacman -S gcc cmake make asciidoc xmlto c-ares libev # Installation of libsodium export LIBSODIUM_VER=1.0.16 @@ -252,10 +252,20 @@ popd sudo ldconfig # Start building -./autogen.sh && ./configure && make +git submodule update --init --recursive +mkdir -p build && cd build +cmake .. +make sudo make install ``` +To run unit tests: + +```bash +cd build +ctest --output-on-failure +``` + You may need to manually install missing softwares. ### FreeBSD diff --git a/autogen.sh b/autogen.sh deleted file mode 100755 index 01db361f..00000000 --- a/autogen.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -autoreconf --install --force diff --git a/build/.gitkeep b/build/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/cmake/FindCares.cmake b/cmake/FindCares.cmake new file mode 100644 index 00000000..07c24861 --- /dev/null +++ b/cmake/FindCares.cmake @@ -0,0 +1,44 @@ +# FindCares.cmake - Find c-ares library +# +# Sets: +# CARES_FOUND +# CARES_INCLUDE_DIRS +# CARES_LIBRARIES + +find_path(CARES_INCLUDE_DIR + NAMES ares.h + HINTS + /opt/homebrew/include + /usr/local/include + /opt/homebrew/opt/c-ares/include + /usr/local/opt/c-ares/include + /usr/include +) + +find_library(CARES_LIBRARY + NAMES cares + HINTS + /opt/homebrew/lib + /usr/local/lib + /opt/homebrew/opt/c-ares/lib + /usr/local/opt/c-ares/lib + /usr/lib +) + +if(CARES_INCLUDE_DIR AND CARES_LIBRARY) + set(CARES_FOUND TRUE) + set(CARES_INCLUDE_DIRS ${CARES_INCLUDE_DIR}) + set(CARES_LIBRARIES ${CARES_LIBRARY}) + + # Verify ares_library_init exists + include(CheckLibraryExists) + check_library_exists(cares ares_library_init "" CARES_HAS_INIT) + if(NOT CARES_HAS_INIT) + message(WARNING "c-ares found but ares_library_init not detected. Proceeding anyway.") + endif() + + message(STATUS "Found c-ares: ${CARES_LIBRARY}") +else() + set(CARES_FOUND FALSE) + message(FATAL_ERROR "Could not find c-ares library. Install libc-ares-dev or equivalent.") +endif() diff --git a/cmake/FindMbedTLS.cmake b/cmake/FindMbedTLS.cmake new file mode 100644 index 00000000..6b832b62 --- /dev/null +++ b/cmake/FindMbedTLS.cmake @@ -0,0 +1,88 @@ +# FindMbedTLS.cmake - Find mbedTLS library with feature detection +# +# Sets: +# MBEDTLS_FOUND +# MBEDTLS_INCLUDE_DIRS +# MBEDTLS_CRYPTO_LIBRARY +# MBEDTLS_TLS_LIBRARY + +include(CheckCSourceCompiles) + +# mbedtls@3 is keg-only on Homebrew; also check versioned opt paths +find_path(MBEDTLS_INCLUDE_DIR + NAMES mbedtls/cipher.h + HINTS + /opt/homebrew/opt/mbedtls@3/include + /usr/local/opt/mbedtls@3/include + /opt/homebrew/opt/mbedtls/include + /usr/local/opt/mbedtls/include + /opt/homebrew/include + /usr/local/include + /usr/include +) + +find_library(MBEDTLS_CRYPTO_LIBRARY + NAMES mbedcrypto + HINTS + /opt/homebrew/opt/mbedtls@3/lib + /usr/local/opt/mbedtls@3/lib + /opt/homebrew/opt/mbedtls/lib + /usr/local/opt/mbedtls/lib + /opt/homebrew/lib + /usr/local/lib + /usr/lib +) + +find_library(MBEDTLS_TLS_LIBRARY + NAMES mbedtls + HINTS + /opt/homebrew/opt/mbedtls@3/lib + /usr/local/opt/mbedtls@3/lib + /opt/homebrew/opt/mbedtls/lib + /usr/local/opt/mbedtls/lib + /opt/homebrew/lib + /usr/local/lib + /usr/lib +) + +if(MBEDTLS_INCLUDE_DIR AND MBEDTLS_CRYPTO_LIBRARY) + set(MBEDTLS_FOUND TRUE) + set(MBEDTLS_INCLUDE_DIRS ${MBEDTLS_INCLUDE_DIR}) + + # Check for required CFB mode support + set(CMAKE_REQUIRED_INCLUDES ${MBEDTLS_INCLUDE_DIR}) + set(CMAKE_REQUIRED_LIBRARIES ${MBEDTLS_CRYPTO_LIBRARY}) + + check_c_source_compiles(" + #include + #if !defined(MBEDTLS_CIPHER_MODE_CFB) + #error CFB mode not supported + #endif + int main(void) { return 0; } + " MBEDTLS_HAS_CFB) + + if(NOT MBEDTLS_HAS_CFB) + # Try mbedtls 3.x config path + check_c_source_compiles(" + #include + #include + #if !defined(MBEDTLS_CIPHER_MODE_CFB) + #error CFB mode not supported + #endif + int main(void) { return 0; } + " MBEDTLS_HAS_CFB_V3) + + if(NOT MBEDTLS_HAS_CFB_V3) + message(FATAL_ERROR "mbedTLS found but MBEDTLS_CIPHER_MODE_CFB is not enabled. " + "Please enable CFB mode in your mbedTLS configuration.") + endif() + endif() + + unset(CMAKE_REQUIRED_INCLUDES) + unset(CMAKE_REQUIRED_LIBRARIES) + + message(STATUS "Found mbedTLS: ${MBEDTLS_CRYPTO_LIBRARY}") +else() + set(MBEDTLS_FOUND FALSE) + message(FATAL_ERROR "Could not find mbedTLS library. Install libmbedtls-dev or equivalent.") +endif() diff --git a/cmake/FindPCRE2.cmake b/cmake/FindPCRE2.cmake new file mode 100644 index 00000000..a89e9354 --- /dev/null +++ b/cmake/FindPCRE2.cmake @@ -0,0 +1,63 @@ +# FindPCRE2.cmake - Find PCRE2 library (8-bit) +# +# Sets: +# PCRE2_FOUND +# PCRE2_INCLUDE_DIRS +# PCRE2_LIBRARIES + +include(FindPkgConfig) + +if(PKG_CONFIG_FOUND) + pkg_check_modules(_PCRE2 QUIET libpcre2-8) +endif() + +if(_PCRE2_FOUND) + set(PCRE2_INCLUDE_DIRS ${_PCRE2_INCLUDE_DIRS}) + set(PCRE2_LIBRARIES ${_PCRE2_LIBRARIES}) + set(PCRE2_FOUND TRUE) +else() + # Try pcre2-config + find_program(PCRE2_CONFIG pcre2-config) + if(PCRE2_CONFIG) + execute_process(COMMAND ${PCRE2_CONFIG} --cflags + OUTPUT_VARIABLE PCRE2_CFLAGS + OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND ${PCRE2_CONFIG} --libs8 + OUTPUT_VARIABLE PCRE2_LDFLAGS + OUTPUT_STRIP_TRAILING_WHITESPACE) + string(REGEX REPLACE "-I" "" PCRE2_INCLUDE_DIRS "${PCRE2_CFLAGS}") + set(PCRE2_LIBRARIES ${PCRE2_LDFLAGS}) + set(PCRE2_FOUND TRUE) + else() + # Manual search + find_path(PCRE2_INCLUDE_DIR + NAMES pcre2.h + HINTS + /opt/homebrew/include + /usr/local/include + /usr/include + ) + + find_library(PCRE2_LIBRARY + NAMES pcre2-8 + HINTS + /opt/homebrew/lib + /usr/local/lib + /usr/lib + ) + + if(PCRE2_INCLUDE_DIR AND PCRE2_LIBRARY) + set(PCRE2_INCLUDE_DIRS ${PCRE2_INCLUDE_DIR}) + set(PCRE2_LIBRARIES ${PCRE2_LIBRARY}) + set(PCRE2_FOUND TRUE) + else() + set(PCRE2_FOUND FALSE) + endif() + endif() +endif() + +if(PCRE2_FOUND) + message(STATUS "Found PCRE2: ${PCRE2_LIBRARIES}") +else() + message(FATAL_ERROR "Could not find PCRE2 library. Install libpcre2-dev or equivalent.") +endif() diff --git a/cmake/FindSodium.cmake b/cmake/FindSodium.cmake new file mode 100644 index 00000000..19013bc5 --- /dev/null +++ b/cmake/FindSodium.cmake @@ -0,0 +1,57 @@ +# FindSodium.cmake - Find libsodium with version check +# +# Sets: +# SODIUM_FOUND +# SODIUM_INCLUDE_DIRS +# SODIUM_LIBRARIES + +find_path(SODIUM_INCLUDE_DIR + NAMES sodium.h + HINTS + /opt/homebrew/include + /usr/local/include + /usr/local/opt/libsodium/include + /opt/homebrew/opt/libsodium/include + /usr/include + $ENV{LIBSODIUM_INCLUDE_DIR} + $ENV{LIBSODIUM_DIR}/include +) + +find_library(SODIUM_LIBRARY + NAMES sodium + HINTS + /opt/homebrew/lib + /usr/local/lib + /usr/local/opt/libsodium/lib + /opt/homebrew/opt/libsodium/lib + /usr/lib +) + +if(SODIUM_INCLUDE_DIR AND SODIUM_LIBRARY) + set(SODIUM_FOUND TRUE) + set(SODIUM_INCLUDE_DIRS ${SODIUM_INCLUDE_DIR}) + set(SODIUM_LIBRARIES ${SODIUM_LIBRARY}) + + # Version check: require SODIUM_LIBRARY_VERSION_MAJOR >= 7 (libsodium >= 1.0.4) + include(CheckCSourceCompiles) + set(CMAKE_REQUIRED_INCLUDES ${SODIUM_INCLUDE_DIR}) + set(CMAKE_REQUIRED_LIBRARIES ${SODIUM_LIBRARY}) + check_c_source_compiles(" + #include + #if SODIUM_LIBRARY_VERSION_MAJOR < 7 + #error libsodium too old + #endif + int main(void) { return 0; } + " SODIUM_VERSION_OK) + unset(CMAKE_REQUIRED_INCLUDES) + unset(CMAKE_REQUIRED_LIBRARIES) + + if(NOT SODIUM_VERSION_OK) + message(FATAL_ERROR "libsodium found but version is too old. Require >= 1.0.4 (SODIUM_LIBRARY_VERSION_MAJOR >= 7)") + endif() + + message(STATUS "Found libsodium: ${SODIUM_LIBRARY}") +else() + set(SODIUM_FOUND FALSE) + message(FATAL_ERROR "Could not find libsodium. Install libsodium-dev or equivalent.") +endif() diff --git a/cmake/config.h.cmake b/cmake/config.h.cmake index 3a627f7c..82216519 100644 --- a/cmake/config.h.cmake +++ b/cmake/config.h.cmake @@ -87,11 +87,8 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_NET_IF_H 1 -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_PCRE_H 1 - -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_PCRE_PCRE_H 1 +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_PCRE2_H 1 /* Have PTHREAD_PRIO_INHERIT. */ #cmakedefine HAVE_PTHREAD_PRIO_INHERIT 1 @@ -236,6 +233,21 @@ #endif +/* Enable support for QOS netfilter mark preservation */ +#cmakedefine USE_NFCONNTRACK_TOS 1 + +/* Enable support for nftables firewall */ +#cmakedefine USE_NFTABLES 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_LINUX_RANDOM_H 1 + +/* Define to 1 if you have the `get_current_dir_name' function. */ +#cmakedefine HAVE_GET_CURRENT_DIR_NAME 1 + +/* Define to 1 if you have the `posix_memalign' function. */ +#cmakedefine HAVE_POSIX_MEMALIGN 1 + /* Define if use system shared lib. */ #cmakedefine USE_SYSTEM_SHARED_LIB 1 diff --git a/cmake/configure.cmake b/cmake/configure.cmake index 17049a98..2c2d915f 100644 --- a/cmake/configure.cmake +++ b/cmake/configure.cmake @@ -2,9 +2,6 @@ # ------------------------------------------------------------- # config.h -# If we generate config.h by automake -#include_directories(.) - # Use cmake to generate config.h include(CheckIncludeFiles) include(CheckFunctionExists) @@ -12,14 +9,20 @@ include(CheckSymbolExists) include(CheckLibraryExists) include(CheckTypeSize) include(CheckCSourceCompiles) +include(CheckCCompilerFlag) # Define if building universal (internal helper macro) # AC_APPLE_UNIVERSAL_BUILD -set(CONNECT_IN_PROGRESS "EINPROGRESS") -set(CONNECT_IN_PROGRESS "EINPROGRESS" CACHE STRING "") + +# Set CONNECT_IN_PROGRESS based on platform +if(MINGW) + set(CONNECT_IN_PROGRESS "WSAEWOULDBLOCK") +else() + set(CONNECT_IN_PROGRESS "EINPROGRESS") +endif() if (CMAKE_SYSTEM_NAME STREQUAL Darwin) - set(CMAKE_REQUIRED_INCLUDES "/usr/local/include" "/usr/include") + set(CMAKE_REQUIRED_INCLUDES "/usr/local/include" "/usr/include" "/opt/homebrew/include") endif () check_include_files(dlfcn.h HAVE_DLFCN_H) @@ -53,8 +56,10 @@ else () endif () check_include_files(linux/tcp.h HAVE_LINUX_TCP_H) check_include_files(net/if.h HAVE_NET_IF_H) -check_include_files(pcre.h HAVE_PCRE_H) -check_include_files(pcre/pcre.h HAVE_PCRE_PCRE_H) +set(CMAKE_REQUIRED_DEFINITIONS_SAVE ${CMAKE_REQUIRED_DEFINITIONS}) +set(CMAKE_REQUIRED_DEFINITIONS "-DPCRE2_CODE_UNIT_WIDTH=8") +check_include_files(pcre2.h HAVE_PCRE2_H) +set(CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS_SAVE}) check_symbol_exists(PTHREAD_PRIO_INHERIT pthread.h HAVE_PTHREAD_PRIO_INHERIT) check_function_exists(select HAVE_SELECT) @@ -78,6 +83,8 @@ check_include_files(sys/types.h HAVE_SYS_TYPES_H) check_include_files(sys/wait.h HAVE_SYS_WAIT_H) check_include_files(ares.h HAVE_ARES_H) check_include_files(unistd.h HAVE_UNISTD_H) +check_include_files(arpa/inet.h HAVE_ARPA_INET_H) +check_include_files(linux/random.h HAVE_LINUX_RANDOM_H) check_function_exists(fork HAVE_FORK) check_function_exists(vfork HAVE_VFORK) @@ -89,6 +96,9 @@ if (HAVE_FORK) set(HAVE_WORKING_FORK 1) endif () +# Additional function checks +check_function_exists(get_current_dir_name HAVE_GET_CURRENT_DIR_NAME) +check_function_exists(posix_memalign HAVE_POSIX_MEMALIGN) # Define to the sub-directory where libtool stores uninstalled libraries. set(LT_OBJDIR ".libs/") @@ -101,8 +111,6 @@ set(PACKAGE_STRING "${PROJECT_NAME} ${PACKAGE_VERSION}") set(PACKAGE_TARNAME ${PROJECT_NAME}) set(PACKAGE_URL "") -#message(${PACKAGE_NAME} - v${PACKAGE_VERSION} - v${PROJECT_VERSION}) - # PTHREAD_CREATE_JOINABLE # Define as the return type of signal handlers (`int' or `void'). @@ -140,31 +148,7 @@ set(_TANDEM_SOURCE 1) set(__EXTENSIONS__ 1) # USE_SYSTEM_SHARED_LIB set(VERSION ${PACKAGE_VERSION}) -# TODO WORDS_BIGENDIAN -# _MINIX -# _POSIX_1_SOURCE -# _POSIX_SOURCE -# _UINT8_T -# Define to empty if `const' does not conform to ANSI C. -# undef const - -# Define to `__inline__' or `__inline' if that's what the C compiler -# calls it, or to nothing if 'inline' is not supported under any name. -#ifndef __cplusplus -#undef inline -#endif -# TODO Assume we got inline support -# https://cmake.org/Wiki/CMakeTestInline - -# Define to `int' if does not define. -# undef pid_t -# Define to the type of an unsigned integer type of width exactly 16 bits if -# such a type exists and the standard includes do not define it. -# undef uint16_t -# Define to the type of an unsigned integer type of width exactly 8 bits if -# such a type exists and the standard includes do not define it. -# undef uint8_t set(CMAKE_EXTRA_INCLUDE_FILES sys/types.h) check_type_size(pid_t PID_T) check_type_size(size_t SIZE_T) @@ -197,3 +181,18 @@ endif () if (NOT HAVE_WORKING_VFORK) set(vfork fork) endif () + +# Stack protector detection +option(DISABLE_SSP "Disable -fstack-protector" OFF) +if(NOT DISABLE_SSP) + check_c_compiler_flag(-fstack-protector HAS_STACK_PROTECTOR) + if(HAS_STACK_PROTECTOR) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector") + message(STATUS "Stack protector enabled") + endif() +endif() + +# MinGW/Cygwin compiler flags +if(MINGW OR CYGWIN) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mno-ms-bitfields") +endif() diff --git a/cmake/shadowsocks-libev.pc.cmake b/cmake/shadowsocks-libev.pc.cmake index a8f6f1bd..2ddd75ef 100644 --- a/cmake/shadowsocks-libev.pc.cmake +++ b/cmake/shadowsocks-libev.pc.cmake @@ -1,9 +1,7 @@ -prefix=@prefix@ -exec_prefix=${prefix}/@CMAKE_INSTALL_BINDIR@ -libdir=${exec_prefix}/@CMAKE_INSTALL_FULL_LIBDIR@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@ includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ -sharedir=${prefix}/@CMAKE_INSTALL_DATAROOTDIR@ -mandir=${prefix}/@CMAKE_INSTALL_MANDIR@ Name: @PROJECT_NAME@ Description: @PROJECT_DESC@ @@ -11,4 +9,4 @@ URL: @PROJECT_URL@ Version: @PROJECT_VERSION@ Requires: Cflags: -I${includedir} -Libs: -L${libdir} -lshadowsocks-libev -lcrypto +Libs: -L${libdir} -lshadowsocks-libev diff --git a/configure.ac b/configure.ac deleted file mode 100755 index 134ea5f6..00000000 --- a/configure.ac +++ /dev/null @@ -1,311 +0,0 @@ -dnl -*- Autoconf -*- -dnl Process this file with autoconf to produce a configure script. - -AC_PREREQ([2.67]) -AC_INIT([shadowsocks-libev], [3.3.5], [max.c.lv@gmail.com]) -AC_CONFIG_SRCDIR([src/crypto.c]) -AC_CONFIG_HEADERS([config.h]) -AC_CONFIG_AUX_DIR(auto) -AC_CONFIG_MACRO_DIR([m4]) -AC_USE_SYSTEM_EXTENSIONS - -AM_INIT_AUTOMAKE([subdir-objects foreign -Wno-gnu -Werror]) -m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) -m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) -AM_MAINTAINER_MODE -AM_DEP_TRACK - -dnl Checks for lib -AC_DISABLE_STATIC -AC_DISABLE_SHARED -LT_INIT([dlopen]) - -dnl Check for pcre library -TS_CHECK_PCRE -if test "x${enable_pcre}" != "xyes"; then - AC_MSG_ERROR([Cannot find pcre library. Configure --with-pcre=DIR]) -fi - -dnl Checks for using shared libraries from system -AC_ARG_ENABLE( - [system-shared-lib], - AS_HELP_STRING([--enable-system-shared-lib], [build against shared libraries when possible]), - [ - case "${enableval}" in - yes) enable_system_shared_lib=true ;; - no) enable_system_shared_lib=false ;; - *) AC_MSG_ERROR([bad value ${enableval} for --enable-system-shared-lib]) ;; - esac], [enable_system_shared_lib=false]) -AM_CONDITIONAL([USE_SYSTEM_SHARED_LIB], [test x$enable_system_shared_lib = xtrue]) - -AC_ARG_ENABLE([documentation], - AS_HELP_STRING([--disable-documentation], [do not build documentation]), - [disable_documentation=true], - [disable_documentation=false]) -AM_CONDITIONAL([ENABLE_DOCUMENTATION], [test x$disable_documentation = xfalse]) - -AM_COND_IF([ENABLE_DOCUMENTATION], [ - AC_PATH_PROG([ASCIIDOC], [asciidoc]) - test x"${ASCIIDOC}" != x || AC_MSG_ERROR([Cannot find `asciidoc` in PATH.]) - AC_PATH_PROG([XMLTO], [xmlto]) - test x"$XMLTO" != x || AC_MSG_ERROR([Cannot find `xmlto` in PATH.]) - AC_PATH_PROG([GZIP], [gzip], [gzip]) - AC_PATH_PROG([MV], [mv], [mv]) - AC_PROG_SED -]) - -dnl Checks for programs. -AC_PROG_CC -AM_PROG_CC_C_O -AC_PROG_INSTALL -AC_PROG_LN_S -AC_PROG_MAKE_SET -AC_LANG_SOURCE - -dnl Add library for mingw -case $host in - *-mingw*) - CFLAGS="$CFLAGS -mno-ms-bitfields" - LIBS="$LIBS -lws2_32 -liphlpapi" - ;; - *-cygwin*) - CFLAGS="$CFLAGS -mno-ms-bitfields" - ;; - *) - ;; -esac - -dnl Checks for TLS -AX_TLS([:], [:]) - -dnl Checks for crypto libraries -ss_MBEDTLS -ss_SODIUM -ss_CARES - -dnl Checks for inet_ntop -ss_FUNC_INET_NTOP - -dnl Checks for host. -AC_MSG_CHECKING(for what kind of host) -case $host in - *-linux*) - os_support=linux - AC_MSG_RESULT(Linux) - ;; - *-mingw*) - os_support=mingw - AC_MSG_RESULT(MinGW) - ;; - *) - AC_MSG_RESULT(transparent proxy does not support for $host) - ;; -esac - -dnl Checks for pthread -AX_PTHREAD([LIBS="$PTHREAD_LIBS $LIBS" - CFLAGS="$CFLAGS $PTHREAD_CFLAGS" - CC="$PTHREAD_CC"], AC_MSG_ERROR(Can not find pthreads. This is required.)) - -dnl Checks for stack protector -GGL_CHECK_STACK_PROTECTOR([has_stack_protector=yes], [has_stack_protector=no]) -# XXX - disable -fstack-protector due to missing libssp_nonshared -case "$host_os" in - *aix*) - AC_MSG_NOTICE([-fstack-protector disabled on AIX]) - has_stack_protector=no - ;; - *sunos*) - AC_MSG_NOTICE([-fstack-protector disabled on SunOS]) - has_stack_protector=no - ;; - *solaris*) - AC_MSG_NOTICE([-fstack-protector disabled on Solaris]) - has_stack_protector=no - ;; -esac - -AC_ARG_ENABLE(ssp, -[AS_HELP_STRING(--disable-ssp,Do not compile with -fstack-protector)], -[ - enable_ssp="no" -], -[ - enable_ssp="yes" -]) - -if test x$has_stack_protector = xyes && test x$enable_ssp = xyes; then - CFLAGS="$CFLAGS -fstack-protector" - AC_MSG_NOTICE([-fstack-protector enabled in CFLAGS]) -fi - -AM_CONDITIONAL(BUILD_REDIRECTOR, test "$os_support" = "linux") -AM_CONDITIONAL(BUILD_WINCOMPAT, test "$os_support" = "mingw") - -dnl Checks for header files. -AC_CHECK_HEADERS([limits.h stdint.h inttypes.h arpa/inet.h fcntl.h langinfo.h locale.h linux/tcp.h netinet/tcp.h netdb.h netinet/in.h stdlib.h string.h strings.h unistd.h sys/ioctl.h linux/random.h]) - -dnl A special check required for on Darwin. See -dnl http://www.gnu.org/software/autoconf/manual/html_node/Header-Portability.html. -AC_CHECK_HEADERS([sys/socket.h]) -AC_CHECK_HEADERS([net/if.h], [], [], -[ -#include -#ifdef STDC_HEADERS -# include -# include -#else -# ifdef HAVE_STDLIB_H -# include -# endif -#endif -#ifdef HAVE_SYS_SOCKET_H -# include -#endif -]) - -case $host in - *-mingw*) - AC_DEFINE([CONNECT_IN_PROGRESS], [WSAEWOULDBLOCK], [errno for incomplete non-blocking connect(2)]) - AC_CHECK_HEADERS([winsock2.h ws2tcpip.h mswsock.h], [], [AC_MSG_ERROR([Missing MinGW headers])], []) - ;; - *-linux*) - AC_DEFINE([CONNECT_IN_PROGRESS], [EINPROGRESS], [errno for incomplete non-blocking connect(2)]) - dnl Checks for netfilter headers - AC_CHECK_HEADERS([linux/if.h linux/netfilter_ipv4.h linux/netfilter_ipv6/ip6_tables.h], - [], [AC_MSG_ERROR([Missing netfilter headers])], - [[ - #if HAVE_LIMITS_H - #include - #endif - /* Netfilter ip(6)tables v1.4.0 has broken headers */ - #if HAVE_NETINET_IN_H - #include - #endif - #if HAVE_LINUX_IF_H - #include - #endif - #if HAVE_SYS_SOCKET_H - #include - #endif - ]]) - ;; - *) - # These are POSIX-like systems using BSD-like sockets API. - AC_DEFINE([CONNECT_IN_PROGRESS], [EINPROGRESS], [errno for incomplete non-blocking connect(2)]) - ;; -esac - -AC_C_BIGENDIAN - -dnl Checks for typedefs, structures, and compiler characteristics. -AC_C_INLINE -AC_TYPE_SSIZE_T - -dnl Checks for header files. -AC_HEADER_ASSERT -AC_HEADER_STDC -AC_HEADER_SYS_WAIT - -dnl Checks for typedefs, structures, and compiler characteristics. -AC_C_CONST -AC_TYPE_PID_T -AC_TYPE_SIZE_T -AC_TYPE_SSIZE_T -AC_TYPE_UINT16_T -AC_TYPE_UINT8_T -AC_HEADER_TIME - -dnl Checks for library functions. -AC_FUNC_FORK -AC_FUNC_SELECT_ARGTYPES -AC_TYPE_SIGNAL -AC_CHECK_FUNCS([memset select setresuid setreuid strerror get_current_dir_name getpwnam_r setrlimit]) - -AC_CHECK_LIB(socket, connect) - -dnl Checks for library functions. -AC_CHECK_FUNCS([malloc memset posix_memalign socket]) - -AC_ARG_WITH(ev, - AS_HELP_STRING([--with-ev=DIR], [use a specific libev library]), - [CFLAGS="$CFLAGS -I$withval/include" - CPPFLAGS="$CPPFLAGS -I$withval/include" - LDFLAGS="$LDFLAGS -L$withval/lib"] -) -AC_CHECK_HEADERS([ev.h libev/ev.h], [], []) -AC_CHECK_LIB([ev], [ev_loop_destroy], [LIBS="-lev $LIBS"], [AC_MSG_ERROR([Couldn't find libev. Try installing libev-dev@<:@el@:>@.])]) - -AC_CONFIG_FILES([shadowsocks-libev.pc - Makefile - doc/Makefile - src/Makefile]) - -AM_COND_IF([USE_SYSTEM_SHARED_LIB], - [AC_DEFINE([USE_SYSTEM_SHARED_LIB], [1], [Define if use system shared lib.])], - [AC_CONFIG_FILES([libbloom/Makefile libcork/Makefile libipset/Makefile])]) - -AC_ARG_ENABLE(connmarktos, -[AS_HELP_STRING(--enable-connmarktos, Enable saved connmark to IP TOS QoS feature)], -[ enable_connmarktos="$enableval" ]) - -if test x"$enable_connmarktos" = "xyes" ; then - AC_MSG_NOTICE([Linux Netfilter Conntrack support requested by --enable-connmarktos: ${enable_connmarktos}]) - if test "x$enable_connmarktos" != "xno"; then - PKG_CHECK_MODULES([NETFILTER_CONNTRACK], [libnetfilter_conntrack],, - [AC_SEARCH_LIBS([nfct_query], [netfilter_conntrack],,[ - if test x"$enable_connmarktos" = "xyes"; then - AC_MSG_ERROR([--enable-connmarktos specified but libnetfilter-conntrack library not found]) - fi - with_netfilter_conntrack=no], [-lnfnetlink]) - AC_CHECK_HEADERS([libnetfilter_conntrack/libnetfilter_conntrack.h \ - libnetfilter_conntrack/libnetfilter_conntrack_tcp.h],,[ - if test x"$enable_connmarktos" = "xyes"; then - AC_MSG_ERROR([--enable-connmarktos specified but libnetfilter-conntrack headers not found]) - fi - with_netfilter_conntrack=no])]) - # If nothing is broken; enable the libraries usage. - if test "x$with_netfilter_conntrack" != "xno"; then - with_netfilter_conntrack=yes - AC_DEFINE(USE_NFCONNTRACK_TOS, 1, [Enable support for QOS netfilter mark preservation]) - fi - fi -fi - -AC_ARG_ENABLE(nftables, -[AS_HELP_STRING(--enable-nftables, report malicious IP to nftables)], -[ enable_nftables="$enableval" ]) - -if test x"$enable_nftables" = "xyes" ; then - AC_MSG_NOTICE([Linux nftables support requested by --enable-nftables: ${enable_nftables}]) - if test "x$enable_nftables" != "xno"; then - PKG_CHECK_MODULES([NFTABLES], [libmnl libnftnl],, - [AC_SEARCH_LIBS([mnl_socket_open], [mnl],,[ - if test x"$enable_nftables" = "xyes"; then - AC_MSG_ERROR([--enable-nftables specified but libmnl library not found]) - fi - with_nftables=no], [-lmnl]) - AC_CHECK_HEADERS([libmnl/libmnl.h],,[ - if test x"$enable_nftables" = "xyes"; then - AC_MSG_ERROR([--enable-nftables specified but libmnl headers not found]) - fi - with_nftables=no]) - AC_SEARCH_LIBS([nftnl_nlmsg_build_hdr], [nftnl],,[ - if test x"$enable_nftables" = "xyes"; then - AC_MSG_ERROR([--enable-nftables specified but libnftnl library not found]) - fi - with_nftables=no], [-lnftnl]) - AC_CHECK_HEADERS([libnftnl/common.h],,[ - if test x"$enable_nftables" = "xyes"; then - AC_MSG_ERROR([--enable-nftables specified but libnftnl headers not found]) - fi - with_nftables=no])]) - # If nothing is broken; enable the libraries usage. - if test "x$with_nftables" != "xno"; then - with_nftables=yes - AC_DEFINE(USE_NFTABLES, 1, [Enable support for nftables firewall]) - fi - fi -fi - -AC_OUTPUT diff --git a/doc/Makefile.am b/doc/Makefile.am deleted file mode 100644 index e2cfbf6d..00000000 --- a/doc/Makefile.am +++ /dev/null @@ -1,60 +0,0 @@ -ASCIIDOC = @ASCIIDOC@ -ASCIIDOC_EXTRA = -MANPAGE_XSL = $(srcdir)/manpage-normal.xsl -XMLTO = @XMLTO@ -XMLTO_EXTRA = -m $(srcdir)/manpage-bold-literal.xsl -GZIPCMD = @GZIP@ -INSTALL = @INSTALL@ -RM = @RM@ -MV = @MV@ -SED = @SED@ -VERSION = `$(SED) -n 's/.*PACKAGE_VERSION "\(.*\)"/\1/p'\ - ../config.h` - -# Guard against environment variables -MAN1_DOC = -MAN1_DOC += ss-local.1 -MAN1_DOC += ss-manager.1 -MAN1_DOC += ss-nat.1 -MAN1_DOC += ss-redir.1 -MAN1_DOC += ss-server.1 -MAN1_DOC += ss-tunnel.1 - -MAN8_DOC = -MAN8_DOC += shadowsocks-libev.8 - -MAN8_XML = $(MAN8_DOC:%.8=%.xml) -MAN1_XML = $(MAN1_DOC:%.1=%.xml) -MAN_XML = $(MAN8_XML) $(MAN1_XML) - -MAN8_HTML = $(MAN8_DOC:%.8=%.html) -MAN1_HTML = $(MAN1_DOC:%.1=%.html) -MAN_HTML = $(MAN8_HTML) $(MAN1_HTML) - -MAN8_TXT = $(MAN8_DOC:%.8=%.asciidoc) -MAN1_TXT = $(MAN1_DOC:%.1=%.asciidoc) -MAN_TXT = $(MAN8_TXT) $(MAN1_TXT) - -man_MANS = $(MAN8_DOC) $(MAN1_DOC) - -html-local: $(MAN_HTML) - -%.1: %.xml - $(AM_V_GEN)$(XMLTO) -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $< - -%.8: %.xml - $(AM_V_GEN)$(XMLTO) -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $< - -%.xml: %.asciidoc - $(AM_V_GEN)$(ASCIIDOC) -b docbook -d manpage -f $(srcdir)/asciidoc.conf \ - -aversion=$(VERSION) $(ASCIIDOC_EXTRA) -o $@ $< - -%.html: %.asciidoc - $(AM_V_GEN)$(ASCIIDOC) -b html4 -d article -f $(srcdir)/asciidoc.conf \ - -aversion=$(VERSION) $(ASCIIDOC_EXTRA) -o $@ $< - -doc_DATA = $(MAN_HTML) - -CLEANFILES = $(MAN_XML) $(man_MANS) $(MAN_HTML) - -EXTRA_DIST = *.asciidoc asciidoc.conf *.xsl diff --git a/doc/shadowsocks-libev.asciidoc b/doc/shadowsocks-libev.asciidoc index 4a283dd6..4cb0f50c 100644 --- a/doc/shadowsocks-libev.asciidoc +++ b/doc/shadowsocks-libev.asciidoc @@ -151,6 +151,26 @@ Only available with Linux kernel > 3.9.0. --no-delay:: Enable TCP_NODELAY. +--tcp-incoming-sndbuf :: +Set TCP send buffer size for incoming connections. ++ +Not available in manager mode. + +--tcp-incoming-rcvbuf :: +Set TCP receive buffer size for incoming connections. ++ +Not available in manager mode. + +--tcp-outgoing-sndbuf :: +Set TCP send buffer size for outgoing connections. ++ +Not available in manager mode. + +--tcp-outgoing-rcvbuf :: +Set TCP receive buffer size for outgoing connections. ++ +Not available in manager mode. + --acl :: Enable ACL (Access Control List) and specify config file. + @@ -166,6 +186,17 @@ Specify the executable path of `ss-server`. + Only available in manager mode. +-D :: +--workdir :: +Specify the working directory of ss-manager. ++ +Only available in manager mode. + +--nftables-sets :: +Specify nftables sets for reporting malicious IPs. ++ +Only available in server mode, when built with nftables support. + -v:: Enable verbose mode. diff --git a/doc/ss-local.asciidoc b/doc/ss-local.asciidoc index 46ce8c82..19ba7002 100644 --- a/doc/ss-local.asciidoc +++ b/doc/ss-local.asciidoc @@ -104,7 +104,7 @@ Enable UDP relay. Enable UDP relay and disable TCP relay. -6:: -Resovle hostname to IPv6 address first. +Resolve hostname to IPv6 address first. --fast-open:: Enable TCP fast open. @@ -130,6 +130,18 @@ Only available with MPTCP enabled Linux kernel. --no-delay:: Enable TCP_NODELAY. +--tcp-incoming-sndbuf :: +Set TCP send buffer size for incoming connections. + +--tcp-incoming-rcvbuf :: +Set TCP receive buffer size for incoming connections. + +--tcp-outgoing-sndbuf :: +Set TCP send buffer size for outgoing connections. + +--tcp-outgoing-rcvbuf :: +Set TCP receive buffer size for outgoing connections. + --plugin :: Enable SIP003 plugin. (Experimental) diff --git a/doc/ss-manager.asciidoc b/doc/ss-manager.asciidoc index 0c127570..632789e7 100644 --- a/doc/ss-manager.asciidoc +++ b/doc/ss-manager.asciidoc @@ -117,7 +117,8 @@ Specify the executable path of ss-server. + Only available in manager mode. ---executable :: +-D :: +--workdir :: Specify the working directory of ss-manager. + Only available in manager mode. diff --git a/doc/ss-redir.asciidoc b/doc/ss-redir.asciidoc index ea15ef31..5d17c51e 100644 --- a/doc/ss-redir.asciidoc +++ b/doc/ss-redir.asciidoc @@ -99,7 +99,7 @@ Enable UDP relay and disable TCP relay. Use tproxy instead of redirect. (for tcp) -6:: -Resovle hostname to IPv6 address first. +Resolve hostname to IPv6 address first. --mtu :: Specify the MTU of your network interface. @@ -117,6 +117,18 @@ Only available with Linux kernel > 3.9.0. --no-delay:: Enable TCP_NODELAY. +--tcp-incoming-sndbuf :: +Set TCP send buffer size for incoming connections. + +--tcp-incoming-rcvbuf :: +Set TCP receive buffer size for incoming connections. + +--tcp-outgoing-sndbuf :: +Set TCP send buffer size for outgoing connections. + +--tcp-outgoing-rcvbuf :: +Set TCP receive buffer size for outgoing connections. + --plugin :: Enable SIP003 plugin. (Experimental) diff --git a/doc/ss-server.asciidoc b/doc/ss-server.asciidoc index d06cd191..932a6194 100644 --- a/doc/ss-server.asciidoc +++ b/doc/ss-server.asciidoc @@ -99,7 +99,7 @@ Enable UDP relay. Enable UDP relay and disable TCP relay. -6:: -Resovle hostname to IPv6 address first. +Resolve hostname to IPv6 address first. -d :: Setup name servers for internal DNS resolver (libc-ares). @@ -118,6 +118,18 @@ Only available with Linux kernel > 3.9.0. --no-delay:: Enable TCP_NODELAY. +--tcp-incoming-sndbuf :: +Set TCP send buffer size for incoming connections. + +--tcp-incoming-rcvbuf :: +Set TCP receive buffer size for incoming connections. + +--tcp-outgoing-sndbuf :: +Set TCP send buffer size for outgoing connections. + +--tcp-outgoing-rcvbuf :: +Set TCP receive buffer size for outgoing connections. + --acl :: Enable ACL (Access Control List) and specify config file. diff --git a/doc/ss-tunnel.asciidoc b/doc/ss-tunnel.asciidoc index 26fabafb..9dfda6b8 100644 --- a/doc/ss-tunnel.asciidoc +++ b/doc/ss-tunnel.asciidoc @@ -103,7 +103,7 @@ Enable UDP relay. Enable UDP relay and disable TCP relay. -6:: -Resovle hostname to IPv6 address first. +Resolve hostname to IPv6 address first. -L :: Specify destination server address and port for local port forwarding. @@ -126,6 +126,18 @@ Only available with Linux kernel > 3.9.0. --no-delay:: Enable TCP_NODELAY. +--tcp-incoming-sndbuf :: +Set TCP send buffer size for incoming connections. + +--tcp-incoming-rcvbuf :: +Set TCP receive buffer size for incoming connections. + +--tcp-outgoing-sndbuf :: +Set TCP send buffer size for outgoing connections. + +--tcp-outgoing-rcvbuf :: +Set TCP receive buffer size for outgoing connections. + --plugin :: Enable SIP003 plugin. (Experimental) @@ -138,6 +150,19 @@ Enable verbose mode. -h|--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), diff --git a/docker/alpine/Dockerfile b/docker/alpine/Dockerfile index f6988d48..79949746 100644 --- a/docker/alpine/Dockerfile +++ b/docker/alpine/Dockerfile @@ -14,21 +14,19 @@ COPY . /tmp/repo RUN set -x \ # Build environment setup && apk add --no-cache --virtual .build-deps \ - autoconf \ - automake \ build-base \ + cmake \ c-ares-dev \ libcap \ libev-dev \ - libtool \ libsodium-dev \ linux-headers \ mbedtls-dev \ - pcre-dev \ + pcre2-dev \ # Build & install && cd /tmp/repo \ - && ./autogen.sh \ - && ./configure --prefix=/usr/local --disable-documentation \ + && mkdir -p build && cd build \ + && cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local -DBUILD_TESTING=OFF -DWITH_STATIC=OFF -DCMAKE_BUILD_TYPE=Release \ && make -j$(getconf _NPROCESSORS_ONLN) \ && make install \ && cd /usr/local/bin \ diff --git a/docker/build/builder.Dockerfile b/docker/build/builder.Dockerfile index a1402c57..8aa92c21 100644 --- a/docker/build/builder.Dockerfile +++ b/docker/build/builder.Dockerfile @@ -5,4 +5,4 @@ MAINTAINER wener # Better for cache and dev RUN apk add --no-cache --virtual .build-deps \ alpine-sdk cmake \ - linux-headers libev-dev libsodium-dev mbedtls-static mbedtls-dev pcre-dev udns-dev + linux-headers libev-dev libsodium-dev mbedtls-static mbedtls-dev pcre2-dev udns-dev diff --git a/m4/ax_pthread.m4 b/m4/ax_pthread.m4 deleted file mode 100644 index 4c4051ea..00000000 --- a/m4/ax_pthread.m4 +++ /dev/null @@ -1,485 +0,0 @@ -# =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_pthread.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) -# -# DESCRIPTION -# -# This macro figures out how to build C programs using POSIX threads. It -# sets the PTHREAD_LIBS output variable to the threads library and linker -# flags, and the PTHREAD_CFLAGS output variable to any special C compiler -# flags that are needed. (The user can also force certain compiler -# flags/libs to be tested by setting these environment variables.) -# -# Also sets PTHREAD_CC to any special C compiler that is needed for -# multi-threaded programs (defaults to the value of CC otherwise). (This -# is necessary on AIX to use the special cc_r compiler alias.) -# -# NOTE: You are assumed to not only compile your program with these flags, -# but also to link with them as well. For example, you might link with -# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS -# -# If you are only building threaded programs, you may wish to use these -# variables in your default LIBS, CFLAGS, and CC: -# -# LIBS="$PTHREAD_LIBS $LIBS" -# CFLAGS="$CFLAGS $PTHREAD_CFLAGS" -# CC="$PTHREAD_CC" -# -# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant -# has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to -# that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX). -# -# Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the -# PTHREAD_PRIO_INHERIT symbol is defined when compiling with -# PTHREAD_CFLAGS. -# -# ACTION-IF-FOUND is a list of shell commands to run if a threads library -# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it -# is not found. If ACTION-IF-FOUND is not specified, the default action -# will define HAVE_PTHREAD. -# -# Please let the authors know if this macro fails on any platform, or if -# you have any other suggestions or comments. This macro was based on work -# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help -# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by -# Alejandro Forero Cuervo to the autoconf macro repository. We are also -# grateful for the helpful feedback of numerous users. -# -# Updated for Autoconf 2.68 by Daniel Richard G. -# -# LICENSE -# -# Copyright (c) 2008 Steven G. Johnson -# Copyright (c) 2011 Daniel Richard G. -# -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation, either version 3 of the License, or (at your -# option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General -# Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program. If not, see . -# -# As a special exception, the respective Autoconf Macro's copyright owner -# gives unlimited permission to copy, distribute and modify the configure -# scripts that are the output of Autoconf when processing the Macro. You -# need not follow the terms of the GNU General Public License when using -# or distributing such scripts, even though portions of the text of the -# Macro appear in them. The GNU General Public License (GPL) does govern -# all other use of the material that constitutes the Autoconf Macro. -# -# This special exception to the GPL applies to versions of the Autoconf -# Macro released by the Autoconf Archive. When you make and distribute a -# modified version of the Autoconf Macro, you may extend this special -# exception to the GPL to apply to your modified version as well. - -#serial 23 - -AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) -AC_DEFUN([AX_PTHREAD], [ -AC_REQUIRE([AC_CANONICAL_HOST]) -AC_REQUIRE([AC_PROG_CC]) -AC_REQUIRE([AC_PROG_SED]) -AC_LANG_PUSH([C]) -ax_pthread_ok=no - -# We used to check for pthread.h first, but this fails if pthread.h -# requires special compiler flags (e.g. on Tru64 or Sequent). -# It gets checked for in the link test anyway. - -# First of all, check if the user has set any of the PTHREAD_LIBS, -# etcetera environment variables, and if threads linking works using -# them: -if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then - ax_pthread_save_CC="$CC" - ax_pthread_save_CFLAGS="$CFLAGS" - ax_pthread_save_LIBS="$LIBS" - AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"]) - CFLAGS="$CFLAGS $PTHREAD_CFLAGS" - LIBS="$PTHREAD_LIBS $LIBS" - AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS]) - AC_LINK_IFELSE([AC_LANG_CALL([], [pthread_join])], [ax_pthread_ok=yes]) - AC_MSG_RESULT([$ax_pthread_ok]) - if test "x$ax_pthread_ok" = "xno"; then - PTHREAD_LIBS="" - PTHREAD_CFLAGS="" - fi - CC="$ax_pthread_save_CC" - CFLAGS="$ax_pthread_save_CFLAGS" - LIBS="$ax_pthread_save_LIBS" -fi - -# We must check for the threads library under a number of different -# names; the ordering is very important because some systems -# (e.g. DEC) have both -lpthread and -lpthreads, where one of the -# libraries is broken (non-POSIX). - -# Create a list of thread flags to try. Items starting with a "-" are -# C compiler flags, and other items are library names, except for "none" -# which indicates that we try without any flags at all, and "pthread-config" -# which is a program returning the flags for the Pth emulation library. - -ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" - -# The ordering *is* (sometimes) important. Some notes on the -# individual items follow: - -# pthreads: AIX (must check this before -lpthread) -# none: in case threads are in libc; should be tried before -Kthread and -# other compiler flags to prevent continual compiler warnings -# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) -# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64 -# (Note: HP C rejects this with "bad form for `-t' option") -# -pthreads: Solaris/gcc (Note: HP C also rejects) -# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it -# doesn't hurt to check since this sometimes defines pthreads and -# -D_REENTRANT too), HP C (must be checked before -lpthread, which -# is present but should not be used directly; and before -mthreads, -# because the compiler interprets this as "-mt" + "-hreads") -# -mthreads: Mingw32/gcc, Lynx/gcc -# pthread: Linux, etcetera -# --thread-safe: KAI C++ -# pthread-config: use pthread-config program (for GNU Pth library) - -case $host_os in - - freebsd*) - - # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) - # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) - - ax_pthread_flags="-kthread lthread $ax_pthread_flags" - ;; - - hpux*) - - # From the cc(1) man page: "[-mt] Sets various -D flags to enable - # multi-threading and also sets -lpthread." - - ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags" - ;; - - openedition*) - - # IBM z/OS requires a feature-test macro to be defined in order to - # enable POSIX threads at all, so give the user a hint if this is - # not set. (We don't define these ourselves, as they can affect - # other portions of the system API in unpredictable ways.) - - AC_EGREP_CPP([AX_PTHREAD_ZOS_MISSING], - [ -# if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS) - AX_PTHREAD_ZOS_MISSING -# endif - ], - [AC_MSG_WARN([IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support.])]) - ;; - - solaris*) - - # On Solaris (at least, for some versions), libc contains stubbed - # (non-functional) versions of the pthreads routines, so link-based - # tests will erroneously succeed. (N.B.: The stubs are missing - # pthread_cleanup_push, or rather a function called by this macro, - # so we could check for that, but who knows whether they'll stub - # that too in a future libc.) So we'll check first for the - # standard Solaris way of linking pthreads (-mt -lpthread). - - ax_pthread_flags="-mt,pthread pthread $ax_pthread_flags" - ;; -esac - -# GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC) - -AS_IF([test "x$GCC" = "xyes"], - [ax_pthread_flags="-pthread -pthreads $ax_pthread_flags"]) - -# The presence of a feature test macro requesting re-entrant function -# definitions is, on some systems, a strong hint that pthreads support is -# correctly enabled - -case $host_os in - darwin* | hpux* | linux* | osf* | solaris*) - ax_pthread_check_macro="_REENTRANT" - ;; - - aix*) - ax_pthread_check_macro="_THREAD_SAFE" - ;; - - *) - ax_pthread_check_macro="--" - ;; -esac -AS_IF([test "x$ax_pthread_check_macro" = "x--"], - [ax_pthread_check_cond=0], - [ax_pthread_check_cond="!defined($ax_pthread_check_macro)"]) - -# Are we compiling with Clang? - -AC_CACHE_CHECK([whether $CC is Clang], - [ax_cv_PTHREAD_CLANG], - [ax_cv_PTHREAD_CLANG=no - # Note that Autoconf sets GCC=yes for Clang as well as GCC - if test "x$GCC" = "xyes"; then - AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG], - [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */ -# if defined(__clang__) && defined(__llvm__) - AX_PTHREAD_CC_IS_CLANG -# endif - ], - [ax_cv_PTHREAD_CLANG=yes]) - fi - ]) -ax_pthread_clang="$ax_cv_PTHREAD_CLANG" - -ax_pthread_clang_warning=no - -# Clang needs special handling, because older versions handle the -pthread -# option in a rather... idiosyncratic way - -if test "x$ax_pthread_clang" = "xyes"; then - - # Clang takes -pthread; it has never supported any other flag - - # (Note 1: This will need to be revisited if a system that Clang - # supports has POSIX threads in a separate library. This tends not - # to be the way of modern systems, but it's conceivable.) - - # (Note 2: On some systems, notably Darwin, -pthread is not needed - # to get POSIX threads support; the API is always present and - # active. We could reasonably leave PTHREAD_CFLAGS empty. But - # -pthread does define _REENTRANT, and while the Darwin headers - # ignore this macro, third-party headers might not.) - - PTHREAD_CFLAGS="-pthread" - PTHREAD_LIBS= - - ax_pthread_ok=yes - - # However, older versions of Clang make a point of warning the user - # that, in an invocation where only linking and no compilation is - # taking place, the -pthread option has no effect ("argument unused - # during compilation"). They expect -pthread to be passed in only - # when source code is being compiled. - # - # Problem is, this is at odds with the way Automake and most other - # C build frameworks function, which is that the same flags used in - # compilation (CFLAGS) are also used in linking. Many systems - # supported by AX_PTHREAD require exactly this for POSIX threads - # support, and in fact it is often not straightforward to specify a - # flag that is used only in the compilation phase and not in - # linking. Such a scenario is extremely rare in practice. - # - # Even though use of the -pthread flag in linking would only print - # a warning, this can be a nuisance for well-run software projects - # that build with -Werror. So if the active version of Clang has - # this misfeature, we search for an option to squash it. - - AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread], - [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG], - [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown - # Create an alternate version of $ac_link that compiles and - # links in two steps (.c -> .o, .o -> exe) instead of one - # (.c -> exe), because the warning occurs only in the second - # step - ax_pthread_save_ac_link="$ac_link" - ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g' - ax_pthread_link_step=`$as_echo "$ac_link" | sed "$ax_pthread_sed"` - ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)" - ax_pthread_save_CFLAGS="$CFLAGS" - for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do - AS_IF([test "x$ax_pthread_try" = "xunknown"], [break]) - CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS" - ac_link="$ax_pthread_save_ac_link" - AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], - [ac_link="$ax_pthread_2step_ac_link" - AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], - [break]) - ]) - done - ac_link="$ax_pthread_save_ac_link" - CFLAGS="$ax_pthread_save_CFLAGS" - AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no]) - ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try" - ]) - - case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in - no | unknown) ;; - *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;; - esac - -fi # $ax_pthread_clang = yes - -if test "x$ax_pthread_ok" = "xno"; then -for ax_pthread_try_flag in $ax_pthread_flags; do - - case $ax_pthread_try_flag in - none) - AC_MSG_CHECKING([whether pthreads work without any flags]) - ;; - - -mt,pthread) - AC_MSG_CHECKING([whether pthreads work with -mt -lpthread]) - PTHREAD_CFLAGS="-mt" - PTHREAD_LIBS="-lpthread" - ;; - - -*) - AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag]) - PTHREAD_CFLAGS="$ax_pthread_try_flag" - ;; - - pthread-config) - AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no]) - AS_IF([test "x$ax_pthread_config" = "xno"], [continue]) - PTHREAD_CFLAGS="`pthread-config --cflags`" - PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" - ;; - - *) - AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag]) - PTHREAD_LIBS="-l$ax_pthread_try_flag" - ;; - esac - - ax_pthread_save_CFLAGS="$CFLAGS" - ax_pthread_save_LIBS="$LIBS" - CFLAGS="$CFLAGS $PTHREAD_CFLAGS" - LIBS="$PTHREAD_LIBS $LIBS" - - # Check for various functions. We must include pthread.h, - # since some functions may be macros. (On the Sequent, we - # need a special flag -Kthread to make this header compile.) - # We check for pthread_join because it is in -lpthread on IRIX - # while pthread_create is in libc. We check for pthread_attr_init - # due to DEC craziness with -lpthreads. We check for - # pthread_cleanup_push because it is one of the few pthread - # functions on Solaris that doesn't have a non-functional libc stub. - # We try pthread_create on general principles. - - AC_LINK_IFELSE([AC_LANG_PROGRAM([#include -# if $ax_pthread_check_cond -# error "$ax_pthread_check_macro must be defined" -# endif - static void routine(void *a) { a = 0; } - static void *start_routine(void *a) { return a; }], - [pthread_t th; pthread_attr_t attr; - pthread_create(&th, 0, start_routine, 0); - pthread_join(th, 0); - pthread_attr_init(&attr); - pthread_cleanup_push(routine, 0); - pthread_cleanup_pop(0) /* ; */])], - [ax_pthread_ok=yes], - []) - - CFLAGS="$ax_pthread_save_CFLAGS" - LIBS="$ax_pthread_save_LIBS" - - AC_MSG_RESULT([$ax_pthread_ok]) - AS_IF([test "x$ax_pthread_ok" = "xyes"], [break]) - - PTHREAD_LIBS="" - PTHREAD_CFLAGS="" -done -fi - -# Various other checks: -if test "x$ax_pthread_ok" = "xyes"; then - ax_pthread_save_CFLAGS="$CFLAGS" - ax_pthread_save_LIBS="$LIBS" - CFLAGS="$CFLAGS $PTHREAD_CFLAGS" - LIBS="$PTHREAD_LIBS $LIBS" - - # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. - AC_CACHE_CHECK([for joinable pthread attribute], - [ax_cv_PTHREAD_JOINABLE_ATTR], - [ax_cv_PTHREAD_JOINABLE_ATTR=unknown - for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do - AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], - [int attr = $ax_pthread_attr; return attr /* ; */])], - [ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break], - []) - done - ]) - AS_IF([test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \ - test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \ - test "x$ax_pthread_joinable_attr_defined" != "xyes"], - [AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], - [$ax_cv_PTHREAD_JOINABLE_ATTR], - [Define to necessary symbol if this constant - uses a non-standard name on your system.]) - ax_pthread_joinable_attr_defined=yes - ]) - - AC_CACHE_CHECK([whether more special flags are required for pthreads], - [ax_cv_PTHREAD_SPECIAL_FLAGS], - [ax_cv_PTHREAD_SPECIAL_FLAGS=no - case $host_os in - solaris*) - ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS" - ;; - esac - ]) - AS_IF([test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \ - test "x$ax_pthread_special_flags_added" != "xyes"], - [PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS" - ax_pthread_special_flags_added=yes]) - - AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], - [ax_cv_PTHREAD_PRIO_INHERIT], - [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], - [[int i = PTHREAD_PRIO_INHERIT;]])], - [ax_cv_PTHREAD_PRIO_INHERIT=yes], - [ax_cv_PTHREAD_PRIO_INHERIT=no]) - ]) - AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \ - test "x$ax_pthread_prio_inherit_defined" != "xyes"], - [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.]) - ax_pthread_prio_inherit_defined=yes - ]) - - CFLAGS="$ax_pthread_save_CFLAGS" - LIBS="$ax_pthread_save_LIBS" - - # More AIX lossage: compile with *_r variant - if test "x$GCC" != "xyes"; then - case $host_os in - aix*) - AS_CASE(["x/$CC"], - [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6], - [#handle absolute path differently from PATH based program lookup - AS_CASE(["x$CC"], - [x/*], - [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])], - [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])]) - ;; - esac - fi -fi - -test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" - -AC_SUBST([PTHREAD_LIBS]) -AC_SUBST([PTHREAD_CFLAGS]) -AC_SUBST([PTHREAD_CC]) - -# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: -if test "x$ax_pthread_ok" = "xyes"; then - ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1]) - : -else - ax_pthread_ok=no - $2 -fi -AC_LANG_POP -])dnl AX_PTHREAD diff --git a/m4/ax_tls.m4 b/m4/ax_tls.m4 deleted file mode 100644 index 809b761a..00000000 --- a/m4/ax_tls.m4 +++ /dev/null @@ -1,74 +0,0 @@ -# =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_tls.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_TLS([action-if-found], [action-if-not-found]) -# -# DESCRIPTION -# -# Provides a test for the compiler support of thread local storage (TLS) -# extensions. Defines TLS if it is found. Currently knows about GCC/ICC -# and MSVC. I think SunPro uses the same as GCC, and Borland apparently -# supports either. -# -# LICENSE -# -# Copyright (c) 2008 Alan Woodland -# Copyright (c) 2010 Diego Elio Petteno` -# -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation, either version 3 of the License, or (at your -# option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General -# Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program. If not, see . -# -# As a special exception, the respective Autoconf Macro's copyright owner -# gives unlimited permission to copy, distribute and modify the configure -# scripts that are the output of Autoconf when processing the Macro. You -# need not follow the terms of the GNU General Public License when using -# or distributing such scripts, even though portions of the text of the -# Macro appear in them. The GNU General Public License (GPL) does govern -# all other use of the material that constitutes the Autoconf Macro. -# -# This special exception to the GPL applies to versions of the Autoconf -# Macro released by the Autoconf Archive. When you make and distribute a -# modified version of the Autoconf Macro, you may extend this special -# exception to the GPL to apply to your modified version as well. - -#serial 11 - -AC_DEFUN([AX_TLS], [ - AC_MSG_CHECKING([for thread local storage (TLS) class]) - AC_CACHE_VAL([ac_cv_tls], - [for ax_tls_keyword in __thread '__declspec(thread)' none; do - AS_CASE([$ax_tls_keyword], - [none], [ac_cv_tls=none ; break], - [AC_TRY_COMPILE( - [#include - static void - foo(void) { - static ] $ax_tls_keyword [ int bar; - exit(1); - }], - [], - [ac_cv_tls=$ax_tls_keyword ; break], - ac_cv_tls=none - )]) - done - ]) - AC_MSG_RESULT([$ac_cv_tls]) - - AS_IF([test "$ac_cv_tls" != "none"], - [AC_DEFINE_UNQUOTED([TLS],[$ac_cv_tls],[If the compiler supports a TLS storage class define it to that here]) - m4_ifnblank([$1],[$1])], - [m4_ifnblank([$2],[$2])]) -]) diff --git a/m4/cares.m4 b/m4/cares.m4 deleted file mode 100644 index a6853ce8..00000000 --- a/m4/cares.m4 +++ /dev/null @@ -1,30 +0,0 @@ -dnl Check to find the libcares headers/libraries - -AC_DEFUN([ss_CARES], -[ - - AC_ARG_WITH(cares, - AS_HELP_STRING([--with-cares=DIR], [The c-ares library base directory, or:]), - [cares="$withval" - CFLAGS="$CFLAGS -I$withval/include" - LDFLAGS="$LDFLAGS -L$withval/lib"] - ) - - AC_ARG_WITH(cares-include, - AS_HELP_STRING([--with-cares-include=DIR], [The c-ares library headers directory (without trailing /cares)]), - [cares_include="$withval" - CFLAGS="$CFLAGS -I$withval"] - ) - - AC_ARG_WITH(cares-lib, - AS_HELP_STRING([--with-cares-lib=DIR], [The c-ares library library directory]), - [cares_lib="$withval" - LDFLAGS="$LDFLAGS -L$withval"] - ) - - AC_CHECK_LIB(cares, ares_library_init, - [LIBS="-lcares $LIBS"], - [AC_MSG_ERROR([The c-ares library libraries not found.])] - ) - -]) diff --git a/m4/inet_ntop.m4 b/m4/inet_ntop.m4 deleted file mode 100644 index adb439b4..00000000 --- a/m4/inet_ntop.m4 +++ /dev/null @@ -1,41 +0,0 @@ -# inet_ntop.m4 serial 19 -dnl Copyright (C) 2005-2006, 2008-2013 Free Software Foundation, Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. - -AC_DEFUN([ss_FUNC_INET_NTOP], -[ - AC_REQUIRE([AC_C_RESTRICT]) - - dnl Most platforms that provide inet_ntop define it in libc. - dnl Solaris 8..10 provide inet_ntop in libnsl instead. - dnl Solaris 2.6..7 provide inet_ntop in libresolv instead. - HAVE_INET_NTOP=1 - INET_NTOP_LIB= - ss_save_LIBS=$LIBS - AC_SEARCH_LIBS([inet_ntop], [nsl resolv], [], - [AC_CHECK_FUNCS([inet_ntop]) - if test $ac_cv_func_inet_ntop = no; then - HAVE_INET_NTOP=0 - fi - ]) - LIBS=$ss_save_LIBS - - if test "$ac_cv_search_inet_ntop" != "no" \ - && test "$ac_cv_search_inet_ntop" != "none required"; then - INET_NTOP_LIB="$ac_cv_search_inet_ntop" - fi - - AC_CHECK_HEADERS_ONCE([netdb.h]) - AC_CHECK_DECLS([inet_ntop],,, - [[#include - #if HAVE_NETDB_H - # include - #endif - ]]) - if test $ac_cv_have_decl_inet_ntop = no; then - HAVE_DECL_INET_NTOP=0 - fi - AC_SUBST([INET_NTOP_LIB]) -]) diff --git a/m4/mbedtls.m4 b/m4/mbedtls.m4 deleted file mode 100644 index a795790c..00000000 --- a/m4/mbedtls.m4 +++ /dev/null @@ -1,114 +0,0 @@ -dnl Check to find the mbed TLS headers/libraries - -AC_DEFUN([ss_MBEDTLS], -[ - - AC_ARG_WITH(mbedtls, - AS_HELP_STRING([--with-mbedtls=DIR], [mbed TLS base directory, or:]), - [mbedtls="$withval" - CFLAGS="$CFLAGS -I$withval/include" - LDFLAGS="$LDFLAGS -L$withval/lib"] - ) - - AC_ARG_WITH(mbedtls-include, - AS_HELP_STRING([--with-mbedtls-include=DIR], [mbed TLS headers directory (without trailing /mbedtls)]), - [mbedtls_include="$withval" - CFLAGS="$CFLAGS -I$withval"] - ) - - AC_ARG_WITH(mbedtls-lib, - AS_HELP_STRING([--with-mbedtls-lib=DIR], [mbed TLS library directory]), - [mbedtls_lib="$withval" - LDFLAGS="$LDFLAGS -L$withval"] - ) - - AC_CHECK_LIB(mbedcrypto, mbedtls_cipher_setup, - [LIBS="-lmbedcrypto $LIBS"], - [AC_MSG_ERROR([mbed TLS libraries not found.])] - ) - - AC_MSG_CHECKING([whether mbedtls supports Cipher Feedback mode or not]) - AC_COMPILE_IFELSE( - [AC_LANG_PROGRAM( - [[ -#include -#if MBEDTLS_VERSION_NUMBER >= 0x03000000 -#include -#else -#include -#endif - ]], - [[ -#ifndef MBEDTLS_CIPHER_MODE_CFB -#error Cipher Feedback mode a.k.a CFB not supported by your mbed TLS. -#endif - ]] - )], - [AC_MSG_RESULT([ok])], - [AC_MSG_ERROR([MBEDTLS_CIPHER_MODE_CFB required])] - ) - - - AC_MSG_CHECKING([whether mbedtls supports the ARC4 stream cipher or not]) - AC_COMPILE_IFELSE( - [AC_LANG_PROGRAM( - [[ -#include -#if MBEDTLS_VERSION_NUMBER >= 0x03000000 -#include -#else -#include -#endif - ]], - [[ -#ifndef MBEDTLS_ARC4_C -#error the ARC4 stream cipher not supported by your mbed TLS. -#endif - ]] - )], - [AC_MSG_RESULT([ok])], - [AC_MSG_WARN([We will continue without ARC4 stream cipher support, MBEDTLS_ARC4_C required])] - ) - - AC_MSG_CHECKING([whether mbedtls supports the Blowfish block cipher or not]) - AC_COMPILE_IFELSE( - [AC_LANG_PROGRAM( - [[ -#include -#if MBEDTLS_VERSION_NUMBER >= 0x03000000 -#include -#else -#include -#endif - ]], - [[ -#ifndef MBEDTLS_BLOWFISH_C -#error the Blowfish block cipher not supported by your mbed TLS. -#endif - ]] - )], - [AC_MSG_RESULT([ok])], - [AC_MSG_WARN([We will continue without Blowfish block cipher support, MBEDTLS_BLOWFISH_C required])] - ) - - AC_MSG_CHECKING([whether mbedtls supports the Camellia block cipher or not]) - AC_COMPILE_IFELSE( - [AC_LANG_PROGRAM( - [[ -#include -#if MBEDTLS_VERSION_NUMBER >= 0x03000000 -#include -#else -#include -#endif - ]], - [[ -#ifndef MBEDTLS_CAMELLIA_C -#error the Camellia block cipher not supported by your mbed TLS. -#endif - ]] - )], - [AC_MSG_RESULT([ok])], - [AC_MSG_WARN([We will continue without Camellia block cipher support, MBEDTLS_CAMELLIA_C required])] - ) -]) diff --git a/m4/pcre.m4 b/m4/pcre.m4 deleted file mode 100644 index 4d965bc7..00000000 --- a/m4/pcre.m4 +++ /dev/null @@ -1,152 +0,0 @@ -dnl -------------------------------------------------------- -*- autoconf -*- -dnl Licensed to the Apache Software Foundation (ASF) under one or more -dnl contributor license agreements. See the NOTICE file distributed with -dnl this work for additional information regarding copyright ownership. -dnl The ASF licenses this file to You under the Apache License, Version 2.0 -dnl (the "License"); you may not use this file except in compliance with -dnl the License. You may obtain a copy of the License at -dnl -dnl http://www.apache.org/licenses/LICENSE-2.0 -dnl -dnl Unless required by applicable law or agreed to in writing, software -dnl distributed under the License is distributed on an "AS IS" BASIS, -dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -dnl See the License for the specific language governing permissions and -dnl limitations under the License. - -dnl -dnl TS_ADDTO(variable, value) -dnl -dnl Add value to variable -dnl -AC_DEFUN([TS_ADDTO], [ - if test "x$$1" = "x"; then - test "x$verbose" = "xyes" && echo " setting $1 to \"$2\"" - $1="$2" - else - ats_addto_bugger="$2" - for i in $ats_addto_bugger; do - ats_addto_duplicate="0" - for j in $$1; do - if test "x$i" = "x$j"; then - ats_addto_duplicate="1" - break - fi - done - if test $ats_addto_duplicate = "0"; then - test "x$verbose" = "xyes" && echo " adding \"$i\" to $1" - $1="$$1 $i" - fi - done - fi -])dnl - -dnl -dnl TS_ADDTO_RPATH(path) -dnl -dnl Adds path to variable with the '-rpath' directive. -dnl -AC_DEFUN([TS_ADDTO_RPATH], [ - AC_MSG_NOTICE([adding $1 to RPATH]) - TS_ADDTO(LIBTOOL_LINK_FLAGS, [-R$1]) -])dnl - -dnl -dnl pcre.m4: Trafficserver's pcre autoconf macros -dnl - -dnl -dnl TS_CHECK_PCRE: look for pcre libraries and headers -dnl -AC_DEFUN([TS_CHECK_PCRE], [ -enable_pcre=no -AC_ARG_WITH(pcre, [AC_HELP_STRING([--with-pcre=DIR],[use a specific pcre library])], -[ - if test "x$withval" != "xyes" && test "x$withval" != "x"; then - pcre_base_dir="$withval" - if test "$withval" != "no"; then - enable_pcre=yes - case "$withval" in - *":"*) - pcre_include="`echo $withval |sed -e 's/:.*$//'`" - pcre_ldflags="`echo $withval |sed -e 's/^.*://'`" - AC_MSG_CHECKING(checking for pcre includes in $pcre_include libs in $pcre_ldflags ) - ;; - *) - pcre_include="$withval/include" - pcre_ldflags="$withval/lib" - AC_MSG_CHECKING(checking for pcre includes in $withval) - ;; - esac - fi - fi -], -[ - AC_CHECK_PROG(PCRE_CONFIG, pcre-config, pcre-config) - if test "x$PCRE_CONFIG" != "x"; then - enable_pcre=yes - pcre_base_dir="`$PCRE_CONFIG --prefix`" - pcre_include="`$PCRE_CONFIG --cflags | sed -es/-I//`" - pcre_ldflags="`$PCRE_CONFIG --libs | sed -es/-lpcre// -es/-L//`" - fi -]) - -if test "x$pcre_base_dir" = "x"; then - AC_MSG_CHECKING([for pcre location]) - AC_CACHE_VAL(ats_cv_pcre_dir,[ - for dir in /usr/local /usr ; do - if test -d $dir && ( test -f $dir/include/pcre.h || test -f $dir/include/pcre/pcre.h ); then - ats_cv_pcre_dir=$dir - break - fi - done - ]) - pcre_base_dir=$ats_cv_pcre_dir - if test "x$pcre_base_dir" = "x"; then - enable_pcre=no - AC_MSG_RESULT([not found]) - else - enable_pcre=yes - pcre_include="$pcre_base_dir/include" - pcre_ldflags="$pcre_base_dir/lib" - AC_MSG_RESULT([$pcre_base_dir]) - fi -else - AC_MSG_CHECKING(for pcre headers in $pcre_include) - if test -d $pcre_include && test -d $pcre_ldflags && ( test -f $pcre_include/pcre.h || test -f $pcre_include/pcre/pcre.h ); then - AC_MSG_RESULT([ok]) - else - AC_MSG_RESULT([not found]) - fi -fi - -pcreh=0 -pcre_pcreh=0 -if test "$enable_pcre" != "no"; then - saved_ldflags=$LDFLAGS - saved_cppflags=$CFLAGS - pcre_have_headers=0 - pcre_have_libs=0 - if test "$pcre_base_dir" != "/usr"; then - TS_ADDTO(CFLAGS, [-I${pcre_include}]) - TS_ADDTO(CFLAGS, [-DPCRE_STATIC]) - TS_ADDTO(LDFLAGS, [-L${pcre_ldflags}]) - TS_ADDTO_RPATH(${pcre_ldflags}) - fi - AC_SEARCH_LIBS([pcre_exec], [pcre], [pcre_have_libs=1]) - if test "$pcre_have_libs" != "0"; then - AC_CHECK_HEADERS(pcre.h, [pcre_have_headers=1]) - AC_CHECK_HEADERS(pcre/pcre.h, [pcre_have_headers=1]) - fi - if test "$pcre_have_headers" != "0"; then - AC_DEFINE(HAVE_LIBPCRE,1,[Compiling with pcre support]) - AC_SUBST(LIBPCRE, [-lpcre]) - else - enable_pcre=no - CFLAGS=$saved_cppflags - LDFLAGS=$saved_ldflags - fi -fi -AC_SUBST(pcreh) -AC_SUBST(pcre_pcreh) -]) diff --git a/m4/sodium.m4 b/m4/sodium.m4 deleted file mode 100644 index b4c254e6..00000000 --- a/m4/sodium.m4 +++ /dev/null @@ -1,40 +0,0 @@ -dnl Check to find the libsodium headers/libraries - -AC_DEFUN([ss_SODIUM], -[ - - AC_ARG_WITH(sodium, - AS_HELP_STRING([--with-sodium=DIR], [The Sodium crypto library base directory, or:]), - [sodium="$withval" - CFLAGS="$CFLAGS -I$withval/include" - LDFLAGS="$LDFLAGS -L$withval/lib"] - ) - - AC_ARG_WITH(sodium-include, - AS_HELP_STRING([--with-sodium-include=DIR], [The Sodium crypto library headers directory (without trailing /sodium)]), - [sodium_include="$withval" - CFLAGS="$CFLAGS -I$withval"] - ) - - AC_ARG_WITH(sodium-lib, - AS_HELP_STRING([--with-sodium-lib=DIR], [The Sodium crypto library library directory]), - [sodium_lib="$withval" - LDFLAGS="$LDFLAGS -L$withval"] - ) - - AC_CHECK_LIB(sodium, sodium_init, - [LIBS="-lsodium $LIBS"], - [AC_MSG_ERROR([The Sodium crypto library libraries not found.])] - ) - - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ - #include - ], [ - #if SODIUM_LIBRARY_VERSION_MAJOR < 7 || SODIUM_LIBRARY_VERSION_MAJOR ==7 && SODIUM_LIBRARY_VERSION_MINOR < 6 - # error - #endif - ])], - [AC_MSG_RESULT([checking for version of libsodium... yes])], - [AC_MSG_ERROR([Wrong libsodium: version >= 1.0.4 required])]) - -]) diff --git a/m4/stack-protector.m4 b/m4/stack-protector.m4 deleted file mode 100644 index 33b3aae0..00000000 --- a/m4/stack-protector.m4 +++ /dev/null @@ -1,58 +0,0 @@ -# -# Copyright 2007 Google Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# GGL_CHECK_STACK_PROTECTOR([ACTION-IF-OK], [ACTION-IF-NOT-OK]) -# Check if c compiler supports -fstack-protector and -fstack-protector-all -# options. - -AC_DEFUN([GGL_CHECK_STACK_PROTECTOR], [ -ggl_check_stack_protector_save_CXXFLAGS="$CXXFLAGS" -ggl_check_stack_protector_save_CFLAGS="$CFLAGS" - -AC_MSG_CHECKING([if -fstack-protector and -fstack-protector-all are supported.]) - -CXXFLAGS="$CXXFLAGS -fstack-protector" -CFLAGS="$CFLAGS -fstack-protector" -AC_COMPILE_IFELSE([AC_LANG_SOURCE([ -int main() { - return 0; -} -])], -[ggl_check_stack_protector_ok=yes], -[ggl_check_stack_protector_ok=no]) - -CXXFLAGS="$ggl_check_stack_protector_save_CXXFLAGS -fstack-protector-all" -CFLAGS="$ggl_check_stack_protector_save_CFLAGS -fstack-protector-all" -AC_COMPILE_IFELSE([AC_LANG_SOURCE([ -int main() { - return 0; -} -])], -[ggl_check_stack_protector_all_ok=yes], -[ggl_check_stack_protector_all_ok=no]) - -if test "x$ggl_check_stack_protector_ok" = "xyes" -a \ - "x$ggl_check_stack_protector_all_ok" = "xyes"; then - AC_MSG_RESULT([yes]) - ifelse([$1], , :, [$1]) -else - AC_MSG_RESULT([no]) - ifelse([$2], , :, [$2]) -fi - -CXXFLAGS="$ggl_check_stack_protector_save_CXXFLAGS" -CFLAGS="$ggl_check_stack_protector_save_CFLAGS" - -]) # GGL_CHECK_STACK_PROTECTOR diff --git a/shadowsocks-libev.pc.in b/shadowsocks-libev.pc.in deleted file mode 100644 index e3738766..00000000 --- a/shadowsocks-libev.pc.in +++ /dev/null @@ -1,12 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: shadowsocks-libev -Description: a lightweight secured socks5 proxy -URL: https://shadowsocks.org -Version: @VERSION@ -Requires: -Cflags: -I${includedir} -Libs: -L${libdir} -lshadowsocks-libev diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9ca9d852..18ae7ba0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -84,25 +84,29 @@ set(SS_REDIR_SOURCE ${SS_PLUGIN_SOURCE} ) -if (CMAKE_SYSTEM_NAME STREQUAL Darwin) -find_path(LIBSODIUM_INCLUDE_DIR sodium.h - PATHS - $ENV{LIBSODIUM_INCLUDE_DIR} - $ENV{LIBSODIUM_DIR}/include - /usr/local/libsodium/include - /opt/libsodium/include - /usr/local/include -) -include_directories(${LIBSODIUM_INCLUDE_DIR}) -endif () +# Apply -Werror only to project sources (not bundled submodules) +set(SS_WERROR_FLAGS -Werror) + +# Include directories from Find modules +# MbedTLS must come first to avoid conflicts with other mbedtls versions +# that may be in /opt/homebrew/include (e.g. mbedtls 4.x vs 3.x) +include_directories(BEFORE ${MBEDTLS_INCLUDE_DIRS}) +include_directories(${SODIUM_INCLUDE_DIRS}) +include_directories(${PCRE2_INCLUDE_DIRS}) +include_directories(${CARES_INCLUDE_DIRS}) + +# Extract mbedTLS lib dir to ensure static/shared libs come from same version +get_filename_component(_MBEDTLS_LIB_DIR "${MBEDTLS_CRYPTO_LIBRARY}" DIRECTORY) if (WITH_STATIC) find_library(LIBSODIUM libsodium.a) +find_library(LIBMBEDTLS libmbedtls.a HINTS ${_MBEDTLS_LIB_DIR} NO_DEFAULT_PATH) find_library(LIBMBEDTLS libmbedtls.a) +find_library(LIBMBEDCRYPTO libmbedcrypto.a HINTS ${_MBEDTLS_LIB_DIR} NO_DEFAULT_PATH) find_library(LIBMBEDCRYPTO libmbedcrypto.a) find_library(LIBEV libev.a) find_library(LIBUDNS libcares.a) -find_library(LIBPCRE libpcre.a) +find_library(LIBPCRE2 libpcre2-8.a) # Dependencies we need for static and shared list(APPEND DEPS @@ -110,7 +114,7 @@ list(APPEND DEPS bloom ${LIBEV} ${LIBUDNS} - ${LIBPCRE} + ${LIBPCRE2} ${LIBSODIUM} ${LIBMBEDTLS} ${LIBMBEDCRYPTO} @@ -118,16 +122,18 @@ list(APPEND DEPS if (MINGW) list(APPEND DEPS ws2_32 iphlpapi) -add_compile_definitions(CARES_STATICLIB PCRE_STATIC) +add_compile_definitions(CARES_STATICLIB PCRE2_STATIC) endif () endif () find_library(LIBSODIUM_SHARED sodium) +find_library(LIBMBEDTLS_SHARED mbedtls HINTS ${_MBEDTLS_LIB_DIR} NO_DEFAULT_PATH) find_library(LIBMBEDTLS_SHARED mbedtls) +find_library(LIBMBEDCRYPTO_SHARED mbedcrypto HINTS ${_MBEDTLS_LIB_DIR} NO_DEFAULT_PATH) find_library(LIBMBEDCRYPTO_SHARED mbedcrypto) find_library(LIBEV_SHARED ev) find_library(LIBUDNS_SHARED cares) -find_library(LIBPCRE_SHARED pcre) +find_library(LIBPCRE2_SHARED pcre2-8) if (WITH_EMBEDDED_SRC) list(APPEND DEPS_SHARED @@ -137,7 +143,7 @@ list(APPEND DEPS_SHARED ipset-shared ${LIBEV_SHARED} ${LIBUDNS_SHARED} - ${LIBPCRE_SHARED} + ${LIBPCRE2_SHARED} ${LIBSODIUM_SHARED} ${LIBMBEDTLS_SHARED} ${LIBMBEDCRYPTO_SHARED} @@ -153,13 +159,24 @@ list(APPEND DEPS_SHARED ${LIBCORKIPSET_SHARED} ${LIBEV_SHARED} ${LIBUDNS_SHARED} - ${LIBPCRE_SHARED} + ${LIBPCRE2_SHARED} ${LIBSODIUM_SHARED} ${LIBMBEDTLS_SHARED} ${LIBMBEDCRYPTO_SHARED} ) endif () +# Connmarktos/nftables libraries for ss-server +if(ENABLE_CONNMARKTOS) + list(APPEND DEPS_CONNMARKTOS ${NETFILTER_CONNTRACK_LIB}) + if(NFNETLINK_LIB) + list(APPEND DEPS_CONNMARKTOS ${NFNETLINK_LIB}) + endif() +endif() +if(ENABLE_NFTABLES) + list(APPEND DEPS_NFTABLES ${MNL_LIB} ${NFTNL_LIB}) +endif() + find_package (Threads) if (WITH_STATIC) @@ -189,9 +206,24 @@ target_compile_definitions(ss-local PUBLIC -DMODULE_LOCAL) target_compile_definitions(ss-redir PUBLIC -DMODULE_REDIR) target_compile_definitions(shadowsocks-libev PUBLIC -DMODULE_LOCAL -DLIB_ONLY) +target_compile_options(ss-server PRIVATE ${SS_WERROR_FLAGS}) +target_compile_options(ss-tunnel PRIVATE ${SS_WERROR_FLAGS}) +target_compile_options(ss-manager PRIVATE ${SS_WERROR_FLAGS}) +target_compile_options(ss-local PRIVATE ${SS_WERROR_FLAGS}) +target_compile_options(ss-redir PRIVATE ${SS_WERROR_FLAGS}) +target_compile_options(shadowsocks-libev PRIVATE ${SS_WERROR_FLAGS}) + +# Connmarktos/nftables compile definitions +if(ENABLE_CONNMARKTOS) + target_compile_definitions(ss-server PUBLIC -DUSE_NFCONNTRACK_TOS) +endif() +if(ENABLE_NFTABLES) + target_compile_definitions(ss-server PUBLIC -DUSE_NFTABLES) +endif() + target_include_directories(shadowsocks-libev PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) -target_link_libraries(ss-server cork ipset ${DEPS}) +target_link_libraries(ss-server cork ipset ${DEPS} ${DEPS_CONNMARKTOS} ${DEPS_NFTABLES}) target_link_libraries(ss-tunnel cork ${DEPS}) target_link_libraries(ss-manager m bloom cork ${LIBEV} ${LIBUDNS}) target_link_libraries(ss-local cork ipset ${DEPS}) @@ -226,9 +258,24 @@ target_compile_definitions(ss-local-shared PUBLIC -DMODULE_LOCAL) target_compile_definitions(ss-redir-shared PUBLIC -DMODULE_REDIR) target_compile_definitions(shadowsocks-libev-shared PUBLIC -DMODULE_LOCAL -DLIB_ONLY) +target_compile_options(ss-server-shared PRIVATE ${SS_WERROR_FLAGS}) +target_compile_options(ss-tunnel-shared PRIVATE ${SS_WERROR_FLAGS}) +target_compile_options(ss-manager-shared PRIVATE ${SS_WERROR_FLAGS}) +target_compile_options(ss-local-shared PRIVATE ${SS_WERROR_FLAGS}) +target_compile_options(ss-redir-shared PRIVATE ${SS_WERROR_FLAGS}) +target_compile_options(shadowsocks-libev-shared PRIVATE ${SS_WERROR_FLAGS}) + +# Connmarktos/nftables compile definitions for shared +if(ENABLE_CONNMARKTOS) + target_compile_definitions(ss-server-shared PUBLIC -DUSE_NFCONNTRACK_TOS) +endif() +if(ENABLE_NFTABLES) + target_compile_definitions(ss-server-shared PUBLIC -DUSE_NFTABLES) +endif() + target_include_directories(shadowsocks-libev-shared PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) -target_link_libraries(ss-server-shared ${DEPS_SHARED}) +target_link_libraries(ss-server-shared ${DEPS_SHARED} ${DEPS_CONNMARKTOS} ${DEPS_NFTABLES}) target_link_libraries(ss-tunnel-shared ${DEPS_SHARED}) target_link_libraries(ss-manager-shared ${CMAKE_THREAD_LIBS_INIT} ${LIBEV_SHARED} ${LIBUDNS_SHARED} ${DEPS_SHARED}) target_link_libraries(ss-local-shared ${DEPS_SHARED}) @@ -247,6 +294,7 @@ set_target_properties(ss-server-shared ss-tunnel-shared ss-manager-shared ss-loc ) set_target_properties(shadowsocks-libev-shared PROPERTIES OUTPUT_NAME shadowsocks-libev) +set_target_properties(shadowsocks-libev-shared PROPERTIES VERSION 2.0.0 SOVERSION 2) target_compile_definitions(shadowsocks-libev-shared PUBLIC -DMODULE_LOCAL) target_link_libraries(shadowsocks-libev-shared ${DEPS_SHARED}) @@ -255,17 +303,17 @@ target_link_libraries(shadowsocks-libev-shared ${DEPS_SHARED}) # Recommend to install shared by default install(DIRECTORY ${RUNTIME_SHARED_OUTPUT_DIRECTORY}/ USE_SOURCE_PERMISSIONS - DESTINATION bin) + DESTINATION ${CMAKE_INSTALL_BINDIR}) if (WITH_STATIC) install(TARGETS shadowsocks-libev - ARCHIVE DESTINATION lib) + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) endif () install(TARGETS shadowsocks-libev-shared - LIBRARY DESTINATION lib) + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) -install(FILES shadowsocks.h DESTINATION include) +install(FILES shadowsocks.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) add_custom_target(distclean diff --git a/src/Makefile.am b/src/Makefile.am deleted file mode 100644 index c255b608..00000000 --- a/src/Makefile.am +++ /dev/null @@ -1,115 +0,0 @@ -VERSION_INFO = 2:0:0 - -AM_CFLAGS = -g -O2 -Wall -Werror -Wno-deprecated-declarations -fno-strict-aliasing -std=gnu99 -D_GNU_SOURCE -AM_CFLAGS += $(PTHREAD_CFLAGS) -if !USE_SYSTEM_SHARED_LIB -AM_CFLAGS += -I$(top_srcdir)/libbloom -AM_CFLAGS += -I$(top_srcdir)/libipset/include -AM_CFLAGS += -I$(top_srcdir)/libcork/include -endif -AM_CFLAGS += $(LIBPCRE_CFLAGS) - -SS_COMMON_LIBS = $(INET_NTOP_LIB) $(LIBPCRE_LIBS) $(NETFILTER_CONNTRACK_LIBS) $(NFTABLES_LIBS) -if !USE_SYSTEM_SHARED_LIB -SS_COMMON_LIBS += $(top_builddir)/libbloom/libbloom.la \ - $(top_builddir)/libipset/libipset.la \ - $(top_builddir)/libcork/libcork.la -else -SS_COMMON_LIBS += -lbloom -lcork -lcorkipset -endif -SS_COMMON_LIBS += -lev -lsodium -lm - -bin_PROGRAMS = ss-local ss-tunnel ss-server -if !BUILD_WINCOMPAT -bin_PROGRAMS += ss-manager -endif - -acl_src = rule.c \ - acl.c - -crypto_src = crypto.c \ - aead.c \ - stream.c \ - ppbloom.c \ - base64.c - -plugin_src = plugin.c - -common_src = utils.c \ - jconf.c \ - json.c \ - udprelay.c \ - cache.c \ - netutils.c - -if BUILD_WINCOMPAT -common_src += winsock.c -endif - -ss_local_SOURCES = local.c \ - $(common_src) \ - $(crypto_src) \ - $(plugin_src) \ - $(acl_src) - -ss_tunnel_SOURCES = tunnel.c \ - $(common_src) \ - $(crypto_src) \ - $(plugin_src) - -ss_server_SOURCES = resolv.c \ - server.c \ - $(common_src) \ - $(crypto_src) \ - $(plugin_src) \ - ${acl_src} - -ss_manager_SOURCES = utils.c \ - jconf.c \ - json.c \ - netutils.c \ - manager.c - -ss_local_LDADD = $(SS_COMMON_LIBS) -ss_tunnel_LDADD = $(SS_COMMON_LIBS) -ss_server_LDADD = $(SS_COMMON_LIBS) -ss_manager_LDADD = $(SS_COMMON_LIBS) -ss_local_LDADD += -lcares -ss_tunnel_LDADD += -lcares -ss_server_LDADD += -lcares -ss_manager_LDADD += -lcares - -ss_local_CFLAGS = $(AM_CFLAGS) -DMODULE_LOCAL -ss_tunnel_CFLAGS = $(AM_CFLAGS) -DMODULE_TUNNEL -ss_server_CFLAGS = $(AM_CFLAGS) -DMODULE_REMOTE -ss_manager_CFLAGS = $(AM_CFLAGS) -DMODULE_MANAGER - -if BUILD_REDIRECTOR -bin_SCRIPTS = ss-nat -bin_PROGRAMS += ss-redir -ss_redir_SOURCES = utils.c \ - jconf.c \ - json.c \ - netutils.c \ - cache.c \ - udprelay.c \ - redir.c \ - $(crypto_src) \ - $(plugin_src) - -ss_redir_CFLAGS = $(AM_CFLAGS) -DMODULE_REDIR -ss_redir_LDADD = $(SS_COMMON_LIBS) -ss_redir_LDADD += -lcares -endif - -lib_LTLIBRARIES = libshadowsocks-libev.la -libshadowsocks_libev_la_SOURCES = $(ss_local_SOURCES) -libshadowsocks_libev_la_CFLAGS = $(ss_local_CFLAGS) -DLIB_ONLY -libshadowsocks_libev_la_LDFLAGS = -version-info $(VERSION_INFO) -libshadowsocks_libev_la_LIBADD = $(ss_local_LDADD) -include_HEADERS = shadowsocks.h - -noinst_HEADERS = acl.h crypto.h stream.h aead.h json.h netutils.h redir.h server.h uthash.h \ - cache.h local.h plugin.h resolv.h tunnel.h utils.h base64.h ppbloom.h \ - common.h jconf.h manager.h rule.h socks5.h udprelay.h winsock.h -EXTRA_DIST = ss-nat diff --git a/src/rule.c b/src/rule.c index 737013e6..6f8428de 100644 --- a/src/rule.c +++ b/src/rule.c @@ -78,16 +78,21 @@ int init_rule(rule_t *rule) { if (rule->pattern_re == NULL) { - const char *reerr; - int reerroffset; + int errcode; + PCRE2_SIZE erroffset; rule->pattern_re = - pcre_compile(rule->pattern, 0, &reerr, &reerroffset, NULL); + pcre2_compile((PCRE2_SPTR)rule->pattern, PCRE2_ZERO_TERMINATED, + 0, &errcode, &erroffset, NULL); if (rule->pattern_re == NULL) { + PCRE2_UCHAR errbuf[256]; + pcre2_get_error_message(errcode, errbuf, sizeof(errbuf)); LOGE("Regex compilation of \"%s\" failed: %s, offset %d", - rule->pattern, reerr, reerroffset); + rule->pattern, errbuf, (int)erroffset); return 0; } + rule->match_data = pcre2_match_data_create_from_pattern( + rule->pattern_re, NULL); } return 1; @@ -105,8 +110,8 @@ lookup_rule(const struct cork_dllist *rules, const char *name, size_t name_len) cork_dllist_foreach_void(rules, curr, next) { rule_t *rule = cork_container_of(curr, rule_t, entries); - if (pcre_exec(rule->pattern_re, NULL, - name, name_len, 0, 0, NULL, 0) >= 0) + if (pcre2_match(rule->pattern_re, (PCRE2_SPTR)name, + name_len, 0, 0, rule->match_data, NULL) >= 0) return rule; } @@ -127,7 +132,9 @@ free_rule(rule_t *rule) return; ss_free(rule->pattern); + if (rule->match_data != NULL) + pcre2_match_data_free(rule->match_data); if (rule->pattern_re != NULL) - pcre_free(rule->pattern_re); + pcre2_code_free(rule->pattern_re); ss_free(rule); } diff --git a/src/rule.h b/src/rule.h index 015bc42b..ae907f77 100644 --- a/src/rule.h +++ b/src/rule.h @@ -33,17 +33,15 @@ #include -#ifdef HAVE_PCRE_H -#include -#elif HAVE_PCRE_PCRE_H -#include -#endif +#define PCRE2_CODE_UNIT_WIDTH 8 +#include typedef struct rule { char *pattern; /* Runtime fields */ - pcre *pattern_re; + pcre2_code *pattern_re; + pcre2_match_data *match_data; struct cork_dllist_item entries; } rule_t; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 00000000..bba512f5 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,118 @@ +# Unit tests using CTest +include_directories(BEFORE ${MBEDTLS_INCLUDE_DIRS}) +include_directories(${PROJECT_SOURCE_DIR}/src) +include_directories(${PROJECT_BINARY_DIR}/src) +include_directories(${SODIUM_INCLUDE_DIRS}) +include_directories(${PCRE2_INCLUDE_DIRS}) +include_directories(${CARES_INCLUDE_DIRS}) + +if(WITH_EMBEDDED_SRC) + include_directories(${PROJECT_SOURCE_DIR}/libcork/include) + include_directories(${PROJECT_SOURCE_DIR}/libipset/include) + include_directories(${PROJECT_SOURCE_DIR}/libbloom/murmur2) + include_directories(${PROJECT_SOURCE_DIR}/libbloom) +endif() + +# Helper function to add a unit test +function(ss_add_test name sources libs) + add_executable(${name} ${sources}) + target_compile_definitions(${name} PRIVATE -DHAVE_CONFIG_H) + target_link_libraries(${name} ${libs}) + add_test(NAME ${name} COMMAND ${name}) +endfunction() + +# test_base64 - no external deps +ss_add_test(test_base64 + "test_base64.c;${PROJECT_SOURCE_DIR}/src/base64.c" + "") + +# test_utils - needs sodium (for ss_malloc -> uses sodium in utils.c) +ss_add_test(test_utils + "test_utils.c;${PROJECT_SOURCE_DIR}/src/utils.c" + "${LIBSODIUM_SHARED}") + +# test_json - standalone json parser +ss_add_test(test_json + "test_json.c;${PROJECT_SOURCE_DIR}/src/json.c" + "m") + +# test_netutils - needs cork for ip address parsing +set(TEST_NETUTILS_LIBS + ${LIBSODIUM_SHARED} + ${LIBEV_SHARED} + ${LIBUDNS_SHARED} +) +if(WITH_EMBEDDED_SRC) + list(APPEND TEST_NETUTILS_LIBS cork-shared) +else() + list(APPEND TEST_NETUTILS_LIBS ${LIBCORK_SHARED}) +endif() +ss_add_test(test_netutils + "test_netutils.c;${PROJECT_SOURCE_DIR}/src/netutils.c;${PROJECT_SOURCE_DIR}/src/utils.c" + "${TEST_NETUTILS_LIBS}") + +# test_cache - needs libev for ev_time() +ss_add_test(test_cache + "test_cache.c;${PROJECT_SOURCE_DIR}/src/cache.c;${PROJECT_SOURCE_DIR}/src/utils.c" + "${LIBEV_SHARED};${LIBSODIUM_SHARED}") + +# test_ppbloom - needs bloom +set(TEST_PPBLOOM_LIBS "") +if(WITH_EMBEDDED_SRC) + list(APPEND TEST_PPBLOOM_LIBS bloom-shared) +else() + list(APPEND TEST_PPBLOOM_LIBS ${LIBBLOOM_SHARED}) +endif() +ss_add_test(test_ppbloom + "test_ppbloom.c;${PROJECT_SOURCE_DIR}/src/ppbloom.c;${PROJECT_SOURCE_DIR}/src/utils.c" + "${TEST_PPBLOOM_LIBS};${LIBSODIUM_SHARED};m") + +# test_rule - needs pcre and cork +set(TEST_RULE_LIBS + ${LIBPCRE2_SHARED} + ${LIBSODIUM_SHARED} +) +if(WITH_EMBEDDED_SRC) + list(APPEND TEST_RULE_LIBS cork-shared) +else() + list(APPEND TEST_RULE_LIBS ${LIBCORK_SHARED}) +endif() +ss_add_test(test_rule + "test_rule.c;${PROJECT_SOURCE_DIR}/src/rule.c;${PROJECT_SOURCE_DIR}/src/utils.c" + "${TEST_RULE_LIBS}") + +# test_jconf - needs cork and json +set(TEST_JCONF_LIBS + ${LIBSODIUM_SHARED} + ${LIBEV_SHARED} + ${LIBUDNS_SHARED} +) +if(WITH_EMBEDDED_SRC) + list(APPEND TEST_JCONF_LIBS cork-shared) +else() + list(APPEND TEST_JCONF_LIBS ${LIBCORK_SHARED}) +endif() +ss_add_test(test_jconf + "test_jconf.c;${PROJECT_SOURCE_DIR}/src/jconf.c;${PROJECT_SOURCE_DIR}/src/json.c;${PROJECT_SOURCE_DIR}/src/utils.c;${PROJECT_SOURCE_DIR}/src/netutils.c" + "${TEST_JCONF_LIBS};m") + +# test_buffer - needs sodium and mbedtls for crypto.c buffer functions +ss_add_test(test_buffer + "test_buffer.c;${PROJECT_SOURCE_DIR}/src/crypto.c;${PROJECT_SOURCE_DIR}/src/utils.c;${PROJECT_SOURCE_DIR}/src/base64.c;${PROJECT_SOURCE_DIR}/src/aead.c;${PROJECT_SOURCE_DIR}/src/stream.c;${PROJECT_SOURCE_DIR}/src/ppbloom.c;${PROJECT_SOURCE_DIR}/src/cache.c" + "${LIBSODIUM_SHARED};${LIBMBEDTLS_SHARED};${LIBMBEDCRYPTO_SHARED};${LIBEV_SHARED};${TEST_PPBLOOM_LIBS};m") + +# test_crypto - full crypto test +set(TEST_CRYPTO_LIBS + ${LIBSODIUM_SHARED} + ${LIBMBEDTLS_SHARED} + ${LIBMBEDCRYPTO_SHARED} + ${LIBEV_SHARED} +) +if(WITH_EMBEDDED_SRC) + list(APPEND TEST_CRYPTO_LIBS bloom-shared) +else() + list(APPEND TEST_CRYPTO_LIBS ${LIBBLOOM_SHARED}) +endif() +ss_add_test(test_crypto + "test_crypto.c;${PROJECT_SOURCE_DIR}/src/crypto.c;${PROJECT_SOURCE_DIR}/src/aead.c;${PROJECT_SOURCE_DIR}/src/stream.c;${PROJECT_SOURCE_DIR}/src/ppbloom.c;${PROJECT_SOURCE_DIR}/src/cache.c;${PROJECT_SOURCE_DIR}/src/base64.c;${PROJECT_SOURCE_DIR}/src/utils.c" + "${TEST_CRYPTO_LIBS};m") diff --git a/tests/stress_test.py b/tests/stress_test.py new file mode 100755 index 00000000..0ec13aae --- /dev/null +++ b/tests/stress_test.py @@ -0,0 +1,445 @@ +#!/usr/bin/env python3 +""" +Stress test for shadowsocks-libev: measures bandwidth on loopback +with different ciphers using ss-server and ss-tunnel, and monitors +for memory leaks. + +Usage: + python3 tests/stress_test.py --bin build/bin/ + python3 tests/stress_test.py --bin build/bin/ --size 50 --duration 10 + python3 tests/stress_test.py --bin build/bin/ --cipher aes-256-gcm +""" + +from __future__ import print_function +import argparse +import json +import os +import platform +import signal +import socket +import sys +import threading +import time +from subprocess import Popen + +# Ciphers to test: AEAD (recommended) + common stream ciphers +AEAD_CIPHERS = [ + "aes-128-gcm", + "aes-256-gcm", + "chacha20-ietf-poly1305", +] + +STREAM_CIPHERS = [ + "aes-128-cfb", + "aes-256-cfb", + "aes-256-ctr", + "chacha20-ietf", +] + +ALL_CIPHERS = AEAD_CIPHERS + STREAM_CIPHERS + +# Ports (picked high to avoid conflicts) +SERVER_PORT = 18388 +TUNNEL_LOCAL_PORT = 18389 +SINK_PORT = 18390 + + +def get_rss_kb(pid): + """Get resident set size in KB for a process.""" + system = platform.system() + try: + if system == "Linux": + with open("/proc/%d/status" % pid) as f: + for line in f: + if line.startswith("VmRSS:"): + return int(line.split()[1]) + elif system == "Darwin": + import subprocess + out = subprocess.check_output( + ["ps", "-o", "rss=", "-p", str(pid)], + stderr=subprocess.DEVNULL + ).decode().strip() + if out: + return int(out) + except (IOError, OSError, ValueError): + pass + return None + + +class SinkServer: + """A simple TCP server that accepts connections and discards all data. + Sends back a small acknowledgement per chunk so the sender knows + data was received (for accurate bandwidth measurement).""" + + def __init__(self, port): + self.port = port + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self.sock.bind(("127.0.0.1", port)) + self.sock.listen(5) + self.sock.settimeout(1.0) + self.running = True + self.bytes_received = 0 + self.thread = threading.Thread(target=self._run, daemon=True) + self.thread.start() + + def _run(self): + while self.running: + try: + conn, addr = self.sock.accept() + t = threading.Thread( + target=self._handle, args=(conn,), daemon=True + ) + t.start() + except socket.timeout: + continue + except OSError: + break + + def _handle(self, conn): + conn.settimeout(5.0) + try: + while self.running: + data = conn.recv(65536) + if not data: + break + self.bytes_received += len(data) + except (socket.timeout, OSError): + pass + finally: + conn.close() + + def stop(self): + self.running = False + self.sock.close() + self.thread.join(timeout=3) + + +def server_args(ss_server, cipher, password, server_port): + """Build command-line args for ss-server.""" + return [ + ss_server, + "-s", "127.0.0.1", + "-p", str(server_port), + "-k", password, + "-m", cipher, + ] + + +def tunnel_args(ss_tunnel, cipher, password, server_port, local_port, fwd_host, fwd_port): + """Build command-line args for ss-tunnel.""" + return [ + ss_tunnel, + "-s", "127.0.0.1", + "-p", str(server_port), + "-l", str(local_port), + "-k", password, + "-m", cipher, + "-L", "%s:%d" % (fwd_host, fwd_port), + ] + + +def kill_proc(proc): + """Kill a process gracefully.""" + if proc and proc.poll() is None: + try: + proc.send_signal(signal.SIGTERM) + proc.wait(timeout=5) + except Exception: + try: + proc.kill() + proc.wait(timeout=3) + except Exception: + pass + + +def run_bandwidth_test(bin_dir, cipher, data_size_mb, password="stress_test_pw"): + """Run a single bandwidth test with the given cipher. + + Returns dict with: cipher, bandwidth_mbps, duration_sec, + server_rss_before_kb, server_rss_after_kb, tunnel_rss_before_kb, + tunnel_rss_after_kb, bytes_transferred, error + """ + result = { + "cipher": cipher, + "bandwidth_mbps": 0, + "duration_sec": 0, + "bytes_transferred": 0, + "server_rss_before_kb": None, + "server_rss_after_kb": None, + "tunnel_rss_before_kb": None, + "tunnel_rss_after_kb": None, + "error": None, + } + + ss_server_bin = os.path.join(bin_dir, "ss-server") + ss_tunnel_bin = os.path.join(bin_dir, "ss-tunnel") + + if not os.path.isfile(ss_server_bin) or not os.path.isfile(ss_tunnel_bin): + result["error"] = "binaries not found in %s" % bin_dir + return result + + server_proc = None + tunnel_proc = None + sink = None + devnull = open(os.devnull, "w") + + try: + # Start sink server (the destination ss-tunnel forwards to) + sink = SinkServer(SINK_PORT) + + # Start ss-server + server_proc = Popen( + server_args(ss_server_bin, cipher, password, SERVER_PORT), + stdout=devnull, stderr=devnull, close_fds=True + ) + + # Start ss-tunnel forwarding to the sink + tunnel_proc = Popen( + tunnel_args(ss_tunnel_bin, cipher, password, SERVER_PORT, + TUNNEL_LOCAL_PORT, "127.0.0.1", SINK_PORT), + stdout=devnull, stderr=devnull, close_fds=True + ) + + # Wait for processes to start and bind ports + # Don't probe the ports as ss-tunnel crashes on empty connections + time.sleep(3.0) + + if server_proc.poll() is not None: + result["error"] = "ss-server exited prematurely (code %d)" % server_proc.returncode + return result + + if tunnel_proc.poll() is not None: + result["error"] = "ss-tunnel exited prematurely (code %d)" % tunnel_proc.returncode + return result + + # Record RSS before + result["server_rss_before_kb"] = get_rss_kb(server_proc.pid) + result["tunnel_rss_before_kb"] = get_rss_kb(tunnel_proc.pid) + + # Send data through the tunnel + chunk_size = 64 * 1024 # 64KB chunks + total_bytes = data_size_mb * 1024 * 1024 + data_chunk = b"\x00" * chunk_size + + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.settimeout(30) + try: + sock.connect(("127.0.0.1", TUNNEL_LOCAL_PORT)) + except (socket.error, OSError) as e: + result["error"] = "connect to tunnel failed: %s" % e + return result + + sent = 0 + start_time = time.time() + + try: + while sent < total_bytes: + remaining = total_bytes - sent + to_send = min(chunk_size, remaining) + if to_send < chunk_size: + data_chunk = b"\x00" * to_send + sock.sendall(data_chunk) + sent += to_send + except (socket.error, OSError) as e: + result["error"] = "send error at %d bytes: %s" % (sent, e) + finally: + elapsed = time.time() - start_time + try: + sock.shutdown(socket.SHUT_WR) + except OSError: + pass + sock.close() + + # Wait a bit for data to flow through + time.sleep(0.5) + + result["bytes_transferred"] = sent + result["duration_sec"] = elapsed + if elapsed > 0: + result["bandwidth_mbps"] = (sent * 8) / (elapsed * 1e6) + + # Record RSS after + result["server_rss_after_kb"] = get_rss_kb(server_proc.pid) + result["tunnel_rss_after_kb"] = get_rss_kb(tunnel_proc.pid) + + except Exception as e: + result["error"] = str(e) + finally: + kill_proc(tunnel_proc) + kill_proc(server_proc) + if sink: + sink.stop() + devnull.close() + + return result + + +def format_size(nbytes): + if nbytes >= 1024 * 1024 * 1024: + return "%.1f GB" % (nbytes / (1024 * 1024 * 1024)) + elif nbytes >= 1024 * 1024: + return "%.1f MB" % (nbytes / (1024 * 1024)) + elif nbytes >= 1024: + return "%.1f KB" % (nbytes / 1024) + return "%d B" % nbytes + + +def format_rss(kb): + if kb is None: + return "N/A" + if kb >= 1024: + return "%.1f MB" % (kb / 1024.0) + return "%d KB" % kb + + +def main(): + parser = argparse.ArgumentParser( + description="Stress test ss-server + ss-tunnel bandwidth on loopback" + ) + parser.add_argument( + "--bin", type=str, required=True, + help="Path to directory containing ss-server and ss-tunnel binaries" + ) + parser.add_argument( + "--size", type=int, default=100, + help="Data size to transfer per cipher in MB (default: 100)" + ) + parser.add_argument( + "--cipher", type=str, default=None, + help="Test only this specific cipher" + ) + parser.add_argument( + "--repeat", type=int, default=1, + help="Number of times to repeat each cipher test (default: 1)" + ) + parser.add_argument( + "--stream", action="store_true", + help="Include stream ciphers (deprecated, insecure)" + ) + parser.add_argument( + "--leak-threshold", type=int, default=10240, + help="RSS growth threshold in KB to flag as potential leak (default: 10240)" + ) + parser.add_argument( + "--json", type=str, default=None, + help="Write results as JSON to this file" + ) + args = parser.parse_args() + + # Determine ciphers to test + if args.cipher: + ciphers = [args.cipher] + elif args.stream: + ciphers = ALL_CIPHERS + else: + ciphers = AEAD_CIPHERS + + print("=" * 72) + print("shadowsocks-libev stress test") + print("=" * 72) + print(" Binaries : %s" % os.path.abspath(args.bin)) + print(" Data size: %d MB per cipher" % args.size) + print(" Ciphers : %s" % ", ".join(ciphers)) + print(" Repeats : %d" % args.repeat) + print("=" * 72) + print() + + all_results = [] + leak_warnings = [] + + for cipher in ciphers: + for run in range(args.repeat): + label = cipher + if args.repeat > 1: + label = "%s (run %d/%d)" % (cipher, run + 1, args.repeat) + + sys.stdout.write("Testing %-35s ... " % label) + sys.stdout.flush() + + result = run_bandwidth_test(args.bin, cipher, args.size) + all_results.append(result) + + if result["error"]: + print("FAILED: %s" % result["error"]) + continue + + bw = result["bandwidth_mbps"] + dur = result["duration_sec"] + print("%8.1f Mbps (%5.2fs, %s)" % ( + bw, dur, format_size(result["bytes_transferred"]) + )) + + # Check for memory leaks + for role in ["server", "tunnel"]: + before = result["%s_rss_before_kb" % role] + after = result["%s_rss_after_kb" % role] + if before is not None and after is not None: + growth = after - before + if growth > args.leak_threshold: + msg = ( + " WARNING: ss-%s RSS grew by %s " + "(%s -> %s) during %s test" + % (role, format_rss(growth), + format_rss(before), format_rss(after), cipher) + ) + print(msg) + leak_warnings.append(msg) + + # Summary + print() + print("=" * 72) + print("RESULTS SUMMARY") + print("=" * 72) + print() + print("%-30s %10s %8s %12s %12s" % ( + "Cipher", "Bandwidth", "Time", "Server RSS", "Tunnel RSS" + )) + print("-" * 72) + + for r in all_results: + if r["error"]: + print("%-30s %10s" % (r["cipher"], "FAILED")) + continue + + srv_rss = "" + tun_rss = "" + if r["server_rss_before_kb"] and r["server_rss_after_kb"]: + srv_rss = "%s->%s" % ( + format_rss(r["server_rss_before_kb"]), + format_rss(r["server_rss_after_kb"]) + ) + if r["tunnel_rss_before_kb"] and r["tunnel_rss_after_kb"]: + tun_rss = "%s->%s" % ( + format_rss(r["tunnel_rss_before_kb"]), + format_rss(r["tunnel_rss_after_kb"]) + ) + print("%-30s %7.1f Mbps %6.2fs %12s %12s" % ( + r["cipher"], r["bandwidth_mbps"], r["duration_sec"], + srv_rss or "N/A", tun_rss or "N/A" + )) + + if leak_warnings: + print() + print("MEMORY LEAK WARNINGS:") + for w in leak_warnings: + print(w) + print() + sys.exit(1) + + print() + passed = [r for r in all_results if not r["error"]] + failed = [r for r in all_results if r["error"]] + print("%d passed, %d failed" % (len(passed), len(failed))) + + if args.json: + with open(args.json, "w") as f: + json.dump(all_results, f, indent=2) + print("Results written to %s" % args.json) + + if failed: + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/tests/test_base64.c b/tests/test_base64.c new file mode 100644 index 00000000..f9c9d45f --- /dev/null +++ b/tests/test_base64.c @@ -0,0 +1,116 @@ +#include +#include +#include + +#include "base64.h" + +static void +test_encode_decode(void) +{ + /* Known test vector: "Hello" */ + const uint8_t input[] = "Hello"; + char encoded[BASE64_SIZE(5)]; + uint8_t decoded[5]; + + char *ret = base64_encode(encoded, sizeof(encoded), input, 5); + assert(ret != NULL); + assert(strlen(encoded) > 0); + + int decoded_len = base64_decode(decoded, encoded, sizeof(decoded)); + assert(decoded_len == 5); + assert(memcmp(decoded, input, 5) == 0); +} + +static void +test_empty_input(void) +{ + char encoded[BASE64_SIZE(0)]; + char *ret = base64_encode(encoded, sizeof(encoded), (const uint8_t *)"", 0); + assert(ret != NULL); + assert(strlen(encoded) == 0); +} + +static void +test_single_byte(void) +{ + const uint8_t input[] = { 0x41 }; /* 'A' */ + char encoded[BASE64_SIZE(1)]; + uint8_t decoded[1]; + + char *ret = base64_encode(encoded, sizeof(encoded), input, 1); + assert(ret != NULL); + + int decoded_len = base64_decode(decoded, encoded, sizeof(decoded)); + assert(decoded_len == 1); + assert(decoded[0] == 0x41); +} + +static void +test_two_bytes(void) +{ + const uint8_t input[] = { 0x41, 0x42 }; /* "AB" */ + char encoded[BASE64_SIZE(2)]; + uint8_t decoded[2]; + + char *ret = base64_encode(encoded, sizeof(encoded), input, 2); + assert(ret != NULL); + + int decoded_len = base64_decode(decoded, encoded, sizeof(decoded)); + assert(decoded_len == 2); + assert(decoded[0] == 0x41); + assert(decoded[1] == 0x42); +} + +static void +test_three_bytes(void) +{ + const uint8_t input[] = { 0x00, 0xFF, 0x80 }; + char encoded[BASE64_SIZE(3)]; + uint8_t decoded[3]; + + char *ret = base64_encode(encoded, sizeof(encoded), input, 3); + assert(ret != NULL); + + int decoded_len = base64_decode(decoded, encoded, sizeof(decoded)); + assert(decoded_len == 3); + assert(decoded[0] == 0x00); + assert(decoded[1] == 0xFF); + assert(decoded[2] == 0x80); +} + +static void +test_roundtrip_binary(void) +{ + const uint8_t input[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16 }; + char encoded[BASE64_SIZE(17)]; + uint8_t decoded[17]; + + char *ret = base64_encode(encoded, sizeof(encoded), input, 17); + assert(ret != NULL); + + int decoded_len = base64_decode(decoded, encoded, sizeof(decoded)); + assert(decoded_len == 17); + assert(memcmp(decoded, input, 17) == 0); +} + +static void +test_invalid_chars(void) +{ + /* Character '!' (ASCII 33) is below the base64 range start ('+' = 43) */ + int decoded_len = base64_decode((uint8_t[4]){}, "!!!!", 4); + assert(decoded_len == -1); +} + +int +main(void) +{ + test_encode_decode(); + test_empty_input(); + test_single_byte(); + test_two_bytes(); + test_three_bytes(); + test_roundtrip_binary(); + test_invalid_chars(); + return 0; +} diff --git a/tests/test_buffer.c b/tests/test_buffer.c new file mode 100644 index 00000000..5d5639f9 --- /dev/null +++ b/tests/test_buffer.c @@ -0,0 +1,103 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +int verbose = 0; + +#include "crypto.h" + +/* Provide nonce_cache symbol needed by crypto.c */ +struct cache *nonce_cache = NULL; + +static void +test_balloc(void) +{ + buffer_t buf; + memset(&buf, 0, sizeof(buf)); + + int ret = balloc(&buf, 100); + assert(ret == 0); + (void)ret; + assert(buf.data != NULL); + assert(buf.capacity >= 100); + assert(buf.len == 0); + assert(buf.idx == 0); + + bfree(&buf); + assert(buf.data == NULL); + assert(buf.capacity == 0); +} + +static void +test_brealloc(void) +{ + buffer_t buf; + memset(&buf, 0, sizeof(buf)); + + balloc(&buf, 50); + buf.len = 10; + + /* Grow the buffer */ + int ret = brealloc(&buf, 10, 200); + assert(ret == 0); + (void)ret; + assert(buf.capacity >= 200); + assert(buf.len == 10); + + bfree(&buf); +} + +static void +test_bprepend(void) +{ + buffer_t dst, src; + memset(&dst, 0, sizeof(dst)); + memset(&src, 0, sizeof(src)); + + balloc(&dst, 100); + balloc(&src, 100); + + /* Put some data in src */ + memcpy(src.data, "HEADER", 6); + src.len = 6; + + /* Put some data in dst */ + memcpy(dst.data, "BODY", 4); + dst.len = 4; + + int ret = bprepend(&dst, &src, 200); + assert(ret == 0); + (void)ret; + assert(dst.len == 10); + assert(memcmp(dst.data, "HEADERBODY", 10) == 0); + + bfree(&dst); + bfree(&src); +} + +static void +test_balloc_zero(void) +{ + buffer_t buf; + memset(&buf, 0, sizeof(buf)); + + int ret = balloc(&buf, 0); + assert(ret == 0); + (void)ret; + /* A zero-capacity buffer should still succeed */ + bfree(&buf); +} + +int +main(void) +{ + test_balloc(); + test_brealloc(); + test_bprepend(); + test_balloc_zero(); + return 0; +} diff --git a/tests/test_cache.c b/tests/test_cache.c new file mode 100644 index 00000000..d0e00b7f --- /dev/null +++ b/tests/test_cache.c @@ -0,0 +1,129 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +int verbose = 0; + +#include "cache.h" + +static void +test_create_delete(void) +{ + struct cache *c = NULL; + int ret = cache_create(&c, 100, NULL); + assert(ret == 0); + assert(c != NULL); + + ret = cache_delete(c, 0); + assert(ret == 0); + (void)ret; +} + +static void +test_create_null(void) +{ + int ret = cache_create(NULL, 100, NULL); + assert(ret == EINVAL); + (void)ret; +} + +static void +test_insert_lookup(void) +{ + struct cache *c = NULL; + cache_create(&c, 100, NULL); + + char *data = strdup("test_data"); + cache_insert(c, "key1", 4, data); + + char *result = NULL; + cache_lookup(c, "key1", 4, &result); + assert(result != NULL); + assert(strcmp(result, "test_data") == 0); + + cache_delete(c, 0); +} + +static void +test_key_exist(void) +{ + struct cache *c = NULL; + cache_create(&c, 100, NULL); + + char *data = strdup("value"); + cache_insert(c, "mykey", 5, data); + + assert(cache_key_exist(c, "mykey", 5) == 1); + assert(cache_key_exist(c, "nokey", 5) == 0); + + cache_delete(c, 0); +} + +static void +test_remove(void) +{ + struct cache *c = NULL; + cache_create(&c, 100, NULL); + + char *data = strdup("to_remove"); + cache_insert(c, "rmkey", 5, data); + assert(cache_key_exist(c, "rmkey", 5) == 1); + + cache_remove(c, "rmkey", 5); + assert(cache_key_exist(c, "rmkey", 5) == 0); + + cache_delete(c, 0); +} + +static void +test_lookup_missing(void) +{ + struct cache *c = NULL; + cache_create(&c, 100, NULL); + + char *result = (char *)0xdeadbeef; + cache_lookup(c, "missing", 7, &result); + assert(result == NULL); + + cache_delete(c, 0); +} + +static void +test_eviction(void) +{ + struct cache *c = NULL; + cache_create(&c, 3, NULL); + + /* Insert 3 entries to fill cache */ + cache_insert(c, "k1", 2, strdup("v1")); + cache_insert(c, "k2", 2, strdup("v2")); + cache_insert(c, "k3", 2, strdup("v3")); + + /* This should trigger eviction of the oldest entry */ + cache_insert(c, "k4", 2, strdup("v4")); + + /* k1 should have been evicted */ + assert(cache_key_exist(c, "k1", 2) == 0); + /* k4 should exist */ + assert(cache_key_exist(c, "k4", 2) == 1); + + cache_delete(c, 0); +} + +int +main(void) +{ + test_create_delete(); + test_create_null(); + test_insert_lookup(); + test_key_exist(); + test_remove(); + test_lookup_missing(); + test_eviction(); + return 0; +} diff --git a/tests/test_crypto.c b/tests/test_crypto.c new file mode 100644 index 00000000..7359ad0c --- /dev/null +++ b/tests/test_crypto.c @@ -0,0 +1,164 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +int verbose = 0; + +#include "crypto.h" + +/* Provide nonce_cache symbol needed by crypto.c */ +struct cache *nonce_cache = NULL; + +static void +test_crypto_md5(void) +{ + /* MD5("") = d41d8cd98f00b204e9800998ecf8427e */ + unsigned char result[16]; + crypto_md5((const unsigned char *)"", 0, result); + + unsigned char expected[] = { + 0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04, + 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e + }; + assert(memcmp(result, expected, 16) == 0); + (void)expected; + + /* MD5("abc") = 900150983cd24fb0d6963f7d28e17f72 */ + crypto_md5((const unsigned char *)"abc", 3, result); + unsigned char expected_abc[] = { + 0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0, + 0xd6, 0x96, 0x3f, 0x7d, 0x28, 0xe1, 0x7f, 0x72 + }; + assert(memcmp(result, expected_abc, 16) == 0); + (void)expected_abc; +} + +static void +test_crypto_derive_key(void) +{ + uint8_t key[32]; + + /* derive_key should produce deterministic output from a password */ + int ret = crypto_derive_key("password", key, 32); + assert(ret == 32); + + /* Same password should produce same key */ + uint8_t key2[32]; + ret = crypto_derive_key("password", key2, 32); + assert(ret == 32); + assert(memcmp(key, key2, 32) == 0); + + /* Different password should produce different key */ + uint8_t key3[32]; + ret = crypto_derive_key("different", key3, 32); + assert(ret == 32); + assert(memcmp(key, key3, 32) != 0); + (void)ret; +} + +static void +test_crypto_hkdf(void) +{ + /* RFC 5869 Test Case 1 */ + const mbedtls_md_info_t *md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); + assert(md != NULL); + + unsigned char ikm[22]; + memset(ikm, 0x0b, 22); + + unsigned char salt[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c + }; + + unsigned char info[] = { + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9 + }; + + unsigned char okm[42]; + int ret = crypto_hkdf(md, salt, sizeof(salt), ikm, sizeof(ikm), + info, sizeof(info), okm, sizeof(okm)); + assert(ret == 0); + (void)ret; + + unsigned char expected_okm[] = { + 0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, + 0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36, 0x2f, 0x2a, + 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c, + 0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4, 0xc5, 0xbf, + 0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18, + 0x58, 0x65 + }; + assert(memcmp(okm, expected_okm, 42) == 0); + (void)expected_okm; +} + +static void +test_crypto_hkdf_extract(void) +{ + const mbedtls_md_info_t *md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); + assert(md != NULL); + + unsigned char ikm[22]; + memset(ikm, 0x0b, 22); + + unsigned char salt[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c + }; + + unsigned char prk[32]; + int ret = crypto_hkdf_extract(md, salt, sizeof(salt), ikm, sizeof(ikm), prk); + assert(ret == 0); + (void)ret; + + /* RFC 5869 Test Case 1 PRK */ + unsigned char expected_prk[] = { + 0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf, + 0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63, + 0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31, + 0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, 0xb3, 0xe5 + }; + assert(memcmp(prk, expected_prk, 32) == 0); + (void)expected_prk; +} + +static void +test_crypto_parse_key(void) +{ + /* base64_encode uses URL-safe base64 with -_ instead of +/ */ + uint8_t key[32]; + + /* A known base64-encoded 32-byte key (all zeros) */ + /* 32 zero bytes = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" in standard base64 */ + /* With URL-safe: same since no +/ needed */ + int ret = crypto_parse_key("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", key, 32); + assert(ret == 32); + (void)ret; + + /* All bytes should be 0 */ + for (int i = 0; i < 32; i++) { + assert(key[i] == 0); + } +} + +int +main(void) +{ + if (sodium_init() < 0) { + return 1; + } + + test_crypto_md5(); + test_crypto_derive_key(); + test_crypto_hkdf(); + test_crypto_hkdf_extract(); + test_crypto_parse_key(); + return 0; +} diff --git a/tests/test_jconf.c b/tests/test_jconf.c new file mode 100644 index 00000000..797112e6 --- /dev/null +++ b/tests/test_jconf.c @@ -0,0 +1,85 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +int verbose = 0; + +#include "netutils.h" +#include "jconf.h" + +static void +test_parse_addr_ipv4_with_port(void) +{ + ss_addr_t addr; + memset(&addr, 0, sizeof(addr)); + parse_addr("192.168.1.1:8080", &addr); + assert(addr.host != NULL); + assert(addr.port != NULL); + assert(strcmp(addr.host, "192.168.1.1") == 0); + assert(strcmp(addr.port, "8080") == 0); + free_addr(&addr); +} + +static void +test_parse_addr_ipv6_with_port(void) +{ + ss_addr_t addr; + memset(&addr, 0, sizeof(addr)); + parse_addr("[::1]:443", &addr); + assert(addr.host != NULL); + assert(addr.port != NULL); + assert(strcmp(addr.host, "::1") == 0); + assert(strcmp(addr.port, "443") == 0); + free_addr(&addr); +} + +static void +test_parse_addr_hostname_with_port(void) +{ + ss_addr_t addr; + memset(&addr, 0, sizeof(addr)); + parse_addr("example.com:1234", &addr); + assert(addr.host != NULL); + assert(addr.port != NULL); + assert(strcmp(addr.host, "example.com") == 0); + assert(strcmp(addr.port, "1234") == 0); + free_addr(&addr); +} + +static void +test_parse_addr_no_port(void) +{ + ss_addr_t addr; + memset(&addr, 0, sizeof(addr)); + parse_addr("10.0.0.1", &addr); + assert(addr.host != NULL); + assert(strcmp(addr.host, "10.0.0.1") == 0); + /* Port may be NULL when no port is specified */ + free_addr(&addr); +} + +static void +test_parse_addr_ipv6_no_port(void) +{ + ss_addr_t addr; + memset(&addr, 0, sizeof(addr)); + parse_addr("::1", &addr); + assert(addr.host != NULL); + assert(strcmp(addr.host, "::1") == 0); + free_addr(&addr); +} + +int +main(void) +{ + test_parse_addr_ipv4_with_port(); + test_parse_addr_ipv6_with_port(); + test_parse_addr_hostname_with_port(); + test_parse_addr_no_port(); + test_parse_addr_ipv6_no_port(); + return 0; +} diff --git a/tests/test_json.c b/tests/test_json.c new file mode 100644 index 00000000..9606284d --- /dev/null +++ b/tests/test_json.c @@ -0,0 +1,130 @@ +#include +#include +#include + +#include "json.h" + +static void +test_parse_simple_object(void) +{ + const char *json_str = "{\"key\": \"value\", \"num\": 42}"; + json_value *val = json_parse(json_str, strlen(json_str)); + assert(val != NULL); + assert(val->type == json_object); + assert(val->u.object.length == 2); + + /* Check first entry */ + assert(strcmp(val->u.object.values[0].name, "key") == 0); + assert(val->u.object.values[0].value->type == json_string); + assert(strcmp(val->u.object.values[0].value->u.string.ptr, "value") == 0); + + /* Check second entry */ + assert(strcmp(val->u.object.values[1].name, "num") == 0); + assert(val->u.object.values[1].value->type == json_integer); + assert(val->u.object.values[1].value->u.integer == 42); + + json_value_free(val); +} + +static void +test_parse_array(void) +{ + const char *json_str = "[1, 2, 3]"; + json_value *val = json_parse(json_str, strlen(json_str)); + assert(val != NULL); + assert(val->type == json_array); + assert(val->u.array.length == 3); + assert(val->u.array.values[0]->type == json_integer); + assert(val->u.array.values[0]->u.integer == 1); + assert(val->u.array.values[1]->u.integer == 2); + assert(val->u.array.values[2]->u.integer == 3); + + json_value_free(val); +} + +static void +test_parse_nested(void) +{ + const char *json_str = "{\"outer\": {\"inner\": true}}"; + json_value *val = json_parse(json_str, strlen(json_str)); + assert(val != NULL); + assert(val->type == json_object); + assert(val->u.object.length == 1); + + json_value *outer = val->u.object.values[0].value; + assert(outer->type == json_object); + assert(outer->u.object.length == 1); + + assert(outer->u.object.values[0].value->type == json_boolean); + assert(outer->u.object.values[0].value->u.boolean != 0); + + json_value_free(val); +} + +static void +test_parse_types(void) +{ + const char *json_str = "{\"s\": \"hello\", \"i\": -5, \"d\": 3.14, \"b\": false, \"n\": null}"; + json_value *val = json_parse(json_str, strlen(json_str)); + assert(val != NULL); + assert(val->type == json_object); + assert(val->u.object.length == 5); + + assert(val->u.object.values[0].value->type == json_string); + assert(val->u.object.values[1].value->type == json_integer); + assert(val->u.object.values[1].value->u.integer == -5); + assert(val->u.object.values[2].value->type == json_double); + assert(val->u.object.values[3].value->type == json_boolean); + assert(val->u.object.values[3].value->u.boolean == 0); + assert(val->u.object.values[4].value->type == json_null); + + json_value_free(val); +} + +static void +test_parse_invalid(void) +{ + /* Missing closing brace */ + assert(json_parse("{\"key\": 1", 9) == NULL); + + /* Empty string */ + assert(json_parse("", 0) == NULL); + + /* Just garbage */ + assert(json_parse("not json", 8) == NULL); +} + +static void +test_parse_empty_object(void) +{ + const char *json_str = "{}"; + json_value *val = json_parse(json_str, strlen(json_str)); + assert(val != NULL); + assert(val->type == json_object); + assert(val->u.object.length == 0); + json_value_free(val); +} + +static void +test_parse_empty_array(void) +{ + const char *json_str = "[]"; + json_value *val = json_parse(json_str, strlen(json_str)); + assert(val != NULL); + assert(val->type == json_array); + assert(val->u.array.length == 0); + json_value_free(val); +} + +int +main(void) +{ + test_parse_simple_object(); + test_parse_array(); + test_parse_nested(); + test_parse_types(); + test_parse_invalid(); + test_parse_empty_object(); + test_parse_empty_array(); + return 0; +} diff --git a/tests/test_netutils.c b/tests/test_netutils.c new file mode 100644 index 00000000..beb99dd8 --- /dev/null +++ b/tests/test_netutils.c @@ -0,0 +1,115 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +int verbose = 0; + +#include "netutils.h" + +static void +test_get_sockaddr_len(void) +{ + struct sockaddr_in addr4; + struct sockaddr_in6 addr6; + struct sockaddr_storage unknown; + + memset(&addr4, 0, sizeof(addr4)); + addr4.sin_family = AF_INET; + assert(get_sockaddr_len((struct sockaddr *)&addr4) == sizeof(struct sockaddr_in)); + + memset(&addr6, 0, sizeof(addr6)); + addr6.sin6_family = AF_INET6; + assert(get_sockaddr_len((struct sockaddr *)&addr6) == sizeof(struct sockaddr_in6)); + + memset(&unknown, 0, sizeof(unknown)); + unknown.ss_family = AF_UNSPEC; + assert(get_sockaddr_len((struct sockaddr *)&unknown) == 0); +} + +static void +test_sockaddr_cmp(void) +{ + struct sockaddr_storage a, b; + struct sockaddr_in *a4 = (struct sockaddr_in *)&a; + struct sockaddr_in *b4 = (struct sockaddr_in *)&b; + + /* Same address and port */ + memset(&a, 0, sizeof(a)); + memset(&b, 0, sizeof(b)); + a4->sin_family = AF_INET; + b4->sin_family = AF_INET; + a4->sin_port = htons(80); + b4->sin_port = htons(80); + inet_pton(AF_INET, "127.0.0.1", &a4->sin_addr); + inet_pton(AF_INET, "127.0.0.1", &b4->sin_addr); + assert(sockaddr_cmp(&a, &b, sizeof(struct sockaddr_in)) == 0); + + /* Different port */ + b4->sin_port = htons(81); + assert(sockaddr_cmp(&a, &b, sizeof(struct sockaddr_in)) != 0); +} + +static void +test_sockaddr_cmp_addr(void) +{ + struct sockaddr_storage a, b; + struct sockaddr_in *a4 = (struct sockaddr_in *)&a; + struct sockaddr_in *b4 = (struct sockaddr_in *)&b; + + memset(&a, 0, sizeof(a)); + memset(&b, 0, sizeof(b)); + a4->sin_family = AF_INET; + b4->sin_family = AF_INET; + a4->sin_port = htons(80); + b4->sin_port = htons(443); + inet_pton(AF_INET, "10.0.0.1", &a4->sin_addr); + inet_pton(AF_INET, "10.0.0.1", &b4->sin_addr); + + /* Same address, different port - should be equal */ + assert(sockaddr_cmp_addr(&a, &b, sizeof(struct sockaddr_in)) == 0); + + /* Different address */ + inet_pton(AF_INET, "10.0.0.2", &b4->sin_addr); + assert(sockaddr_cmp_addr(&a, &b, sizeof(struct sockaddr_in)) != 0); +} + +static void +test_validate_hostname(void) +{ + /* Valid hostnames */ + assert(validate_hostname("example.com", 11) == 1); + assert(validate_hostname("sub.example.com", 15) == 1); + assert(validate_hostname("a", 1) == 1); + assert(validate_hostname("a-b", 3) == 1); + assert(validate_hostname("123.456", 7) == 1); + + /* Invalid hostnames */ + assert(validate_hostname(NULL, 0) == 0); + assert(validate_hostname("", 0) == 0); + assert(validate_hostname(".example.com", 12) == 0); /* starts with dot */ + assert(validate_hostname("-example.com", 12) == 0); /* label starts with hyphen */ + assert(validate_hostname("example-.com", 12) == 0); /* label ends with hyphen */ + + /* Too long hostname (> 255) */ + char long_name[260]; + memset(long_name, 'a', 259); + long_name[259] = '\0'; + assert(validate_hostname(long_name, 259) == 0); +} + +int +main(void) +{ + test_get_sockaddr_len(); + test_sockaddr_cmp(); + test_sockaddr_cmp_addr(); + test_validate_hostname(); + return 0; +} diff --git a/tests/test_ppbloom.c b/tests/test_ppbloom.c new file mode 100644 index 00000000..0ef17bbc --- /dev/null +++ b/tests/test_ppbloom.c @@ -0,0 +1,74 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +int verbose = 0; + +#include "ppbloom.h" + +static void +test_init_free(void) +{ + int ret = ppbloom_init(1000, 0.01); + assert(ret == 0); + (void)ret; + ppbloom_free(); +} + +static void +test_add_check(void) +{ + ppbloom_init(1000, 0.01); + + const char *item1 = "hello"; + const char *item2 = "world"; + const char *item3 = "missing"; + + /* Not in filter initially */ + assert(ppbloom_check(item1, strlen(item1)) == 0); + assert(ppbloom_check(item2, strlen(item2)) == 0); + + /* Add items */ + ppbloom_add(item1, strlen(item1)); + ppbloom_add(item2, strlen(item2)); + + /* Should be found */ + assert(ppbloom_check(item1, strlen(item1)) == 1); + assert(ppbloom_check(item2, strlen(item2)) == 1); + + /* Should not be found */ + assert(ppbloom_check(item3, strlen(item3)) == 0); + (void)item3; + + ppbloom_free(); +} + +static void +test_binary_data(void) +{ + ppbloom_init(1000, 0.01); + + const uint8_t data1[] = { 0x00, 0x01, 0x02, 0x03 }; + const uint8_t data2[] = { 0xFF, 0xFE, 0xFD, 0xFC }; + + ppbloom_add(data1, sizeof(data1)); + assert(ppbloom_check(data1, sizeof(data1)) == 1); + assert(ppbloom_check(data2, sizeof(data2)) == 0); + (void)data2; + + ppbloom_free(); +} + +int +main(void) +{ + test_init_free(); + test_add_check(); + test_binary_data(); + return 0; +} diff --git a/tests/test_rule.c b/tests/test_rule.c new file mode 100644 index 00000000..8c14b27a --- /dev/null +++ b/tests/test_rule.c @@ -0,0 +1,126 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +int verbose = 0; + +#include "rule.h" +#include "utils.h" + +static void +test_new_rule(void) +{ + rule_t *rule = new_rule(); + assert(rule != NULL); + assert(rule->pattern == NULL); + assert(rule->pattern_re == NULL); + free(rule); +} + +static void +test_accept_rule_arg(void) +{ + rule_t *rule = new_rule(); + assert(rule != NULL); + + int ret = accept_rule_arg(rule, "^example\\.com$"); + assert(ret == 1); + assert(rule->pattern != NULL); + assert(strcmp(rule->pattern, "^example\\.com$") == 0); + + /* Second call should fail - pattern already set */ + ret = accept_rule_arg(rule, "another"); + assert(ret == -1); + (void)ret; + + free(rule->pattern); + free(rule); +} + +static void +test_init_rule(void) +{ + rule_t *rule = new_rule(); + accept_rule_arg(rule, "^test.*$"); + + int ret = init_rule(rule); + assert(ret == 1); + (void)ret; + assert(rule->pattern_re != NULL); + + if (rule->match_data) + pcre2_match_data_free(rule->match_data); + if (rule->pattern_re) + pcre2_code_free(rule->pattern_re); + free(rule->pattern); + free(rule); +} + +static void +test_init_rule_invalid(void) +{ + rule_t *rule = new_rule(); + accept_rule_arg(rule, "[invalid"); /* Unclosed bracket */ + + int ret = init_rule(rule); + assert(ret == 0); /* Should fail */ + (void)ret; + + free(rule->pattern); + free(rule); +} + +static void +test_lookup_rule(void) +{ + struct cork_dllist rules; + cork_dllist_init(&rules); + + rule_t *rule1 = new_rule(); + accept_rule_arg(rule1, "^google\\.com$"); + init_rule(rule1); + add_rule(&rules, rule1); + + rule_t *rule2 = new_rule(); + accept_rule_arg(rule2, ".*\\.example\\.com$"); + init_rule(rule2); + add_rule(&rules, rule2); + + /* Should match rule1 */ + rule_t *found = lookup_rule(&rules, "google.com", 10); + assert(found == rule1); + + /* Should match rule2 */ + found = lookup_rule(&rules, "sub.example.com", 15); + assert(found == rule2); + + /* Should not match */ + found = lookup_rule(&rules, "other.net", 9); + assert(found == NULL); + (void)found; + + /* Clean up */ + if (rule1->match_data) pcre2_match_data_free(rule1->match_data); + if (rule1->pattern_re) pcre2_code_free(rule1->pattern_re); + free(rule1->pattern); + free(rule1); + if (rule2->match_data) pcre2_match_data_free(rule2->match_data); + if (rule2->pattern_re) pcre2_code_free(rule2->pattern_re); + free(rule2->pattern); + free(rule2); +} + +int +main(void) +{ + test_new_rule(); + test_accept_rule_arg(); + test_init_rule(); + test_init_rule_invalid(); + test_lookup_rule(); + return 0; +} diff --git a/tests/test_utils.c b/tests/test_utils.c new file mode 100644 index 00000000..83046dab --- /dev/null +++ b/tests/test_utils.c @@ -0,0 +1,76 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +int verbose = 0; + +#include "utils.h" + +static void +test_ss_itoa(void) +{ + char *s; + + s = ss_itoa(0); + assert(s != NULL); + assert(strcmp(s, "0") == 0); + + s = ss_itoa(42); + assert(s != NULL); + assert(strcmp(s, "42") == 0); + + s = ss_itoa(-1); + assert(s != NULL); + assert(strcmp(s, "-1") == 0); + + s = ss_itoa(12345); + assert(s != NULL); + assert(strcmp(s, "12345") == 0); + (void)s; +} + +static void +test_ss_isnumeric(void) +{ + assert(ss_isnumeric("12345") == 1); + assert(ss_isnumeric("0") == 1); + assert(ss_isnumeric("") == 0); + assert(ss_isnumeric("abc") == 0); + assert(ss_isnumeric("123abc") == 0); + assert(ss_isnumeric("12.34") == 0); +} + +static void +test_ss_strndup(void) +{ + char *s; + + s = ss_strndup("hello world", 5); + assert(s != NULL); + assert(strcmp(s, "hello") == 0); + assert(strlen(s) == 5); + free(s); + + s = ss_strndup("short", 10); + assert(s != NULL); + assert(strcmp(s, "short") == 0); + free(s); + + s = ss_strndup("", 0); + assert(s != NULL); + assert(strcmp(s, "") == 0); + free(s); +} + +int +main(void) +{ + test_ss_itoa(); + test_ss_isnumeric(); + test_ss_strndup(); + return 0; +}