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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* Fix test build on Linux: add stdint.h and link libm

- test_ppbloom.c: add missing #include <stdint.h> for uint8_t
- test_json: link against libm for pow() used in json.c

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* Fix Linux build: link libm for test_jconf (json.c uses pow)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Max Lv
2026-02-07 17:06:47 +08:00
committed by GitHub
co-authored by Claude Opus 4.6
parent b2a0c545d8
commit 7bb250739e
50 changed files with 2293 additions and 2039 deletions
+71 -23
View File
@@ -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
-115
View File
@@ -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
+14 -7
View File
@@ -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);
}
+4 -6
View File
@@ -33,17 +33,15 @@
#include <libcork/ds.h>
#ifdef HAVE_PCRE_H
#include <pcre.h>
#elif HAVE_PCRE_PCRE_H
#include <pcre/pcre.h>
#endif
#define PCRE2_CODE_UNIT_WIDTH 8
#include <pcre2.h>
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;