fix: разделение правил маршрутизации секции (fix коллизии AND) и поддержка http/https ссылок
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eo pipefail
|
||||
|
||||
TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd "$TEST_DIR/.." && pwd)"
|
||||
TACHYON_LIB="$ROOT_DIR/tachyon/files/usr/lib"
|
||||
GENERATOR_UC="$TACHYON_LIB/singbox/generator.uc"
|
||||
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
trap 'rm -rf "$WORK_DIR"' EXIT
|
||||
|
||||
fail() {
|
||||
echo "FAIL: $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 1. Test coexistence: section with both domains and community_lists MUST generate separate rules
|
||||
cat >"$WORK_DIR/fixture.json" <<'JSON'
|
||||
{
|
||||
"settings": {
|
||||
".name": "settings",
|
||||
".type": "settings",
|
||||
"enabled": "1",
|
||||
"dns_type": "udp",
|
||||
"dns_server": "1.1.1.1",
|
||||
"service_listen_address": "127.0.0.1"
|
||||
},
|
||||
"section": [
|
||||
{
|
||||
".name": "mixed_sec",
|
||||
".type": "section",
|
||||
"enabled": "1",
|
||||
"action": "connection",
|
||||
"outbound_jsons": [ "{\"type\":\"direct\"}" ],
|
||||
"domain_suffix": [ "customdomain.org" ],
|
||||
"community_lists": [ "discord" ]
|
||||
}
|
||||
]
|
||||
}
|
||||
JSON
|
||||
|
||||
output="$WORK_DIR/out.json"
|
||||
mkdir -p "$output.section-cache" "$output.rulesets"
|
||||
ucode -L "$TACHYON_LIB" "$GENERATOR_UC" generate-config-fixture \
|
||||
"$WORK_DIR/fixture.json" "$output" "127.0.0.1" "0" "1"
|
||||
|
||||
# Verify route rules in out.json
|
||||
ucode -e '
|
||||
let fs = require("fs");
|
||||
let raw = fs.readfile("'"$output"'");
|
||||
let cfg = json(raw);
|
||||
let rules = cfg.route.rules || [];
|
||||
|
||||
let found_domain_only = false;
|
||||
let found_ruleset_only = false;
|
||||
let found_combined = false;
|
||||
|
||||
for (let r in rules) {
|
||||
if (r.domain_suffix && r.rule_set) {
|
||||
found_combined = true;
|
||||
}
|
||||
if (r.domain_suffix && !r.rule_set) {
|
||||
for (let d in r.domain_suffix) {
|
||||
if (d == "customdomain.org")
|
||||
found_domain_only = true;
|
||||
}
|
||||
}
|
||||
if (r.rule_set && !r.domain_suffix) {
|
||||
let rs_str = sprintf("%s", r.rule_set);
|
||||
if (index(rs_str, "discord") >= 0)
|
||||
found_ruleset_only = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (found_combined) {
|
||||
print("ERROR: found merged domain_suffix AND rule_set in single rule! Sing-box treats this as logical AND!\n");
|
||||
exit(1);
|
||||
}
|
||||
if (!found_domain_only) {
|
||||
print("ERROR: domain_suffix rule missing for customdomain.org\n");
|
||||
exit(2);
|
||||
}
|
||||
if (!found_ruleset_only) {
|
||||
print("ERROR: rule_set rule missing for discord\n");
|
||||
exit(3);
|
||||
}
|
||||
' || fail "Coexistence check failed: domain and ruleset rules are not cleanly separated"
|
||||
|
||||
# 2. Verify DPI-out is never selected as download_detour
|
||||
cat >"$WORK_DIR/fixture_dpi.json" <<'JSON'
|
||||
{
|
||||
"settings": {
|
||||
".name": "settings",
|
||||
".type": "settings",
|
||||
"enabled": "1",
|
||||
"dns_type": "udp",
|
||||
"dns_server": "1.1.1.1",
|
||||
"service_listen_address": "127.0.0.1"
|
||||
},
|
||||
"section": [
|
||||
{
|
||||
".name": "dpi_sec",
|
||||
".type": "section",
|
||||
"enabled": "1",
|
||||
"action": "connection",
|
||||
"outbound_jsons": [
|
||||
"{\"tag\":\"DPI-out\",\"type\":\"socks\",\"server\":\"127.0.0.1\",\"server_port\":1080}"
|
||||
],
|
||||
"community_lists": [ "discord" ]
|
||||
}
|
||||
]
|
||||
}
|
||||
JSON
|
||||
|
||||
output_dpi="$WORK_DIR/out_dpi.json"
|
||||
mkdir -p "$output_dpi.section-cache" "$output_dpi.rulesets"
|
||||
ucode -L "$TACHYON_LIB" "$GENERATOR_UC" generate-config-fixture \
|
||||
"$WORK_DIR/fixture_dpi.json" "$output_dpi" "127.0.0.1" "0" "1"
|
||||
|
||||
ucode -e '
|
||||
let fs = require("fs");
|
||||
let raw = fs.readfile("'"$output_dpi"'");
|
||||
let cfg = json(raw);
|
||||
let rulesets = cfg.route.rule_set || [];
|
||||
for (let rs in rulesets) {
|
||||
if (rs.download_detour == "DPI-out") {
|
||||
print("ERROR: DPI-out was selected as download_detour!\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
' || fail "DPI-out must never be set as download_detour"
|
||||
|
||||
printf "Route rules coexistence and detour checks passed\n"
|
||||
@@ -0,0 +1,143 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eo pipefail
|
||||
|
||||
TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd "$TEST_DIR/.." && pwd)"
|
||||
TACHYON_LIB="$ROOT_DIR/tachyon/files/usr/lib"
|
||||
PARSER_UC="$TACHYON_LIB/subscription/parser.uc"
|
||||
GENERATOR_UC="$TACHYON_LIB/singbox/generator.uc"
|
||||
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
trap 'rm -rf "$WORK_DIR"' EXIT
|
||||
|
||||
fail() {
|
||||
echo "FAIL: $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 1. Test parsing http:// and https:// share links
|
||||
cat >"$WORK_DIR/http_links.txt" <<'EOF'
|
||||
http://admin:secret123@192.168.1.50:8080#My-Http-Proxy
|
||||
https://user:pass@proxy.example.com:8443?sni=secure.example.com&insecure=1#My-Https-Proxy
|
||||
EOF
|
||||
|
||||
output_json="$WORK_DIR/parsed.json"
|
||||
ucode -L "$TACHYON_LIB" "$PARSER_UC" normalize-uri-list "$WORK_DIR/http_links.txt" "$output_json"
|
||||
|
||||
ucode -e '
|
||||
let fs = require("fs");
|
||||
let parsed = json(fs.readfile("'"$output_json"'"));
|
||||
let list = parsed.outbounds || [];
|
||||
if (length(list) != 2) {
|
||||
print("Expected 2 outbounds, got " + length(list) + "\n");
|
||||
exit(1);
|
||||
}
|
||||
let http = list[0];
|
||||
if (http.type != "http" || http.server != "192.168.1.50" || http.server_port != 8080 ||
|
||||
http.username != "admin" || http.password != "secret123" || http.tag != "My-Http-Proxy") {
|
||||
print("HTTP outbound fields incorrect: " + sprintf("%J", http) + "\n");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
let https = list[1];
|
||||
if (https.type != "http" || https.server != "proxy.example.com" || https.server_port != 8443 ||
|
||||
https.username != "user" || https.password != "pass" || https.tag != "My-Https-Proxy" ||
|
||||
!https.tls || !https.tls.enabled || https.tls.server_name != "secure.example.com" || !https.tls.insecure) {
|
||||
print("HTTPS outbound fields incorrect: " + sprintf("%J", https) + "\n");
|
||||
exit(3);
|
||||
}
|
||||
' || fail "HTTP/HTTPS share link parsing failed"
|
||||
|
||||
# 2. Test serializing http/https outbound to share link
|
||||
ucode -L "$TACHYON_LIB" -e '
|
||||
let share = require("subscription.share_link");
|
||||
let http_out = {
|
||||
type: "http",
|
||||
server: "1.2.3.4",
|
||||
server_port: 8080,
|
||||
username: "alice",
|
||||
password: "foo",
|
||||
tag: "test-http"
|
||||
};
|
||||
let link = share.serialize_outbound_link(http_out);
|
||||
if (link != "http://alice:foo@1.2.3.4:8080#test-http") {
|
||||
print("Failed http serialize: " + link + "\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
let https_out = {
|
||||
type: "http",
|
||||
server: "secure.net",
|
||||
server_port: 443,
|
||||
tls: {
|
||||
enabled: true,
|
||||
server_name: "sni.secure.net",
|
||||
insecure: true
|
||||
},
|
||||
tag: "test-https"
|
||||
};
|
||||
let link_tls = share.serialize_outbound_link(https_out);
|
||||
if (link_tls != "https://secure.net:443?sni=sni.secure.net&insecure=1#test-https") {
|
||||
print("Failed https serialize: " + link_tls + "\n");
|
||||
exit(2);
|
||||
}
|
||||
' || fail "HTTP/HTTPS serialization failed"
|
||||
|
||||
# 3. Test UDP reject rule generation for HTTP outbound to prevent sing-box crash
|
||||
cat >"$WORK_DIR/fixture.json" <<'JSON'
|
||||
{
|
||||
"settings": {
|
||||
".name": "settings",
|
||||
".type": "settings",
|
||||
"enabled": "1",
|
||||
"dns_type": "udp",
|
||||
"dns_server": "1.1.1.1",
|
||||
"service_listen_address": "127.0.0.1"
|
||||
},
|
||||
"section": [
|
||||
{
|
||||
".name": "http_sec",
|
||||
".type": "section",
|
||||
"enabled": "1",
|
||||
"action": "connection",
|
||||
"outbound_jsons": [
|
||||
"{\"tag\":\"http-out\",\"type\":\"http\",\"server\":\"1.2.3.4\",\"server_port\":8080}"
|
||||
],
|
||||
"domain_suffix": [ "example.com" ]
|
||||
}
|
||||
]
|
||||
}
|
||||
JSON
|
||||
|
||||
gen_out="$WORK_DIR/gen.json"
|
||||
mkdir -p "$gen_out.section-cache" "$gen_out.rulesets"
|
||||
ucode -L "$TACHYON_LIB" "$GENERATOR_UC" generate-config-fixture \
|
||||
"$WORK_DIR/fixture.json" "$gen_out" "127.0.0.1" "0" "1"
|
||||
|
||||
ucode -e '
|
||||
let fs = require("fs");
|
||||
let cfg = json(fs.readfile("'"$gen_out"'"));
|
||||
let rules = cfg.route.rules || [];
|
||||
let found_reject = false;
|
||||
let found_http_route = false;
|
||||
|
||||
for (let r in rules) {
|
||||
if (r.network == "udp" && (r.action == "reject" || r.outbound == "block")) {
|
||||
found_reject = true;
|
||||
}
|
||||
if (r.outbound == "http_sec-out") {
|
||||
found_http_route = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_reject) {
|
||||
print("ERROR: Expected UDP reject rule before HTTP outbound route rule!\n");
|
||||
exit(1);
|
||||
}
|
||||
if (!found_http_route) {
|
||||
print("ERROR: Expected route rule for http_sec-out!\n");
|
||||
exit(2);
|
||||
}
|
||||
' || fail "UDP reject rule for HTTP outbound failed"
|
||||
|
||||
printf "Subscription HTTP tests passed\n"
|
||||
Reference in New Issue
Block a user