Add an interoperability test against shadowsocks-rust

A cipher can round-trip perfectly against our own implementation and
still be wrong: header layout, padding rules, and nonce ordering are only
pinned down by talking to another implementation. This drives ss-local
against rust's ssserver and rust's sslocal against ss-server, over a
local origin server so it needs no network access.

Covers the three SIP022 methods plus aes-256-gcm, so a regression in the
legacy path shows up here too. Skips itself when shadowsocks-rust or the
built binaries are absent, so it is safe to run anywhere.
This commit is contained in:
Max Lv
2026-08-19 18:42:39 +08:00
parent 06bbf3d86c
commit 3728a993a7
2 changed files with 131 additions and 0 deletions
+10
View File
@@ -174,6 +174,16 @@ ss_add_test(test_aead2022
"test_aead2022.c;${SS_CRYPTO_TEST_SOURCES}"
"${TEST_CRYPTO_LIBS};m")
# test_interop_rust - interoperability against shadowsocks-rust.
# Skips itself when shadowsocks-rust or the built binaries are absent.
add_test(NAME test_interop_rust
COMMAND bash "${PROJECT_SOURCE_DIR}/tests/test_interop_rust.sh"
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}")
set_tests_properties(test_interop_rust PROPERTIES
LABELS "integration"
TIMEOUT 300
ENVIRONMENT "SS_BIN_DIR=${RUNTIME_SHARED_OUTPUT_DIRECTORY}")
# test_ss_setup - bash unit tests for the ss-setup TUI tool
add_test(NAME test_ss_setup
COMMAND bash "${PROJECT_SOURCE_DIR}/tests/test_ss_setup.sh"
+121
View File
@@ -0,0 +1,121 @@
#!/bin/bash
#
# Interoperability tests against shadowsocks-rust.
#
# The 2022 (SIP022) ciphers are only useful if they interoperate with other
# implementations, and a spec detail can be got wrong in a way that still
# round-trips against ourselves. This drives our binaries against
# shadowsocks-rust's in both directions.
#
# Skips (exit 0) when shadowsocks-rust is not installed, so it is safe to
# run everywhere. Install it with `brew install shadowsocks-rust` or
# `cargo install shadowsocks-rust`.
set -u
BIN_DIR="${SS_BIN_DIR:-build/shared/bin}"
ORIGIN_PORT=${ORIGIN_PORT:-18999}
SERVER_PORT=${SERVER_PORT:-18388}
LOCAL_PORT=${LOCAL_PORT:-18081}
result=0
SPID=""
LPID=""
HPID=""
cleanup() {
[ -n "$SPID" ] && kill -9 "$SPID" 2>/dev/null
[ -n "$LPID" ] && kill -9 "$LPID" 2>/dev/null
[ -n "$HPID" ] && kill -9 "$HPID" 2>/dev/null
wait 2>/dev/null
return 0
}
trap cleanup EXIT
for tool in sslocal ssserver; do
if ! command -v "$tool" > /dev/null 2>&1; then
echo "SKIP: shadowsocks-rust ($tool) not installed"
exit 0
fi
done
for tool in curl python3 openssl; do
if ! command -v "$tool" > /dev/null 2>&1; then
echo "SKIP: $tool not available"
exit 0
fi
done
if [ ! -x "$BIN_DIR/ss-local" ] || [ ! -x "$BIN_DIR/ss-server" ]; then
echo "SKIP: shadowsocks-libev binaries not found in $BIN_DIR"
echo " (set SS_BIN_DIR to override)"
exit 0
fi
# A local origin server keeps the test off the network.
WWW_DIR="$(mktemp -d)"
echo "interop-ok" > "$WWW_DIR/index.html"
(cd "$WWW_DIR" && python3 -m http.server "$ORIGIN_PORT" --bind 127.0.0.1) \
> /dev/null 2>&1 &
HPID=$!
sleep 1
# Fetch through the SOCKS5 proxy and check we got the origin's content back.
fetch_through_proxy() {
curl -s --max-time 15 --socks5-hostname "127.0.0.1:$LOCAL_PORT" \
"http://127.0.0.1:$ORIGIN_PORT/" 2>/dev/null | grep -q "interop-ok"
}
run_case() {
local desc="$1" server_cmd="$2" client_cmd="$3"
printf '\e[0;36mrunning: %s\e[0m\n' "$desc"
$server_cmd > /dev/null 2>&1 &
SPID=$!
sleep 1
$client_cmd > /dev/null 2>&1 &
LPID=$!
sleep 2
if fetch_through_proxy; then
printf '\e[0;32mOK: %s\e[0m\n' "$desc"
else
printf '\e[0;31mFAILED: %s\e[0m\n' "$desc"
result=1
fi
kill -9 "$SPID" "$LPID" 2>/dev/null
wait "$SPID" "$LPID" 2>/dev/null
SPID=""
LPID=""
# let the listening sockets go away before the next case binds them
sleep 2
}
test_method() {
local method="$1" key_size="$2"
local psk
psk="$(openssl rand -base64 "$key_size")"
run_case "$method: libev client -> rust server" \
"ssserver -s 127.0.0.1:$SERVER_PORT -k $psk -m $method" \
"$BIN_DIR/ss-local -s 127.0.0.1 -p $SERVER_PORT -l $LOCAL_PORT -k $psk -m $method"
run_case "$method: rust client -> libev server" \
"$BIN_DIR/ss-server -s 127.0.0.1 -p $SERVER_PORT -k $psk -m $method" \
"sslocal -b 127.0.0.1:$LOCAL_PORT -s 127.0.0.1:$SERVER_PORT -k $psk -m $method"
}
# SIP022. The key sizes are fixed by the spec.
test_method 2022-blake3-aes-128-gcm 16
test_method 2022-blake3-aes-256-gcm 32
test_method 2022-blake3-chacha20-poly1305 32
# A legacy AEAD cipher, to catch a regression that only affects the old path.
test_method aes-256-gcm 32
rm -rf "$WWW_DIR"
exit $result