420 lines
13 KiB
Bash
420 lines
13 KiB
Bash
#!/usr/bin/env bash
|
|
set -eo pipefail
|
|
|
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
|
VALIDATOR="$PODKOP_LIB/config/validator.uc"
|
|
WORK_DIR="$(mktemp -d)"
|
|
|
|
cleanup() {
|
|
rm -rf "$WORK_DIR"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
fail() {
|
|
printf 'FAIL: %s\n' "$1" >&2
|
|
exit 1
|
|
}
|
|
|
|
cat >"$WORK_DIR/context.json" <<'JSON'
|
|
{}
|
|
JSON
|
|
|
|
context="$(cat "$WORK_DIR/context.json")"
|
|
|
|
validate_fixture() {
|
|
local source="$1"
|
|
local normalized="$WORK_DIR/normalized-$(basename "$source")"
|
|
node - "$source" "$normalized" <<'JS'
|
|
const fs = require('fs');
|
|
const input = JSON.parse(fs.readFileSync(process.argv[2], 'utf8'));
|
|
input.settings ??= { '.name': 'settings', '.type': 'settings' };
|
|
if (input.settings.dns_server === undefined) input.settings.dns_server = ['77.88.8.8'];
|
|
if (input.settings.bootstrap_dns_server === undefined) input.settings.bootstrap_dns_server = ['77.88.8.8'];
|
|
fs.writeFileSync(process.argv[3], JSON.stringify(input));
|
|
JS
|
|
PODKOP_LIB="$PODKOP_LIB" ucode -L "$PODKOP_LIB" "$VALIDATOR" validate-runtime-fixture "$normalized" "$context"
|
|
}
|
|
|
|
assert_rejects() {
|
|
local label="$1"
|
|
local fixture="$2"
|
|
local expected="$3"
|
|
local output
|
|
|
|
if output="$(validate_fixture "$fixture" 2>/dev/null)"; then
|
|
fail "$label should be rejected"
|
|
fi
|
|
|
|
printf '%s\n' "$output" | grep -Fq "$expected" ||
|
|
fail "$label: expected message containing '$expected', got '$output'"
|
|
}
|
|
|
|
cat >"$WORK_DIR/valid.json" <<'JSON'
|
|
{
|
|
"settings": {
|
|
".name": "settings",
|
|
".type": "settings",
|
|
"list_update_enabled": "1",
|
|
"update_interval": "1d",
|
|
"latency_test_url": "https://latency.example/generate_204",
|
|
"dns_type": "doh",
|
|
"dns_server": [ "dns.google/dns-query", "cloudflare-dns.com/dns-query" ],
|
|
"bootstrap_dns_server": [ "1.1.1.1", "8.8.8.8" ],
|
|
"dns_check_interval": "10s",
|
|
"dns_recovery_check_interval": "60s",
|
|
"dns_check_timeout": "2s",
|
|
"dns_detour_enabled": "1",
|
|
"dns_detour_section": "proxy",
|
|
"download_lists_via_proxy": "1",
|
|
"download_lists_via_proxy_section": "proxy"
|
|
},
|
|
"section": [
|
|
{
|
|
".name": "detour",
|
|
".type": "section",
|
|
"enabled": "1",
|
|
"action": "outbound",
|
|
"outbound_json": "{\"type\":\"direct\"}",
|
|
"domain_suffix": [ "detour.example" ]
|
|
},
|
|
{
|
|
".name": "proxy",
|
|
".type": "section",
|
|
"enabled": "1",
|
|
"action": "proxy",
|
|
"ports": [ "80", "1000-2000" ],
|
|
"subscription_urls": [ "https://example.com/sub.txt" ],
|
|
"subscription_url_settings": "{\"https://example.com/sub.txt\":{\"user_agent\":\"Agent/1.0\"}}",
|
|
"subscription_update_enabled": "1",
|
|
"subscription_update_interval": "12h",
|
|
"urltest_enabled": "1",
|
|
"urltest_check_interval": "3m",
|
|
"urltest_filter_mode": "mixed",
|
|
"urltest_tolerance": "10000",
|
|
"urltest_testing_url": "https://urltest.example/generate_204",
|
|
"urltest_include_countries": [ "US" ],
|
|
"urltest_exclude_regex": [ "bad.*" ],
|
|
"detect_server_country": "flag_emoji",
|
|
"domain_suffix": [ "example.org", "сайт.рф", "full:exact.example", "full:full:legacy.example", "full:пример.испытание", "keyword:video", "keyword:пример", "regex:^api[.]example$", "regex:^сайт[.]рф$" ],
|
|
"domain_suffix_text": "text.example\nmünich.example\nkeyword:stream",
|
|
"community_lists": [ "discord" ],
|
|
"rule_set": [ "https://example.com/rules.srs" ],
|
|
"rule_set_with_subnets": [ "/tmp/local.json" ],
|
|
"domain_ip_lists": [ "https://example.com/mixed.lst" ],
|
|
"outbound_detour_enabled": "1",
|
|
"outbound_detour_section": "detour"
|
|
},
|
|
{
|
|
".name": "bypass",
|
|
".type": "section",
|
|
"enabled": "1",
|
|
"action": "bypass",
|
|
"domain_suffix": [ "bypass.example" ]
|
|
},
|
|
{
|
|
".name": "zap",
|
|
".type": "section",
|
|
"enabled": "1",
|
|
"action": "zapret",
|
|
"domain_ip_lists": [ "https://example.com/provider.lst" ]
|
|
}
|
|
],
|
|
"server": [
|
|
{
|
|
".name": "srv",
|
|
".type": "server",
|
|
"enabled": "1",
|
|
"routing_mode": "section",
|
|
"routing_section": "proxy"
|
|
}
|
|
]
|
|
}
|
|
JSON
|
|
|
|
validate_fixture "$WORK_DIR/valid.json"
|
|
|
|
cat >"$WORK_DIR/bad-dns-duration.json" <<'JSON'
|
|
{
|
|
"settings": {
|
|
".name": "settings",
|
|
".type": "settings",
|
|
"dns_server": [ "1.1.1.1", "8.8.8.8" ],
|
|
"bootstrap_dns_server": [ "77.88.8.8" ],
|
|
"dns_check_interval": "ten seconds"
|
|
},
|
|
"section": []
|
|
}
|
|
JSON
|
|
assert_rejects "bad DNS interval" "$WORK_DIR/bad-dns-duration.json" "settings.dns_check_interval"
|
|
|
|
cat >"$WORK_DIR/bad-dns-server.json" <<'JSON'
|
|
{
|
|
"settings": {
|
|
".name": "settings",
|
|
".type": "settings",
|
|
"dns_server": [ "1.1.1.1", "bad value" ],
|
|
"bootstrap_dns_server": [ "77.88.8.8" ]
|
|
},
|
|
"section": []
|
|
}
|
|
JSON
|
|
assert_rejects "bad DNS server" "$WORK_DIR/bad-dns-server.json" "Invalid main DNS server"
|
|
|
|
cat >"$WORK_DIR/empty-main-dns.json" <<'JSON'
|
|
{
|
|
"settings": {
|
|
".name": "settings",
|
|
".type": "settings",
|
|
"dns_server": [],
|
|
"bootstrap_dns_server": [ "77.88.8.8" ]
|
|
},
|
|
"section": []
|
|
}
|
|
JSON
|
|
assert_rejects "empty main DNS list" "$WORK_DIR/empty-main-dns.json" "At least one main DNS server is required"
|
|
|
|
cat >"$WORK_DIR/empty-bootstrap-dns.json" <<'JSON'
|
|
{
|
|
"settings": {
|
|
".name": "settings",
|
|
".type": "settings",
|
|
"dns_server": [ "77.88.8.8" ],
|
|
"bootstrap_dns_server": []
|
|
},
|
|
"section": []
|
|
}
|
|
JSON
|
|
assert_rejects "empty Bootstrap DNS list" "$WORK_DIR/empty-bootstrap-dns.json" "At least one Bootstrap DNS server is required"
|
|
|
|
cat >"$WORK_DIR/bad-dns-detour-bypass.json" <<'JSON'
|
|
{
|
|
"settings": {
|
|
".name": "settings",
|
|
".type": "settings",
|
|
"dns_detour_enabled": "1",
|
|
"dns_detour_section": "bypass"
|
|
},
|
|
"section": [
|
|
{ ".name": "bypass", ".type": "section", "enabled": "1", "action": "bypass" }
|
|
]
|
|
}
|
|
JSON
|
|
assert_rejects "DNS detour bypass" "$WORK_DIR/bad-dns-detour-bypass.json" "unsupported action 'bypass'"
|
|
|
|
cat >"$WORK_DIR/bad-direct-action.json" <<'JSON'
|
|
{
|
|
"settings": { ".name": "settings", ".type": "settings" },
|
|
"section": [
|
|
{ ".name": "old", ".type": "section", "enabled": "1", "action": "direct" }
|
|
]
|
|
}
|
|
JSON
|
|
assert_rejects "bad direct action" "$WORK_DIR/bad-direct-action.json" "unsupported action 'direct'"
|
|
|
|
cat >"$WORK_DIR/bad-port.json" <<'JSON'
|
|
{
|
|
"settings": { ".name": "settings", ".type": "settings" },
|
|
"section": [
|
|
{ ".name": "proxy", ".type": "section", "enabled": "1", "action": "proxy", "ports": [ "70000" ] }
|
|
]
|
|
}
|
|
JSON
|
|
assert_rejects "bad port" "$WORK_DIR/bad-port.json" "Invalid port condition '70000'"
|
|
|
|
cat >"$WORK_DIR/bad-subscription.json" <<'JSON'
|
|
{
|
|
"settings": { ".name": "settings", ".type": "settings" },
|
|
"section": [
|
|
{ ".name": "proxy", ".type": "section", "enabled": "1", "action": "proxy", "subscription_urls": [ "https://example.com/a| Agent" ] }
|
|
]
|
|
}
|
|
JSON
|
|
assert_rejects "bad subscription" "$WORK_DIR/bad-subscription.json" "Configure User-Agent in the subscription item settings"
|
|
|
|
cat >"$WORK_DIR/bad-manual-hwid.json" <<'JSON'
|
|
{
|
|
"settings": { ".name": "settings", ".type": "settings" },
|
|
"section": [
|
|
{ ".name": "proxy", ".type": "section", "enabled": "1", "action": "connection" }
|
|
],
|
|
"subscription_url": [
|
|
{
|
|
".name": "proxy_sub_1",
|
|
".type": "subscription_url",
|
|
"section": "proxy",
|
|
"url": "https://example.com/sub.txt",
|
|
"auto_hwid": "0"
|
|
}
|
|
]
|
|
}
|
|
JSON
|
|
assert_rejects "bad manual HWID" "$WORK_DIR/bad-manual-hwid.json" "manual HWID enabled but HWID is empty"
|
|
|
|
cat >"$WORK_DIR/bad-latency-url.json" <<'JSON'
|
|
{
|
|
"settings": {
|
|
".name": "settings",
|
|
".type": "settings",
|
|
"latency_test_url": "ftp://example.com/ping"
|
|
},
|
|
"section": []
|
|
}
|
|
JSON
|
|
assert_rejects "bad latency URL" "$WORK_DIR/bad-latency-url.json" "settings.latency_test_url"
|
|
|
|
cat >"$WORK_DIR/bad-urltest-tolerance.json" <<'JSON'
|
|
{
|
|
"settings": { ".name": "settings", ".type": "settings" },
|
|
"section": [
|
|
{
|
|
".name": "proxy",
|
|
".type": "section",
|
|
"enabled": "1",
|
|
"action": "proxy",
|
|
"urltest_enabled": "1",
|
|
"urltest_tolerance": "10001"
|
|
}
|
|
]
|
|
}
|
|
JSON
|
|
assert_rejects "bad URLTest tolerance" "$WORK_DIR/bad-urltest-tolerance.json" "Use a number from 0 to 10000"
|
|
|
|
cat >"$WORK_DIR/bad-server-routing-bypass.json" <<'JSON'
|
|
{
|
|
"settings": { ".name": "settings", ".type": "settings" },
|
|
"section": [
|
|
{ ".name": "bypass", ".type": "section", "enabled": "1", "action": "bypass" }
|
|
],
|
|
"server": [
|
|
{ ".name": "srv", ".type": "server", "enabled": "1", "routing_mode": "section", "routing_section": "bypass" }
|
|
]
|
|
}
|
|
JSON
|
|
assert_rejects "bad server routing bypass" "$WORK_DIR/bad-server-routing-bypass.json" "unsupported action 'bypass'"
|
|
|
|
cat >"$WORK_DIR/bad-server-routing-block.json" <<'JSON'
|
|
{
|
|
"settings": { ".name": "settings", ".type": "settings" },
|
|
"section": [
|
|
{ ".name": "block", ".type": "section", "enabled": "1", "action": "block" }
|
|
],
|
|
"server": [
|
|
{ ".name": "srv", ".type": "server", "enabled": "1", "routing_mode": "section", "routing_section": "block" }
|
|
]
|
|
}
|
|
JSON
|
|
assert_rejects "bad server routing block" "$WORK_DIR/bad-server-routing-block.json" "unsupported action 'block'"
|
|
|
|
cat >"$WORK_DIR/bad-detour.json" <<'JSON'
|
|
{
|
|
"settings": { ".name": "settings", ".type": "settings" },
|
|
"section": [
|
|
{ ".name": "first", ".type": "section", "enabled": "1", "action": "proxy", "outbound_detour_enabled": "1", "outbound_detour_section": "second" },
|
|
{ ".name": "second", ".type": "section", "enabled": "1", "action": "proxy", "outbound_detour_enabled": "1", "outbound_detour_section": "first" }
|
|
]
|
|
}
|
|
JSON
|
|
assert_rejects "bad detour" "$WORK_DIR/bad-detour.json" "creates a cycle"
|
|
|
|
cat >"$WORK_DIR/bad-list-reference.json" <<'JSON'
|
|
{
|
|
"settings": { ".name": "settings", ".type": "settings" },
|
|
"section": [
|
|
{ ".name": "proxy", ".type": "section", "enabled": "1", "action": "proxy", "domain_ip_lists": [ "ftp://example.com/list.lst" ] }
|
|
]
|
|
}
|
|
JSON
|
|
assert_rejects "bad list reference" "$WORK_DIR/bad-list-reference.json" "Unknown plain list reference"
|
|
|
|
cat >"$WORK_DIR/bad-country.json" <<'JSON'
|
|
{
|
|
"settings": { ".name": "settings", ".type": "settings" },
|
|
"section": [
|
|
{
|
|
".name": "proxy",
|
|
".type": "section",
|
|
"enabled": "1",
|
|
"action": "proxy",
|
|
"urltest_enabled": "1",
|
|
"urltest_filter_mode": "include",
|
|
"urltest_include_countries": [ "USA" ]
|
|
}
|
|
]
|
|
}
|
|
JSON
|
|
assert_rejects "bad country" "$WORK_DIR/bad-country.json" "Invalid country code 'USA'"
|
|
|
|
provider_context="$(node -e 'const fs=require("fs"); const c=JSON.parse(fs.readFileSync(process.argv[1], "utf8")); c.byedpi_installed=true; process.stdout.write(JSON.stringify(c));' "$WORK_DIR/context.json")"
|
|
cat >"$WORK_DIR/bad-byedpi.json" <<'JSON'
|
|
{
|
|
"settings": { ".name": "settings", ".type": "settings", "dns_server": [ "77.88.8.8" ], "bootstrap_dns_server": [ "77.88.8.8" ] },
|
|
"section": [
|
|
{ ".name": "bye", ".type": "section", "enabled": "1", "action": "byedpi", "byedpi_cmd_opts": "--port 1080 --disorder 3" }
|
|
]
|
|
}
|
|
JSON
|
|
if output="$(PODKOP_LIB="$PODKOP_LIB" ucode -L "$PODKOP_LIB" "$VALIDATOR" validate-runtime-fixture "$WORK_DIR/bad-byedpi.json" "$provider_context" 2>/dev/null)"; then
|
|
fail "bad byedpi should be rejected"
|
|
fi
|
|
printf '%s\n' "$output" | grep -Fq "ByeDPI listen address and port are assigned" ||
|
|
fail "bad byedpi: unexpected message '$output'"
|
|
|
|
runtime_lib="$WORK_DIR/runtime-lib"
|
|
mkdir -p "$runtime_lib"
|
|
ln -s "$PODKOP_LIB/core" "$runtime_lib/core"
|
|
ln -s "$PODKOP_LIB/config" "$runtime_lib/config"
|
|
ln -s "$PODKOP_LIB/subscription" "$runtime_lib/subscription"
|
|
ln -s "$PODKOP_LIB/providers" "$runtime_lib/providers"
|
|
touch "$WORK_DIR/ciadpi-provider"
|
|
cat >"$WORK_DIR/bad-byedpi-runtime-state.json" <<'JSON'
|
|
{
|
|
"settings": { ".name": "settings", ".type": "settings", "dns_server": [ "77.88.8.8" ], "bootstrap_dns_server": [ "77.88.8.8" ] },
|
|
"section": [
|
|
{ ".name": "bye", ".type": "section", "enabled": "1", "action": "byedpi", "byedpi_cmd_opts": "--port 1080 --disorder 3" }
|
|
]
|
|
}
|
|
JSON
|
|
|
|
env \
|
|
COMMUNITY_SERVICES="discord" \
|
|
BYEDPI_DEFAULT_CMD_OPTS="" \
|
|
ZAPRET_DEFAULT_NFQWS_OPT="" \
|
|
ZAPRET_LEGACY_DEFAULT_NFQWS_OPT="" \
|
|
ZAPRET2_DEFAULT_NFQWS2_OPT="" \
|
|
BYEDPI_BIN="$WORK_DIR/ciadpi-provider" \
|
|
ZAPRET_PROVIDER_NFQWS_BIN="$WORK_DIR/missing-nfqws" \
|
|
ZAPRET2_PROVIDER_NFQWS2_BIN="$WORK_DIR/missing-nfqws2" \
|
|
ZAPRET_ROUTE_MARK_BASE="0x01000000" \
|
|
ZAPRET_QUEUE_RANGE_SIZE="16" \
|
|
ZAPRET2_ROUTE_MARK_BASE="0x02000000" \
|
|
ZAPRET2_QUEUE_RANGE_SIZE="16" \
|
|
NFT_FAKEIP_MARK="0x00000800" \
|
|
NFT_OUTBOUND_MARK="0x00000400" \
|
|
PODKOP_LIB="$runtime_lib" \
|
|
ucode -L "$runtime_lib" "$VALIDATOR" validate-runtime-fixture "$WORK_DIR/bad-byedpi-runtime-state.json" "{}"
|
|
chmod 755 "$WORK_DIR/ciadpi-provider"
|
|
if output="$(env \
|
|
COMMUNITY_SERVICES="discord" \
|
|
BYEDPI_DEFAULT_CMD_OPTS="" \
|
|
ZAPRET_DEFAULT_NFQWS_OPT="" \
|
|
ZAPRET_LEGACY_DEFAULT_NFQWS_OPT="" \
|
|
ZAPRET2_DEFAULT_NFQWS2_OPT="" \
|
|
BYEDPI_BIN="$WORK_DIR/ciadpi-provider" \
|
|
ZAPRET_PROVIDER_NFQWS_BIN="$WORK_DIR/missing-nfqws" \
|
|
ZAPRET2_PROVIDER_NFQWS2_BIN="$WORK_DIR/missing-nfqws2" \
|
|
ZAPRET_ROUTE_MARK_BASE="0x01000000" \
|
|
ZAPRET_QUEUE_RANGE_SIZE="16" \
|
|
ZAPRET2_ROUTE_MARK_BASE="0x02000000" \
|
|
ZAPRET2_QUEUE_RANGE_SIZE="16" \
|
|
NFT_FAKEIP_MARK="0x00000800" \
|
|
NFT_OUTBOUND_MARK="0x00000400" \
|
|
PODKOP_LIB="$runtime_lib" \
|
|
ucode -L "$runtime_lib" "$VALIDATOR" validate-runtime-fixture "$WORK_DIR/bad-byedpi-runtime-state.json" "{}" 2>/dev/null)"; then
|
|
fail "executable byedpi provider should enable strategy validation"
|
|
fi
|
|
printf '%s\n' "$output" | grep -Fq "ByeDPI listen address and port are assigned" ||
|
|
fail "executable byedpi provider: unexpected message '$output'"
|
|
|
|
printf 'config validator runtime checks passed\n'
|