feat: add DoH blocking, subscription keyword filtering, bulk proxy text inputs, and insecure TLS flag

This commit is contained in:
Dushnilin
2026-09-09 22:36:12 +03:00
parent 3ee5cf2598
commit b51809c81d
26 changed files with 12547 additions and 9882 deletions
+11
View File
@@ -258,6 +258,11 @@ assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tTachyonTable\ttachyon_inte
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tTachyonTable\ttachyon_interfaces\t{ tun0 }' "runtime base from UCI tun0 interface"
assert_contains "$NFT_LOG" $'nft\tinsert\trule\tinet\tTachyonTable\tmangle\tudp\tdport\t123\treturn' "runtime base from UCI ntp exclusion"
: > "$NFT_LOG"
nft_ucode nft-create-runtime-base TachyonTable localv4 tachyon_subnets tachyon_ports tachyon_ip_ports tachyon_interfaces "br-lan" 0x00100000 0x00200000 198.18.0.0/15 1602 0 "" "" "" "" "" 1
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tTachyonTable\tmangle\tiifname\t@tachyon_interfaces\tip\tdaddr\t1.1.1.1/32\tmeta\tmark\tset\t0x00100000\tcounter' "runtime doh block marking rule"
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tTachyonTable\tmangle\tiifname\t@tachyon_interfaces\tip6\tdaddr\t2606:4700:4700::1111/128\tmeta\tmark\tset\t0x00100000\tcounter' "runtime doh6 block marking rule"
: > "$NFT_LOG"
nft_ucode nft-create-runtime-output-rules TachyonTable localv4 tachyon_subnets tachyon_ports tachyon_ip_ports 0x00100000 198.18.0.0/15
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tTachyonTable\tmangle_output\tip\tdaddr\t@tachyon_subnets\tmeta\tl4proto\ttcp\tmeta\tmark\tset\t0x00100000\tcounter' "runtime output common tcp"
@@ -672,6 +677,8 @@ cat >"$WORK_DIR/signature-expected.txt" <<'EOF_EXPECTED'
br-lan tun0
[settings.exclude_ntp]
1
[settings.block_doh]
0
[settings.game_console_optimizer]
0
[settings.game_console_ips]
@@ -766,6 +773,8 @@ cat >"$WORK_DIR/signature-uci-expected.txt" <<'EOF_EXPECTED'
br-lan tun0
[settings.exclude_ntp]
1
[settings.block_doh]
0
[settings.game_console_optimizer]
0
[settings.game_console_ips]
@@ -816,6 +825,8 @@ cat >"$WORK_DIR/signature-defaults-expected.txt" <<'EOF_EXPECTED'
br-lan
[settings.exclude_ntp]
0
[settings.block_doh]
0
[settings.game_console_optimizer]
0
[settings.game_console_ips]
+2
View File
@@ -563,6 +563,8 @@ sequential
wan
[settings.disable_quic]
1
[settings.block_doh]
0
[settings.update_interval]
2h
[settings.cache_path]
+91
View File
@@ -1043,6 +1043,81 @@ JSON
printf '%s' 'https://example.com/only-xhttp.json' >"$WORK_DIR/subscriptions/only_xhttp-subscription-1.url"
printf '%s' 'Happ' >"$WORK_DIR/subscriptions/only_xhttp-subscription-1.user_agent"
cat >"$WORK_DIR/block-doh-fixture.json" <<'JSON'
{
"settings": {
"config_path": "/tmp/sing-box/config.json",
"dns_server": "1.1.1.1",
"service_listen_address": "127.0.0.1",
"block_doh": "1"
},
"section": [
{
".name": "doh_test",
".type": "section",
"enabled": "1",
"action": "proxy",
"selector_proxy_links": [ "vless://00000000-0000-0000-0000-000000000001@127.0.0.1:443?encryption=none#dummy" ]
}
]
}
JSON
cat >"$WORK_DIR/bulk-proxy-links-fixture.json" <<'JSON'
{
"settings": {
"config_path": "/tmp/sing-box/config.json",
"dns_server": "1.1.1.1",
"service_listen_address": "127.0.0.1"
},
"section": [
{
".name": "sec_test",
".type": "section",
"enabled": "1",
"action": "proxy",
"selector_proxy_links_text": "vless://11111111-1111-4111-8111-111111111111@127.0.0.1:443?encryption=none#BulkOne\n# comment\nvless://22222222-2222-4222-8222-222222222222@127.0.0.1:444?encryption=none#BulkTwo",
"urltest_proxy_links_text": "vless://33333333-3333-4333-8333-333333333333@127.0.0.1:445?encryption=none#BulkUT"
}
]
}
JSON
cat >"$WORK_DIR/subscription-filter-fixture.json" <<'JSON'
{
"settings": {
"config_path": "/tmp/sing-box/config.json",
"dns_server": "1.1.1.1",
"service_listen_address": "127.0.0.1"
},
"section": [
{
".name": "sub_test",
".type": "section",
"enabled": "1",
"action": "proxy",
"subscription_urls": [ "https://example.com/filter-sub.json" ],
"subscription_filter_include_keywords": [ "нидерланды", "германия" ],
"subscription_filter_exclude_keywords": [ "ads" ]
}
]
}
JSON
cat >"$WORK_DIR/subscriptions/sub_test-subscription-1.json" <<'JSON'
{
"outbounds": [
{ "type": "socks", "tag": "nl-fast", "remark": "Нидерланды Fast", "server": "127.0.0.1", "server_port": 1080 },
{ "type": "socks", "tag": "nl-ads", "remark": "Нидерланды Ads Server", "server": "127.0.0.2", "server_port": 1080 },
{ "type": "socks", "tag": "de-highspeed", "remark": "ГЕРМАНИЯ Premium", "server": "127.0.0.3", "server_port": 1080 },
{ "type": "socks", "tag": "us-proxy", "remark": "США Server", "server": "127.0.0.4", "server_port": 1080 }
]
}
JSON
printf '%s' 'https://example.com/filter-sub.json' >"$WORK_DIR/subscriptions/sub_test-subscription-1.url"
generate_config "$WORK_DIR/block-doh-fixture.json" "$WORK_DIR/block-doh.json"
generate_config "$WORK_DIR/bulk-proxy-links-fixture.json" "$WORK_DIR/bulk-proxy-links.json"
generate_config_with_subscription_cache "$WORK_DIR/subscription-filter-fixture.json" "$WORK_DIR/subscription-filter.json"
generate_config "$WORK_DIR/disabled-updates-fixture.json" "$WORK_DIR/disabled.json"
generate_config "$WORK_DIR/default-updates-fixture.json" "$WORK_DIR/default.json"
generate_config "$WORK_DIR/server-inbound-fixture.json" "$WORK_DIR/server.json"
@@ -1428,6 +1503,22 @@ assert(proxy_metadata[0].sourceSection == "proxy-subscription-1" && proxy_metada
assert(proxy_metadata[1].sourceSection == "proxy-subscription-2" && proxy_metadata[1].title == "WolfPN", "proxy source 2 metadata marker");
assert(length(test_metadata) == 1, "same subscription URL metadata kept for second section");
assert(test_metadata[0].sourceSection == "test-subscription-1" && test_metadata[0].title == "WolfPN", "test source metadata marker");
let doh_cfg = cfg("block-doh");
assert(route_rule(doh_cfg, r => r.action == "reject" && contains(r.inbound, "tproxy-in") && contains(r.ip_cidr, "1.1.1.1/32")) != null, "block_doh IPv4 reject rule present");
assert(route_rule(doh_cfg, r => r.action == "reject" && contains(r.inbound, "tproxy-in") && contains(r.ip_cidr, "2606:4700:4700::1111/128")) != null, "block_doh IPv6 reject rule present");
let bulk_cfg = cfg("bulk-proxy-links");
assert(outbound(bulk_cfg, "sec_test-1-out") != null, "bulk selector proxy link 1 created");
assert(outbound(bulk_cfg, "sec_test-2-out") != null, "bulk selector proxy link 2 created");
assert(outbound(bulk_cfg, "sec_test-3-out") != null, "bulk urltest proxy link created");
assert(outbound(bulk_cfg, "sec_test-urltest-out") != null, "bulk urltest group created");
let filter_cfg = cfg("subscription-filter");
assert(outbound(filter_cfg, "nl-fast") != null, "include keyword match preserves node");
assert(outbound(filter_cfg, "de-highspeed") != null, "case-insensitive Cyrillic include keyword match preserves node");
assert(outbound(filter_cfg, "nl-ads") == null, "exclude keyword drops node even if included");
assert(outbound(filter_cfg, "us-proxy") == null, "unmatched include keyword drops node");
' "$WORK_DIR"
printf 'sing-box runtime checks passed\n'
+88
View File
@@ -0,0 +1,88 @@
#!/usr/bin/env bash
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
CACHE_UC="$ROOT_DIR/tachyon/files/usr/lib/subscription/cache.uc"
CONNECTIONS_UC="$ROOT_DIR/tachyon/files/usr/lib/config/connections.uc"
TACHYON_LIB="$ROOT_DIR/tachyon/files/usr/lib"
WORK_DIR="$(mktemp -d)"
cleanup() {
rm -rf "$WORK_DIR"
}
trap cleanup EXIT
fail() {
printf 'FAIL: %s\n' "$1" >&2
exit 1
}
assert_contains() {
local file="$1"
local expected="$2"
local label="$3"
grep -Fq -- "$expected" "$file" || fail "$label: expected '$expected' in '$file'"
}
assert_not_contains() {
local file="$1"
local expected="$2"
local label="$3"
if grep -Fq -- "$expected" "$file"; then
fail "$label: unexpected '$expected' in '$file'"
fi
}
mkdir -p "$WORK_DIR/bin"
cat >"$WORK_DIR/bin/curl" <<SH
#!/bin/sh
printf '%s\n' "\$*" >>"$WORK_DIR/curl.log"
target=""
while [ \$# -gt 0 ]; do
case "\$1" in
-o)
target="\$2"
shift 2
;;
*)
shift
;;
esac
done
if [ -n "\$target" ]; then
printf '%s\n' "dummy subscription data" > "\$target"
fi
exit 0
SH
chmod +x "$WORK_DIR/bin/curl"
export PATH="$WORK_DIR/bin:$PATH"
cache_ucode() {
ucode -L "$TACHYON_LIB" "$CACHE_UC" "$@"
}
# Test 1: cache.download_subscription with allow_insecure = 1
: >"$WORK_DIR/curl.log"
cache_ucode download-subscription "https://example.com/sub" "$WORK_DIR/sub.txt" "" "" "" "" 1
assert_contains "$WORK_DIR/curl.log" "-k" "download_subscription with allow_insecure=1 must pass -k to curl"
# Test 2: cache.download_subscription with allow_insecure = 0
: >"$WORK_DIR/curl.log"
cache_ucode download-subscription "https://example.com/sub" "$WORK_DIR/sub.txt" "" "" "" "" 0
assert_not_contains "$WORK_DIR/curl.log" "-k" "download_subscription with allow_insecure=0 must not pass -k to curl"
# Test 3: connections.uc subscription_insecure helper contract
ucode -L "$TACHYON_LIB" -e '
let conn = require("config.connections");
let sec1 = { "subscription_insecure": "1" };
let sec0 = { "subscription_insecure": "0" };
let sec_unset = {};
if (conn.subscription_insecure(sec1) !== "1") exit(1);
if (conn.subscription_insecure(sec0) !== "0") exit(2);
if (conn.subscription_insecure(sec_unset) !== "0") exit(3);
' || fail "connections.subscription_insecure getter contract"
printf 'subscription insecure checks passed\n'