mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-02 10:53:56 +08:00
💐 Sync 2026-09-30 01:19:34
This commit is contained in:
1 parent
67427d9030
commit
a64d55f7c5
50 files changed
+2837
-589
No files matched your search
@@ -281,6 +281,7 @@ Two further host differences are bridged so the remaining checks still run:
|
||||
| `tests/ucode/test_parser_flatten.uc` | Round-trip invariant for the canonical-Node pipeline: `flatten(normalize(parse_uri(uri)))` must equal the parser's flat UCI dict field-for-field for every supported scheme. This is what stops the parser, the Loader's `PROTOCOL_OPTIONS` (now derived from `parser/mapping.uc`) and the Repository's flatten from drifting apart. |
|
||||
| `tests/ucode/test_subscription_repository.uc` | Integration test for `subscription/repository.uc`: stages a sandboxed UCI file with a user node, a kept subscription node, and a dropped subscription node, then runs `Repository.apply_nodes` and asserts (a) the user node is untouched, (b) the kept node has the new fields and no stale fields, (c) the dropped node is gone, (d) a brand-new node is added under `md5(group+label)`. PR-03 added the `apply_main_node_refs` paths (urltest prune + missing-target switch + reset-to-nil) and the `scrub_stale_urltest_refs` pass to the same test. |
|
||||
| `tests/ucode/test_generators.sh` | Runs `generate_client.uc`/`generate_server.uc` against the UCI fixtures in `tests/fixtures/generators/` inside a scratch directory (the `uci` cursor and `HP_DIR`/`RUN_DIR` are redirected) and validates each result with `sing-box check`. The `redirect` case is the product's default `proxy_mode` and asserts, from the emitted JSON, that every dialling outbound carries the runtime `routing_mark`. |
|
||||
| `tests/ucode/test_dns_proxy_list.sh` | The `proxy_list.txt` → `dns.rules` plumbing and the bootstrap resolver, neither of which `run_case()` in the generator suite can see (it truncates both resource lists before every case and the fixture has no `bootstrap_dns` option). Patches the client fixture to each of the four proxy modes with both lists populated, and asserts the DNS rule order (`direct-domain` < `proxy-domain` via `main-dns` < the SVCB/HTTPS reject, `geosite-cn` last when the mode emits it) plus the routing half's `"tag": "proxy-domain"` and `route.final: main-out`. Then, separately: with `bootstrap_dns` set, `main-dns` must resolve its own hostname through `bootstrap-dns` and that server must carry no `domain_resolver`; with it unset, no `bootstrap-dns` server may be emitted at all. |
|
||||
| `tests/toolchain/build-ucode-linux.sh` | Builds the local ucode toolchain described above; not part of `tests/run.sh`. |
|
||||
| `tests/toolchain/validate-data.sh` | Off-target stand-in for `/sbin/validate_data`, used through `HP_VALIDATE_DATA`; not part of `tests/run.sh`. |
|
||||
| `tests/ucode/test_ucode_grammar.sh` | Pins the ucode dialect: the toolchain must reject `export function ... }` without `;` and object/array destructuring, and must accept the constructs the package uses (`?.`/`??`, object spread, computed keys, template literals). |
|
||||
|
||||
@@ -3282,7 +3282,7 @@
|
||||
"kind": "Value",
|
||||
"name": "dns_server",
|
||||
"title": "DNS server",
|
||||
"description": "Support UDP, TCP, DoH, DoQ, DoT. TCP protocol will be used if not specified.",
|
||||
"description": "Resolves every domain that is not on the China DNS path, through the selected proxy node. Pick an overseas service: a domestic resolver answers these queries with polluted or NXDOMAIN results, which is what makes a foreign site fail to resolve while the proxy itself works. Support UDP, TCP, DoH, DoQ, DoT. TCP protocol will be used if not specified.",
|
||||
"depends": [
|
||||
{
|
||||
"routing_mode": "custom",
|
||||
@@ -3334,7 +3334,7 @@
|
||||
"kind": "Value",
|
||||
"name": "china_dns_server",
|
||||
"title": "China DNS server",
|
||||
"description": "The dns server for resolving China domains. Support UDP, TCP, DoH, DoQ, DoT.",
|
||||
"description": "Resolves the domains on the China path, so it has to be a domestic service to get the correct local CDN answers. Keep the default; the upstream may block UDP/53, in which case a DoH address (https://dns.alidns.com/dns-query, https://doh.pub/dns-query) also works. Support UDP, TCP, DoH, DoQ, DoT.",
|
||||
"depends": [
|
||||
[
|
||||
"routing_mode",
|
||||
@@ -3387,6 +3387,24 @@
|
||||
"rmempty": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "DynamicList",
|
||||
"name": "bootstrap_dns",
|
||||
"title": "Bootstrap DNS server",
|
||||
"description": "Resolver used to look up the DNS server's own hostname - only relevant when that address is a domain, as with a DoH/DoT endpoint. Leave empty to use the WAN resolver. Only the first entry is used.",
|
||||
"depends": [
|
||||
{
|
||||
"routing_mode": "custom",
|
||||
"!reverse": true
|
||||
}
|
||||
],
|
||||
"values": [],
|
||||
"subsection": null,
|
||||
"props": {
|
||||
"datatype": "ipaddr",
|
||||
"modalonly": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "SectionValue",
|
||||
"name": "_dns_cache",
|
||||
|
||||
@@ -432,6 +432,13 @@ fi
|
||||
echo "== generator regression tests =="
|
||||
sh "$ROOT/tests/ucode/test_generators.sh" "$ROOT" "$WORK/generators" || FAILED=1
|
||||
|
||||
echo "== proxy-domain DNS routing and DNS bootstrap =="
|
||||
# The generator suite truncates the resource lists and has no bootstrap option,
|
||||
# so it can see neither proxy_list.txt -> dns.rules (per proxy mode: the
|
||||
# routing half was always mode-agnostic, the DNS half was not) nor the
|
||||
# bootstrap resolver main-dns uses for its own hostname.
|
||||
sh "$ROOT/tests/ucode/test_dns_proxy_list.sh" "$ROOT" "$WORK/dns-proxy-list" || FAILED=1
|
||||
|
||||
echo "== golden protocol snapshot =="
|
||||
sh "$ROOT/tests/ucode/test_golden_outbounds.sh" "$ROOT" "$WORK/golden" || FAILED=1
|
||||
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
#!/bin/sh
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# Two properties of the proxy-mode DNS block that the generator regression
|
||||
# suite cannot see, because run_case() truncates the resource lists and uses a
|
||||
# fixture without the bootstrap option:
|
||||
#
|
||||
# 1. The proxy-domain resource list has to reach the DNS block in *every*
|
||||
# proxy routing mode, not only in bypass_mainland_china.
|
||||
#
|
||||
# proxy_list.txt is the user's escape hatch for a domain the preset lists
|
||||
# mis-classify - the canonical case is Google Play, whose
|
||||
# connect.googleapis.cn resolves through the domestic resolver and then
|
||||
# stays direct, so the store cannot update. The routing half
|
||||
# (route.uc pushes the proxy-domain rule_set to main-out) has always been
|
||||
# emitted in all the proxy modes, while the DNS half used to be nested
|
||||
# inside the bypass_mainland_china branch. In global / gfwlist /
|
||||
# proxy_mainland_china a listed domain therefore still resolved through
|
||||
# the domestic resolver; only the bypass mode did the right thing.
|
||||
#
|
||||
# 2. The bootstrap resolver: the DoH/DoT endpoint (main-dns) is the only
|
||||
# server whose address is a hostname, and it used to borrow default-dns
|
||||
# (the WAN/ISP resolver) for that lookup. When the user configures a
|
||||
# bootstrap resolver, main-dns must point at it instead, and that server
|
||||
# must stay at the bottom of the chain (no domain_resolver of its own,
|
||||
# no DNS rule routing to it) or the lookup depends on itself.
|
||||
#
|
||||
# Usage: sh tests/ucode/test_dns_proxy_list.sh <repo-root> [work-dir]
|
||||
|
||||
ROOT="${1:-.}"
|
||||
WORK="${2:-/tmp/hp-dns-proxy-list-test}"
|
||||
|
||||
ROOT="$(cd "$ROOT" && pwd)"
|
||||
FIXTURE="$ROOT/tests/fixtures/generators/client.uci"
|
||||
DIRECT_DOMAIN="direct.example.cn"
|
||||
PROXY_DOMAIN="play.example.cn"
|
||||
BOOTSTRAP_DNS="223.5.5.5"
|
||||
FAILED=0
|
||||
|
||||
rm -rf "$WORK"
|
||||
mkdir -p "$WORK"
|
||||
|
||||
# Stage the checkout the way test_generators.sh does: HP_DIR/RUN_DIR/UCICONFIG_DIR
|
||||
# are rewritten into the work dir and the fixture is copied in as the UCI
|
||||
# config. The extra bootstrap line is appended to the `config` section here
|
||||
# (the section's first named section), which is how the Loader reads it.
|
||||
stage_case() {
|
||||
dir="$1"
|
||||
label="$2"
|
||||
bootstrap="$3"
|
||||
fixture="$4"
|
||||
|
||||
rm -rf "$dir"
|
||||
mkdir -p "$dir/config" "$dir/run" "$dir/scripts/config" "$dir/scripts/generator" \
|
||||
"$dir/scripts/parser" "$dir/resources"
|
||||
|
||||
cp "$fixture" "$dir/config/homeproxy-pro"
|
||||
if [ -n "$bootstrap" ]; then
|
||||
sed -i "/^\toption routing_mode /i\\
|
||||
\toption bootstrap_dns '$bootstrap'
|
||||
" "$dir/config/homeproxy-pro"
|
||||
fi
|
||||
|
||||
# Both lists are populated on purpose. direct_list.txt is what makes the
|
||||
# direct-domain DNS rule appear at all, and the generator documents that
|
||||
# proxy-domain sits after it; with only one of the two the relative order
|
||||
# is untestable.
|
||||
printf '%s\n' "$DIRECT_DOMAIN" > "$dir/resources/direct_list.txt"
|
||||
printf '%s\n' "$PROXY_DOMAIN" > "$dir/resources/proxy_list.txt"
|
||||
|
||||
VALIDATE_DATA="${HP_VALIDATE_DATA:-/sbin/validate_data}"
|
||||
sed -e "s#^export const HP_DIR = '/etc/homeproxy-pro';#export const HP_DIR = '$dir';#" \
|
||||
-e "s#^export const RUN_DIR = '/var/run/homeproxy-pro';#export const RUN_DIR = '$dir/run';#" \
|
||||
-e "s#^export const UCICONFIG_DIR = '/etc/config';#export const UCICONFIG_DIR = '$dir/config';#" \
|
||||
-e "s#/sbin/validate_data#${VALIDATE_DATA}#" \
|
||||
"$ROOT/root/etc/homeproxy-pro/scripts/homeproxy-pro.uc" > "$dir/scripts/homeproxy-pro.uc"
|
||||
|
||||
cp "$ROOT/root/etc/homeproxy-pro/scripts/config/loader.uc" "$dir/scripts/config/"
|
||||
cp "$ROOT/root/etc/homeproxy-pro/scripts/config/model.uc" "$dir/scripts/config/"
|
||||
cp "$ROOT/root/etc/homeproxy-pro/scripts/config/adapter.uc" "$dir/scripts/config/"
|
||||
cp "$ROOT/root/etc/homeproxy-pro/scripts/parser/"*.uc "$dir/scripts/parser/"
|
||||
cp "$ROOT/root/etc/homeproxy-pro/scripts/generator/"*.uc "$dir/scripts/generator/"
|
||||
cp "$ROOT/root/etc/homeproxy-pro/scripts/generate_client.uc" "$dir/scripts/generate_client.uc"
|
||||
|
||||
# macOS: `sing-box check` rejects the SO_MARK-based routing_mark on a
|
||||
# direct outbound (Linux-only), so the generator copy emits null there.
|
||||
# Same patch as test_generators.sh, applied to the same module.
|
||||
case "$(uname -s)" in
|
||||
Darwin)
|
||||
sed -i '' "s#routing_mark: strToInt(.*self_mark)#routing_mark: null#" \
|
||||
"$dir/scripts/generator/client.uc"
|
||||
;;
|
||||
esac
|
||||
|
||||
if ! ( cd "$dir/scripts" && ucode -L "$dir/scripts" generate_client.uc > "$dir/generate.out" 2> "$dir/generate.err" ); then
|
||||
echo "FAIL: $label: generate_client.uc exited non-zero"
|
||||
head -5 "$dir/generate.err"
|
||||
return 1
|
||||
fi
|
||||
|
||||
json="$dir/run/sing-box-c.json"
|
||||
if [ ! -s "$json" ]; then
|
||||
echo "FAIL: $label: no client configuration was generated"
|
||||
head -5 "$dir/generate.err"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! sing-box check --config "$json"; then
|
||||
echo "FAIL: $label: sing-box rejected the generated configuration"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# The rule_set assertions, shared by every case: the list reaches the config,
|
||||
# the DNS half resolves through main-dns ahead of the SVCB/HTTPS reject, and
|
||||
# direct-domain keeps its precedence.
|
||||
check_proxy_list() {
|
||||
label="$1"
|
||||
json="$2"
|
||||
|
||||
if ! grep -qF "$PROXY_DOMAIN" "$json"; then
|
||||
echo "FAIL: $label: proxy_list.txt never reached the generated config"
|
||||
return 1
|
||||
fi
|
||||
|
||||
dns_rule="$(grep -nF '"rule_set": "proxy-domain"' "$json" | head -1 | cut -d: -f1)"
|
||||
if [ -z "$dns_rule" ]; then
|
||||
echo "FAIL: $label: no proxy-domain rule_set in the generated config"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! sed -n "$((dns_rule + 1)),$((dns_rule + 4))p" "$json" | grep -qF '"main-dns"'; then
|
||||
echo "FAIL: $label: the proxy-domain DNS rule does not resolve through main-dns:"
|
||||
sed -n "$((dns_rule - 3)),$((dns_rule + 4))p" "$json"
|
||||
return 1
|
||||
fi
|
||||
|
||||
reject_line="$(grep -nF '"query_type": [' "$json" | head -1 | cut -d: -f1)"
|
||||
if [ -n "$reject_line" ] && [ "$dns_rule" -gt "$reject_line" ]; then
|
||||
echo "FAIL: $label: the proxy-domain DNS rule is emitted after the SVCB/HTTPS reject"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# A domain present in both lists keeps direct-domain first: the
|
||||
# relocation into the shared prefix must not have reversed the
|
||||
# precedence the bypass block used to have.
|
||||
direct_rule="$(grep -nF '"rule_set": "direct-domain"' "$json" | head -1 | cut -d: -f1)"
|
||||
if [ -z "$direct_rule" ] || [ "$direct_rule" -gt "$dns_rule" ]; then
|
||||
echo "FAIL: $label: direct-domain must be the first DNS rule (direct=${direct_rule:-none} proxy=$dns_rule)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# In the one mode that has both, the domestic lookup still comes last.
|
||||
case "$label" in
|
||||
bypass_mainland_china*)
|
||||
geosite_rule="$(grep -nF '"rule_set": "geosite-cn"' "$json" | head -1 | cut -d: -f1)"
|
||||
if [ -z "$geosite_rule" ] || [ "$geosite_rule" -lt "$dns_rule" ]; then
|
||||
echo "FAIL: $label: geosite-cn must come after proxy-domain (proxy=$dns_rule geosite=${geosite_rule:-none})"
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# --- 1. bootstrap resolver -------------------------------------------------
|
||||
|
||||
dir="$WORK/bootstrap"
|
||||
if stage_case "$dir" "bootstrap" "$BOOTSTRAP_DNS" "$FIXTURE"; then
|
||||
json="$dir/run/sing-box-c.json"
|
||||
|
||||
bootstrap_line="$(grep -nF '"tag": "bootstrap-dns"' "$json" | head -1 | cut -d: -f1)"
|
||||
if [ -z "$bootstrap_line" ]; then
|
||||
echo "FAIL: bootstrap: no bootstrap-dns server was emitted"
|
||||
FAILED=1
|
||||
elif ! sed -n "$bootstrap_line,$((bootstrap_line + 4))p" "$json" | grep -qF "\"$BOOTSTRAP_DNS\""; then
|
||||
echo "FAIL: bootstrap: bootstrap-dns does not carry $BOOTSTRAP_DNS"
|
||||
FAILED=1
|
||||
elif sed -n "$bootstrap_line,$((bootstrap_line + 4))p" "$json" | grep -qF '"domain_resolver"'; then
|
||||
echo "FAIL: bootstrap: the bootstrap resolver has a domain_resolver of its own"
|
||||
FAILED=1
|
||||
else
|
||||
# main-dns is the only hostname-addressed server, so it is the only
|
||||
# one allowed to point at the bootstrap resolver.
|
||||
main_line="$(grep -nF '"tag": "main-dns"' "$json" | head -1 | cut -d: -f1)"
|
||||
if [ -z "$main_line" ]; then
|
||||
echo "FAIL: bootstrap: no main-dns server was emitted"
|
||||
FAILED=1
|
||||
elif ! sed -n "$main_line,$((main_line + 6))p" "$json" | grep -qF '"server": "bootstrap-dns"'; then
|
||||
echo "FAIL: bootstrap: main-dns still resolves through the WAN resolver"
|
||||
FAILED=1
|
||||
else
|
||||
echo "PASS: bootstrap: main-dns resolves its own hostname through bootstrap-dns ($BOOTSTRAP_DNS)"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
FAILED=1
|
||||
fi
|
||||
|
||||
# --- 2. the default (no bootstrap configured) ------------------------------
|
||||
|
||||
dir="$WORK/default"
|
||||
if stage_case "$dir" "default" "" "$FIXTURE"; then
|
||||
json="$dir/run/sing-box-c.json"
|
||||
if grep -qF '"tag": "bootstrap-dns"' "$json"; then
|
||||
echo "FAIL: default: an unconfigured bootstrap resolver was emitted"
|
||||
FAILED=1
|
||||
elif ! grep -qF '"server": "default-dns"' "$json"; then
|
||||
echo "FAIL: default: main-dns no longer resolves through the WAN resolver"
|
||||
FAILED=1
|
||||
else
|
||||
echo "PASS: default: no bootstrap server, main-dns still resolves through default-dns"
|
||||
fi
|
||||
else
|
||||
FAILED=1
|
||||
fi
|
||||
|
||||
# --- 3. proxy-domain in every proxy routing mode ---------------------------
|
||||
|
||||
# One case per proxy routing mode. Custom mode is deliberately absent: it
|
||||
# ignores both resource lists (context.uc gates their inputs on
|
||||
# `routing_mode !== 'custom'`) and has its own user-defined dns_rule sections.
|
||||
for mode in bypass_mainland_china global gfwlist proxy_mainland_china; do
|
||||
dir="$WORK/$mode"
|
||||
sed "s#^\(\s*option routing_mode \).*#\1'$mode'#" "$FIXTURE" > "$WORK/fixture-$mode.uci"
|
||||
if stage_case "$dir" "$mode" "" "$WORK/fixture-$mode.uci"; then
|
||||
if check_proxy_list "$mode" "$dir/run/sing-box-c.json"; then
|
||||
echo "PASS: $mode: direct-domain < proxy-domain via main-dns < SVCB/HTTPS reject"
|
||||
else
|
||||
FAILED=1
|
||||
fi
|
||||
else
|
||||
FAILED=1
|
||||
fi
|
||||
done
|
||||
|
||||
exit $FAILED
|
||||
Reference in new issue
Block a user