mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-02 01:23:56 +08:00
💐 Sync 2026-10-01 01:11:54
This commit is contained in:
1 parent
8f2ec5c4da
commit
2909cae209
44 files changed
+1092
-2287
No files matched your search
@@ -1,51 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# DNS rule action fields that sing-box validates at load time
|
||||
# (route/rule/rule_dns.go, validateDNSRuleAction):
|
||||
#
|
||||
# * `race` and `speculative` cannot be combined on the same rule;
|
||||
# * `client_subnet` and `remove_client_subnet` are mutually exclusive.
|
||||
#
|
||||
# The rules below are a *legal* baseline: both sides of each pair are off.
|
||||
# The testbed opens one side with a one-line sed variation, so the case that
|
||||
# must be refused differs from the case that must pass by exactly the flag
|
||||
# under test.
|
||||
|
||||
config homeproxy-pro 'config'
|
||||
option routing_mode 'custom'
|
||||
option proxy_mode 'redirect_tproxy'
|
||||
option ipv6_support '1'
|
||||
option log_level 'error'
|
||||
|
||||
config homeproxy-pro 'routing'
|
||||
option default_outbound 'direct-out'
|
||||
option default_outbound_dns 'default-dns'
|
||||
|
||||
config homeproxy-pro 'infra'
|
||||
option mixed_port '5330'
|
||||
option dns_port '5333'
|
||||
option redirect_port '5331'
|
||||
option self_mark '100'
|
||||
|
||||
config dns_rule 'dr_race'
|
||||
option enabled '1'
|
||||
option label 'dr-race'
|
||||
option action 'route'
|
||||
option server 'default-dns'
|
||||
option race '0'
|
||||
option speculative '0'
|
||||
|
||||
config dns_rule 'dr_subnet'
|
||||
option enabled '1'
|
||||
option label 'dr-subnet'
|
||||
option action 'route'
|
||||
option server 'default-dns'
|
||||
option client_subnet '1.2.3.0/24'
|
||||
option remove_client_subnet '0'
|
||||
|
||||
config dns_rule 'dr_eval'
|
||||
option enabled '1'
|
||||
option label 'dr-eval'
|
||||
option action 'evaluate'
|
||||
option server 'default-dns'
|
||||
option dns_timeout '5'
|
||||
@@ -1,33 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# A DNS server carrying HTTP headers. UCI stores them as a string list
|
||||
# ("Name: Value" per entry), sing-box wants map[string][]string, and emitting
|
||||
# the UCI list verbatim makes sing-box refuse the whole configuration
|
||||
# ("cannot unmarshal array into Go value of type badoption.HTTPHeader").
|
||||
# The generator converts one shape into the other; a name that appears twice
|
||||
# becomes a two-value header.
|
||||
|
||||
config homeproxy-pro 'config'
|
||||
option routing_mode 'custom'
|
||||
option proxy_mode 'redirect_tproxy'
|
||||
option ipv6_support '1'
|
||||
option log_level 'error'
|
||||
|
||||
config homeproxy-pro 'routing'
|
||||
option default_outbound 'direct-out'
|
||||
option default_outbound_dns 'default-dns'
|
||||
|
||||
config homeproxy-pro 'infra'
|
||||
option mixed_port '5330'
|
||||
option dns_port '5333'
|
||||
option redirect_port '5331'
|
||||
option self_mark '100'
|
||||
|
||||
config dns_server 'ds_https'
|
||||
option enabled '1'
|
||||
option type 'https'
|
||||
option server '1.1.1.1'
|
||||
option path '/dns-query'
|
||||
list headers 'X-Test: one'
|
||||
list headers 'X-Test: two'
|
||||
list headers 'Authorization: Bearer t'
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# A routing rule pointing at a routing_node that was switched off afterwards.
|
||||
# generate_outbound() skips disabled nodes, so the tag the rule refers to is
|
||||
# generated nowhere and sing-box refuses the whole configuration with
|
||||
# "outbound not found: cfg-rn_off-out" - a message that never names the node
|
||||
# the user turned off. get_outbound() now refuses it the way
|
||||
# get_resolver()/get_ruleset() already do.
|
||||
|
||||
config homeproxy-pro 'config'
|
||||
option routing_mode 'custom'
|
||||
option proxy_mode 'redirect_tproxy'
|
||||
option ipv6_support '1'
|
||||
option log_level 'error'
|
||||
|
||||
config homeproxy-pro 'routing'
|
||||
option default_outbound 'direct-out'
|
||||
option default_outbound_dns 'default-dns'
|
||||
|
||||
config homeproxy-pro 'infra'
|
||||
option mixed_port '5330'
|
||||
option dns_port '5333'
|
||||
option redirect_port '5331'
|
||||
option self_mark '100'
|
||||
|
||||
config node 'n_direct'
|
||||
option label 'n-direct'
|
||||
option type 'direct'
|
||||
|
||||
config routing_node 'rn_off'
|
||||
option enabled '0'
|
||||
option label 'rn-off'
|
||||
option node 'n_direct'
|
||||
|
||||
config routing_rule 'rr_off'
|
||||
option enabled '1'
|
||||
option label 'rr-off'
|
||||
option action 'route'
|
||||
option outbound 'rn_off'
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# A routing rule on the action the UI offers but gives no field to fill.
|
||||
# Match fields belong to the rule, not to the action payload, so this rule
|
||||
# reaches sing-box as `{"action": "route-options", ...}` with an empty action
|
||||
# object, and RouteOptionsActionOptions.UnmarshalJSON() refuses exactly that
|
||||
# with "empty route option action" - taking the whole configuration with it.
|
||||
# The generator refuses it by name before writing anything.
|
||||
|
||||
config homeproxy-pro 'config'
|
||||
option routing_mode 'custom'
|
||||
option proxy_mode 'redirect_tproxy'
|
||||
option ipv6_support '1'
|
||||
option log_level 'error'
|
||||
|
||||
config homeproxy-pro 'routing'
|
||||
option default_outbound 'direct-out'
|
||||
option default_outbound_dns 'default-dns'
|
||||
|
||||
config homeproxy-pro 'infra'
|
||||
option mixed_port '5330'
|
||||
option dns_port '5333'
|
||||
option redirect_port '5331'
|
||||
option self_mark '100'
|
||||
|
||||
config routing_rule 'rr_empty'
|
||||
option enabled '1'
|
||||
option label 'rr-empty'
|
||||
option action 'route-options'
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# Both TLS fragmentation switches at once. RouteOptionsActionOptions's
|
||||
# UnmarshalJSON() treats them as mutually exclusive
|
||||
# ("`tls_fragment` and `tls_record_fragment` are mutually exclusive"), and
|
||||
# the form shows both switches without hiding either when the other is on -
|
||||
# a user following the description ("try TLS record fragment first") can
|
||||
# easily tick both.
|
||||
|
||||
config homeproxy-pro 'config'
|
||||
option routing_mode 'custom'
|
||||
option proxy_mode 'redirect_tproxy'
|
||||
option ipv6_support '1'
|
||||
option log_level 'error'
|
||||
|
||||
config homeproxy-pro 'routing'
|
||||
option default_outbound 'direct-out'
|
||||
option default_outbound_dns 'default-dns'
|
||||
|
||||
config homeproxy-pro 'infra'
|
||||
option mixed_port '5330'
|
||||
option dns_port '5333'
|
||||
option redirect_port '5331'
|
||||
option self_mark '100'
|
||||
|
||||
config routing_rule 'rr_tls'
|
||||
option enabled '1'
|
||||
option label 'rr-tls'
|
||||
option action 'route'
|
||||
option outbound 'direct-out'
|
||||
option tls_fragment '1'
|
||||
option tls_record_fragment '1'
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# A remote rule-set whose initial_path points outside the homeproxy-pro whitelist.
|
||||
# The path is read off the local disk as root, so the same gate as a local
|
||||
# rule-set's path applies - and it has to fail the same way. It used to be
|
||||
# dropped silently (initial_path := null), which left a rule-set the user
|
||||
# believed had a local copy downloading at first start instead.
|
||||
|
||||
config homeproxy-pro 'config'
|
||||
option routing_mode 'custom'
|
||||
option proxy_mode 'redirect_tproxy'
|
||||
option ipv6_support '1'
|
||||
option log_level 'error'
|
||||
|
||||
config homeproxy-pro 'routing'
|
||||
option default_outbound 'direct-out'
|
||||
option default_outbound_dns 'default-dns'
|
||||
|
||||
config homeproxy-pro 'infra'
|
||||
option mixed_port '5330'
|
||||
option dns_port '5333'
|
||||
option redirect_port '5331'
|
||||
option self_mark '100'
|
||||
|
||||
config ruleset 'rs_seed'
|
||||
option enabled '1'
|
||||
option label 'rs-seed'
|
||||
option type 'remote'
|
||||
option format 'binary'
|
||||
option url 'https://example.invalid/ruleset.srs'
|
||||
option initial_path '/etc/passwd'
|
||||
|
||||
config routing_rule 'rr_seed'
|
||||
option enabled '1'
|
||||
option label 'rr-seed'
|
||||
option action 'route'
|
||||
option outbound 'direct-out'
|
||||
option rule_set 'rs_seed'
|
||||
@@ -1,51 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# A remote rule-set in a preset routing mode. It used to be fetched through
|
||||
# direct-out regardless of the mode, which fails on a router that can only
|
||||
# reach the rule-set host through the proxy - and the only trace is a line in
|
||||
# the sing-box log. The built-in geoip-cn / geosite-cn rule-sets already
|
||||
# download through main-out (route.uc sets download_detour for them).
|
||||
|
||||
config homeproxy-pro 'config'
|
||||
option routing_mode 'bypass_mainland_china'
|
||||
option proxy_mode 'redirect_tproxy'
|
||||
option ipv6_support '1'
|
||||
option log_level 'error'
|
||||
option main_node 'n_vless'
|
||||
|
||||
config homeproxy-pro 'routing'
|
||||
option default_outbound 'direct-out'
|
||||
|
||||
config homeproxy-pro 'infra'
|
||||
option mixed_port '5330'
|
||||
option dns_port '5333'
|
||||
option redirect_port '5331'
|
||||
option self_mark '100'
|
||||
|
||||
config node 'n_vless'
|
||||
option label 'n-vless'
|
||||
option type 'vless'
|
||||
option address 'example.invalid'
|
||||
option port '443'
|
||||
option uuid '00000000-0000-0000-0000-000000000000'
|
||||
option tls '1'
|
||||
option tls_sni 'example.invalid'
|
||||
|
||||
config routing_node 'rn_main'
|
||||
option enabled '1'
|
||||
option label 'rn-main'
|
||||
option node 'n_vless'
|
||||
|
||||
config ruleset 'rs_remote'
|
||||
option enabled '1'
|
||||
option label 'rs-remote'
|
||||
option type 'remote'
|
||||
option format 'binary'
|
||||
option url 'https://example.invalid/ruleset.srs'
|
||||
|
||||
config routing_rule 'rr_rs'
|
||||
option enabled '1'
|
||||
option label 'rr-rs'
|
||||
option action 'route'
|
||||
option outbound 'rn_main'
|
||||
option rule_set 'rs_remote'
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# A remote rule-set whose update_interval is a bare number. sing-box parses
|
||||
# the field as a duration and refuses a number without a unit
|
||||
# ("time: missing unit in duration"), which rejects the whole configuration -
|
||||
# the same failure the DNS timeouts are normalised for. The form only offers
|
||||
# a placeholder ("1d"), not a format check.
|
||||
|
||||
config homeproxy-pro 'config'
|
||||
option routing_mode 'custom'
|
||||
option proxy_mode 'redirect_tproxy'
|
||||
option ipv6_support '1'
|
||||
option log_level 'error'
|
||||
|
||||
config homeproxy-pro 'routing'
|
||||
option default_outbound 'direct-out'
|
||||
option default_outbound_dns 'default-dns'
|
||||
|
||||
config homeproxy-pro 'infra'
|
||||
option mixed_port '5330'
|
||||
option dns_port '5333'
|
||||
option redirect_port '5331'
|
||||
option self_mark '100'
|
||||
|
||||
config ruleset 'rs_remote'
|
||||
option enabled '1'
|
||||
option label 'rs-remote'
|
||||
option type 'remote'
|
||||
option format 'binary'
|
||||
option url 'https://example.invalid/ruleset.srs'
|
||||
option update_interval '3600'
|
||||
|
||||
config routing_rule 'rr_rs'
|
||||
option enabled '1'
|
||||
option label 'rr-rs'
|
||||
option action 'route'
|
||||
option outbound 'direct-out'
|
||||
option rule_set 'rs_remote'
|
||||
@@ -109,8 +109,6 @@ nft flush chain inet fw4 homeproxy_mangle_tun
|
||||
nft delete chain inet fw4 homeproxy_mangle_tun
|
||||
nft flush chain inet fw4 homeproxy_mangle_tun_mark
|
||||
nft delete chain inet fw4 homeproxy_mangle_tun_mark
|
||||
nft flush chain inet fw4 homeproxy_ipv6_guard
|
||||
nft delete chain inet fw4 homeproxy_ipv6_guard
|
||||
nft flush set inet fw4 homeproxy_local_addr_v4
|
||||
nft delete set inet fw4 homeproxy_local_addr_v4
|
||||
nft flush set inet fw4 homeproxy_local_addr_v6
|
||||
@@ -303,8 +301,6 @@ nft flush chain inet fw4 homeproxy_mangle_tun
|
||||
nft delete chain inet fw4 homeproxy_mangle_tun
|
||||
nft flush chain inet fw4 homeproxy_mangle_tun_mark
|
||||
nft delete chain inet fw4 homeproxy_mangle_tun_mark
|
||||
nft flush chain inet fw4 homeproxy_ipv6_guard
|
||||
nft delete chain inet fw4 homeproxy_ipv6_guard
|
||||
nft flush set inet fw4 homeproxy_local_addr_v4
|
||||
nft delete set inet fw4 homeproxy_local_addr_v4
|
||||
nft flush set inet fw4 homeproxy_local_addr_v6
|
||||
@@ -389,8 +385,6 @@ nft flush chain inet fw4 homeproxy_mangle_tun
|
||||
nft delete chain inet fw4 homeproxy_mangle_tun
|
||||
nft flush chain inet fw4 homeproxy_mangle_tun_mark
|
||||
nft delete chain inet fw4 homeproxy_mangle_tun_mark
|
||||
nft flush chain inet fw4 homeproxy_ipv6_guard
|
||||
nft delete chain inet fw4 homeproxy_ipv6_guard
|
||||
nft flush set inet fw4 homeproxy_local_addr_v4
|
||||
nft delete set inet fw4 homeproxy_local_addr_v4
|
||||
nft flush set inet fw4 homeproxy_local_addr_v6
|
||||
@@ -532,8 +526,6 @@ nft flush chain inet fw4 homeproxy_mangle_tun
|
||||
nft delete chain inet fw4 homeproxy_mangle_tun
|
||||
nft flush chain inet fw4 homeproxy_mangle_tun_mark
|
||||
nft delete chain inet fw4 homeproxy_mangle_tun_mark
|
||||
nft flush chain inet fw4 homeproxy_ipv6_guard
|
||||
nft delete chain inet fw4 homeproxy_ipv6_guard
|
||||
nft flush set inet fw4 homeproxy_local_addr_v4
|
||||
nft delete set inet fw4 homeproxy_local_addr_v4
|
||||
nft flush set inet fw4 homeproxy_local_addr_v6
|
||||
@@ -737,8 +729,6 @@ nft flush chain inet fw4 homeproxy_mangle_tun
|
||||
nft delete chain inet fw4 homeproxy_mangle_tun
|
||||
nft flush chain inet fw4 homeproxy_mangle_tun_mark
|
||||
nft delete chain inet fw4 homeproxy_mangle_tun_mark
|
||||
nft flush chain inet fw4 homeproxy_ipv6_guard
|
||||
nft delete chain inet fw4 homeproxy_ipv6_guard
|
||||
nft flush set inet fw4 homeproxy_local_addr_v4
|
||||
nft delete set inet fw4 homeproxy_local_addr_v4
|
||||
nft flush set inet fw4 homeproxy_local_addr_v6
|
||||
@@ -837,8 +827,6 @@ nft flush chain inet fw4 homeproxy_mangle_tun
|
||||
nft delete chain inet fw4 homeproxy_mangle_tun
|
||||
nft flush chain inet fw4 homeproxy_mangle_tun_mark
|
||||
nft delete chain inet fw4 homeproxy_mangle_tun_mark
|
||||
nft flush chain inet fw4 homeproxy_ipv6_guard
|
||||
nft delete chain inet fw4 homeproxy_ipv6_guard
|
||||
nft flush set inet fw4 homeproxy_local_addr_v4
|
||||
nft delete set inet fw4 homeproxy_local_addr_v4
|
||||
nft flush set inet fw4 homeproxy_local_addr_v6
|
||||
@@ -981,8 +969,6 @@ nft flush chain inet fw4 homeproxy_mangle_tun
|
||||
nft delete chain inet fw4 homeproxy_mangle_tun
|
||||
nft flush chain inet fw4 homeproxy_mangle_tun_mark
|
||||
nft delete chain inet fw4 homeproxy_mangle_tun_mark
|
||||
nft flush chain inet fw4 homeproxy_ipv6_guard
|
||||
nft delete chain inet fw4 homeproxy_ipv6_guard
|
||||
nft flush set inet fw4 homeproxy_local_addr_v4
|
||||
nft delete set inet fw4 homeproxy_local_addr_v4
|
||||
nft flush set inet fw4 homeproxy_local_addr_v6
|
||||
@@ -1134,8 +1120,6 @@ nft flush chain inet fw4 homeproxy_mangle_tun
|
||||
nft delete chain inet fw4 homeproxy_mangle_tun
|
||||
nft flush chain inet fw4 homeproxy_mangle_tun_mark
|
||||
nft delete chain inet fw4 homeproxy_mangle_tun_mark
|
||||
nft flush chain inet fw4 homeproxy_ipv6_guard
|
||||
nft delete chain inet fw4 homeproxy_ipv6_guard
|
||||
nft flush set inet fw4 homeproxy_local_addr_v4
|
||||
nft delete set inet fw4 homeproxy_local_addr_v4
|
||||
nft flush set inet fw4 homeproxy_local_addr_v6
|
||||
@@ -1220,8 +1204,6 @@ nft flush chain inet fw4 homeproxy_mangle_tun
|
||||
nft delete chain inet fw4 homeproxy_mangle_tun
|
||||
nft flush chain inet fw4 homeproxy_mangle_tun_mark
|
||||
nft delete chain inet fw4 homeproxy_mangle_tun_mark
|
||||
nft flush chain inet fw4 homeproxy_ipv6_guard
|
||||
nft delete chain inet fw4 homeproxy_ipv6_guard
|
||||
nft flush set inet fw4 homeproxy_local_addr_v4
|
||||
nft delete set inet fw4 homeproxy_local_addr_v4
|
||||
nft flush set inet fw4 homeproxy_local_addr_v6
|
||||
@@ -1429,8 +1411,6 @@ nft flush chain inet fw4 homeproxy_mangle_tun
|
||||
nft delete chain inet fw4 homeproxy_mangle_tun
|
||||
nft flush chain inet fw4 homeproxy_mangle_tun_mark
|
||||
nft delete chain inet fw4 homeproxy_mangle_tun_mark
|
||||
nft flush chain inet fw4 homeproxy_ipv6_guard
|
||||
nft delete chain inet fw4 homeproxy_ipv6_guard
|
||||
nft flush set inet fw4 homeproxy_local_addr_v4
|
||||
nft delete set inet fw4 homeproxy_local_addr_v4
|
||||
nft flush set inet fw4 homeproxy_local_addr_v6
|
||||
@@ -1496,8 +1476,6 @@ nft flush chain inet fw4 homeproxy_mangle_tun
|
||||
nft delete chain inet fw4 homeproxy_mangle_tun
|
||||
nft flush chain inet fw4 homeproxy_mangle_tun_mark
|
||||
nft delete chain inet fw4 homeproxy_mangle_tun_mark
|
||||
nft flush chain inet fw4 homeproxy_ipv6_guard
|
||||
nft delete chain inet fw4 homeproxy_ipv6_guard
|
||||
nft flush set inet fw4 homeproxy_local_addr_v4
|
||||
nft delete set inet fw4 homeproxy_local_addr_v4
|
||||
nft flush set inet fw4 homeproxy_local_addr_v6
|
||||
@@ -1645,8 +1623,6 @@ nft flush chain inet fw4 homeproxy_mangle_tun
|
||||
nft delete chain inet fw4 homeproxy_mangle_tun
|
||||
nft flush chain inet fw4 homeproxy_mangle_tun_mark
|
||||
nft delete chain inet fw4 homeproxy_mangle_tun_mark
|
||||
nft flush chain inet fw4 homeproxy_ipv6_guard
|
||||
nft delete chain inet fw4 homeproxy_ipv6_guard
|
||||
nft flush set inet fw4 homeproxy_local_addr_v4
|
||||
nft delete set inet fw4 homeproxy_local_addr_v4
|
||||
nft flush set inet fw4 homeproxy_local_addr_v6
|
||||
@@ -1829,8 +1805,6 @@ nft flush chain inet fw4 homeproxy_mangle_tun
|
||||
nft delete chain inet fw4 homeproxy_mangle_tun
|
||||
nft flush chain inet fw4 homeproxy_mangle_tun_mark
|
||||
nft delete chain inet fw4 homeproxy_mangle_tun_mark
|
||||
nft flush chain inet fw4 homeproxy_ipv6_guard
|
||||
nft delete chain inet fw4 homeproxy_ipv6_guard
|
||||
nft flush set inet fw4 homeproxy_local_addr_v4
|
||||
nft delete set inet fw4 homeproxy_local_addr_v4
|
||||
nft flush set inet fw4 homeproxy_local_addr_v6
|
||||
@@ -1952,8 +1926,6 @@ nft flush chain inet fw4 homeproxy_mangle_tun
|
||||
nft delete chain inet fw4 homeproxy_mangle_tun
|
||||
nft flush chain inet fw4 homeproxy_mangle_tun_mark
|
||||
nft delete chain inet fw4 homeproxy_mangle_tun_mark
|
||||
nft flush chain inet fw4 homeproxy_ipv6_guard
|
||||
nft delete chain inet fw4 homeproxy_ipv6_guard
|
||||
nft flush set inet fw4 homeproxy_local_addr_v4
|
||||
nft delete set inet fw4 homeproxy_local_addr_v4
|
||||
nft flush set inet fw4 homeproxy_local_addr_v6
|
||||
|
||||
@@ -133,35 +133,5 @@ check('cert path ignores an empty value',
|
||||
check('cert path ignores a missing section',
|
||||
hp.validateCertificatePath(null, '/etc/passwd') === true);
|
||||
|
||||
/* A query type name reaches sing-box's miekg/dns uppercase table
|
||||
* (option/types.go); a lowercase name is an "unknown DNS query type" and the
|
||||
* whole configuration is refused, which the user only ever saw as the reload
|
||||
* keeping the previous state. A numeric code is valid as-is. */
|
||||
check('query type accepts an uppercase name',
|
||||
hp.validateQueryType('sec1', 'AAAA') === true);
|
||||
check('query type accepts a numeric code',
|
||||
hp.validateQueryType('sec1', '65') === true);
|
||||
check('query type rejects a lowercase name',
|
||||
isError(hp.validateQueryType('sec1', 'aaaa')));
|
||||
check('query type rejects a name with a trailing space',
|
||||
isError(hp.validateQueryType('sec1', 'A ')));
|
||||
check('query type rejects a code above 65535',
|
||||
isError(hp.validateQueryType('sec1', '70000')));
|
||||
check('query type ignores an empty value',
|
||||
hp.validateQueryType('sec1', '') === true);
|
||||
check('query type ignores a missing section',
|
||||
hp.validateQueryType(null, 'aaaa') === true);
|
||||
|
||||
/* An uncompilable regex is not caught anywhere downstream either: sing-box
|
||||
* rejects the configuration as a whole. */
|
||||
check('regex accepts a valid pattern',
|
||||
hp.validateRegex('sec1', '^stun\\..+') === true);
|
||||
check('regex rejects an unbalanced group',
|
||||
isError(hp.validateRegex('sec1', '(unclosed')));
|
||||
check('regex ignores an empty value',
|
||||
hp.validateRegex('sec1', '') === true);
|
||||
check('regex ignores a missing section',
|
||||
hp.validateRegex(null, '(unclosed') === true);
|
||||
|
||||
console.log(`frontend validators: ${checks} checks, ${failures} failures`);
|
||||
process.exit(failures ? 1 : 0);
|
||||
@@ -378,29 +378,5 @@ reset_samples
|
||||
hp_wait_instance "sing-box-c" "$CFG" 1
|
||||
expect "wait_instance (compat): times out when down" "$?" "1"
|
||||
|
||||
# A reload that regenerates identical bytes must not restart the instances:
|
||||
# service_triggers() reloads on every `interface.*.up` for wan, so a flapping
|
||||
# link used to interrupt every connection for a configuration that had not
|
||||
# changed. hp_config_changed() is the decision, and it is pure shell.
|
||||
printf 'configuration one\n' > "$WORK/live.json"
|
||||
DIGEST="$(hp_file_digest "$WORK/live.json")"
|
||||
expect "digest is stable" "$(hp_file_digest "$WORK/live.json")" "$DIGEST"
|
||||
|
||||
hp_config_changed "$WORK/live.json" "$DIGEST" && RC=1 || RC=0
|
||||
expect "an identical file is not a change" "$RC" "0"
|
||||
|
||||
printf 'configuration two\n' > "$WORK/live.json"
|
||||
hp_config_changed "$WORK/live.json" "$DIGEST" && RC=1 || RC=0
|
||||
expect "a rewritten file is a change" "$RC" "1"
|
||||
|
||||
hp_config_changed "$WORK/missing.json" "$DIGEST" && RC=1 || RC=0
|
||||
expect "a missing live file is a change" "$RC" "1"
|
||||
|
||||
hp_config_changed "$WORK/live.json" "" && RC=1 || RC=0
|
||||
expect "nothing to compare against is a change" "$RC" "1"
|
||||
|
||||
: > "$WORK/empty.json"
|
||||
expect "an empty file has no digest" "$(hp_file_digest "$WORK/empty.json")" ""
|
||||
|
||||
printf '%d checks, %d failures\n' "$CHECKS" "$FAILURES"
|
||||
exit $FAILED
|
||||
@@ -242,22 +242,6 @@ expect "an unusable dns_port does not restart dnsmasq" "$(restart_count)" "$GOOD
|
||||
expect "an unusable dns_port is reported in the log" \
|
||||
"$(grep -c 'dns_port' "$LOG")" "1"
|
||||
|
||||
echo
|
||||
echo "== proxy_mainland_china: every query goes to sing-box =="
|
||||
|
||||
# This mode used to expand china_list.txt (111353 domains in the bundled list)
|
||||
# into one `server=` line each, which measured at about 25 MB of dnsmasq RSS on
|
||||
# a real device (1.0 MB empty, 27.2 MB with the list) for no behavioural
|
||||
# difference: the mode's dns.final is default-dns, the WAN resolver, so a
|
||||
# mainland domain resolves through the ISP either way.
|
||||
run "proxy_mainland_china"
|
||||
expect "proxy_mainland_china installs the global redirect snippet" \
|
||||
"$(exists "$DNS_DIR/redirect-dns.conf")" "yes"
|
||||
expect "proxy_mainland_china no longer expands china_list.txt" \
|
||||
"$(exists "$DNS_DIR/china_list.conf")" "no"
|
||||
expect "the redirect snippet points at sing-box" \
|
||||
"$(grep -c "server=127.0.0.1#" "$DNS_DIR/redirect-dns.conf")" "1"
|
||||
|
||||
echo
|
||||
printf '%s checks, %s failures\n' "$CHECKS" "$FAILURES"
|
||||
if [ "$FAILED" != 0 ]; then
|
||||
|
||||
@@ -40,6 +40,10 @@
|
||||
"ruleset",
|
||||
"Rule Set"
|
||||
],
|
||||
[
|
||||
"dns_cache",
|
||||
"DNS Settings"
|
||||
],
|
||||
[
|
||||
"control",
|
||||
"Access Control"
|
||||
@@ -271,7 +275,7 @@
|
||||
],
|
||||
"subsection": null,
|
||||
"props": {
|
||||
"validate": "fn(349:31d7a7fd11ff)"
|
||||
"validate": "fn(376:3a1eaf5f61c2)"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -494,7 +498,7 @@
|
||||
"subsection": null,
|
||||
"props": {
|
||||
"default": "nil",
|
||||
"load": "fn(347:1e6b4e82357b)",
|
||||
"load": "fn(347:bdff551797c2)",
|
||||
"rmempty": false
|
||||
}
|
||||
},
|
||||
@@ -508,7 +512,7 @@
|
||||
"subsection": null,
|
||||
"props": {
|
||||
"default": "default-dns",
|
||||
"load": "fn(326:b9d3cd52289f)",
|
||||
"load": "fn(326:345de0db50cf)",
|
||||
"rmempty": false
|
||||
}
|
||||
}
|
||||
@@ -593,7 +597,7 @@
|
||||
"values": [],
|
||||
"subsection": null,
|
||||
"props": {
|
||||
"load": "fn(356:8d5ad606a86f)",
|
||||
"load": "fn(356:d9c0755071ec)",
|
||||
"modalonly": true
|
||||
}
|
||||
},
|
||||
@@ -669,7 +673,7 @@
|
||||
"subsection": null,
|
||||
"props": {
|
||||
"editable": true,
|
||||
"load": "fn(284:01fc7f231969)",
|
||||
"load": "fn(284:530aca6575b5)",
|
||||
"validate": "fn(487:34bee8692641)"
|
||||
}
|
||||
},
|
||||
@@ -1024,7 +1028,7 @@
|
||||
"values": [],
|
||||
"subsection": null,
|
||||
"props": {
|
||||
"load": "fn(219:0275f2977497)",
|
||||
"load": "fn(219:b7b6c816012f)",
|
||||
"modalonly": true
|
||||
}
|
||||
},
|
||||
@@ -1098,7 +1102,7 @@
|
||||
"subsection": null,
|
||||
"props": {
|
||||
"editable": true,
|
||||
"load": "fn(263:a19a1b2bf7ba)",
|
||||
"load": "fn(263:6f52e51cea77)",
|
||||
"rmempty": false
|
||||
}
|
||||
},
|
||||
@@ -1111,10 +1115,6 @@
|
||||
[
|
||||
"action",
|
||||
"route"
|
||||
],
|
||||
[
|
||||
"action",
|
||||
"route-options"
|
||||
]
|
||||
],
|
||||
"values": [],
|
||||
@@ -1133,10 +1133,6 @@
|
||||
[
|
||||
"action",
|
||||
"route"
|
||||
],
|
||||
[
|
||||
"action",
|
||||
"route-options"
|
||||
]
|
||||
],
|
||||
"values": [],
|
||||
@@ -1155,10 +1151,6 @@
|
||||
[
|
||||
"action",
|
||||
"route"
|
||||
],
|
||||
[
|
||||
"action",
|
||||
"route-options"
|
||||
]
|
||||
],
|
||||
"values": [],
|
||||
@@ -1176,10 +1168,6 @@
|
||||
[
|
||||
"action",
|
||||
"route"
|
||||
],
|
||||
[
|
||||
"action",
|
||||
"route-options"
|
||||
]
|
||||
],
|
||||
"values": [],
|
||||
@@ -1197,10 +1185,6 @@
|
||||
[
|
||||
"action",
|
||||
"route"
|
||||
],
|
||||
[
|
||||
"action",
|
||||
"route-options"
|
||||
]
|
||||
],
|
||||
"values": [],
|
||||
@@ -1219,15 +1203,7 @@
|
||||
[
|
||||
"action",
|
||||
"route"
|
||||
],
|
||||
[
|
||||
"action",
|
||||
"route-options"
|
||||
],
|
||||
{
|
||||
"tls_fragment": "1",
|
||||
"!reverse": true
|
||||
}
|
||||
]
|
||||
],
|
||||
"values": [],
|
||||
"subsection": null,
|
||||
@@ -1244,15 +1220,7 @@
|
||||
[
|
||||
"action",
|
||||
"route"
|
||||
],
|
||||
[
|
||||
"action",
|
||||
"route-options"
|
||||
],
|
||||
{
|
||||
"tls_record_fragment": "1",
|
||||
"!reverse": true
|
||||
}
|
||||
]
|
||||
],
|
||||
"values": [],
|
||||
"subsection": null,
|
||||
@@ -1288,10 +1256,6 @@
|
||||
[
|
||||
"action",
|
||||
"route"
|
||||
],
|
||||
[
|
||||
"action",
|
||||
"route-options"
|
||||
]
|
||||
],
|
||||
"values": [],
|
||||
@@ -1311,10 +1275,6 @@
|
||||
"action",
|
||||
"route"
|
||||
],
|
||||
[
|
||||
"action",
|
||||
"route-options"
|
||||
],
|
||||
[
|
||||
"tls_spoof",
|
||||
{}
|
||||
@@ -1361,7 +1321,7 @@
|
||||
"values": [],
|
||||
"subsection": null,
|
||||
"props": {
|
||||
"load": "fn(356:8d5ad606a86f)",
|
||||
"load": "fn(356:d9c0755071ec)",
|
||||
"modalonly": true
|
||||
}
|
||||
},
|
||||
@@ -1580,8 +1540,7 @@
|
||||
"values": [],
|
||||
"subsection": null,
|
||||
"props": {
|
||||
"modalonly": true,
|
||||
"validate": "fn(184:0290625d9d6f)"
|
||||
"modalonly": true
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1744,8 +1703,7 @@
|
||||
"values": [],
|
||||
"subsection": null,
|
||||
"props": {
|
||||
"modalonly": true,
|
||||
"validate": "fn(184:0290625d9d6f)"
|
||||
"modalonly": true
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -1753,100 +1711,16 @@
|
||||
},
|
||||
"props": {}
|
||||
},
|
||||
{
|
||||
"kind": "Value",
|
||||
"name": "dns_server",
|
||||
"title": "Overseas DNS server",
|
||||
"description": "Resolves every domain that is not on the China DNS path, through the selected proxy node. 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 - so only overseas services are offered here. Support UDP, TCP, DoH, DoQ, DoT. TCP protocol will be used if not specified.",
|
||||
"depends": [
|
||||
{
|
||||
"routing_mode": "custom",
|
||||
"!reverse": true
|
||||
}
|
||||
],
|
||||
"values": [
|
||||
[
|
||||
"1.1.1.1",
|
||||
"CloudFlare Public DNS (1.1.1.1)"
|
||||
],
|
||||
[
|
||||
"208.67.222.222",
|
||||
"Cisco Public DNS (208.67.222.222)"
|
||||
],
|
||||
[
|
||||
"8.8.8.8",
|
||||
"Google Public DNS (8.8.8.8)"
|
||||
]
|
||||
],
|
||||
"subsection": null,
|
||||
"props": {
|
||||
"default": "8.8.8.8",
|
||||
"rmempty": false,
|
||||
"validate": "fn(730:48f7f360fbd2)"
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "Value",
|
||||
"name": "china_dns_server",
|
||||
"title": "China DNS server",
|
||||
"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. A DoH/DoT address here also sends the bootstrap lookup back to the WAN resolver: only a bare IP can resolve the proxy DNS endpoint without the ISP.",
|
||||
"depends": [
|
||||
[
|
||||
"routing_mode",
|
||||
"bypass_mainland_china"
|
||||
]
|
||||
],
|
||||
"values": [
|
||||
[
|
||||
"wan",
|
||||
"WAN DNS (read from interface)"
|
||||
],
|
||||
[
|
||||
"223.5.5.5",
|
||||
"Aliyun Public DNS (223.5.5.5)"
|
||||
],
|
||||
[
|
||||
"210.2.4.8",
|
||||
"CNNIC Public DNS (210.2.4.8)"
|
||||
],
|
||||
[
|
||||
"119.29.29.29",
|
||||
"Tencent Public DNS (119.29.29.29)"
|
||||
],
|
||||
[
|
||||
"117.50.10.10",
|
||||
"ThreatBook Public DNS (117.50.10.10)"
|
||||
]
|
||||
],
|
||||
"subsection": null,
|
||||
"props": {
|
||||
"default": "223.5.5.5",
|
||||
"rmempty": false,
|
||||
"validate": "fn(624:299cd4e865be)"
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "Flag",
|
||||
"name": "cn_ip_fallback",
|
||||
"title": "CN-IP DNS fallback (sing-box 1.14)",
|
||||
"description": "When the main DNS returns a mainland China IP, re-resolve via China DNS using evaluate/match_response.",
|
||||
"depends": [
|
||||
[
|
||||
"routing_mode",
|
||||
"bypass_mainland_china"
|
||||
]
|
||||
],
|
||||
"values": [],
|
||||
"subsection": null,
|
||||
"props": {
|
||||
"rmempty": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "SectionValue",
|
||||
"name": "_dns",
|
||||
"description": "dns",
|
||||
"depends": [],
|
||||
"depends": [
|
||||
[
|
||||
"routing_mode",
|
||||
"custom"
|
||||
]
|
||||
],
|
||||
"values": [],
|
||||
"subsection": {
|
||||
"id": "GridSection:_dns#5",
|
||||
@@ -1859,12 +1733,7 @@
|
||||
"name": "default_strategy",
|
||||
"title": "Default DNS strategy",
|
||||
"description": "The DNS strategy for resolving the domain name in the address.",
|
||||
"depends": [
|
||||
[
|
||||
"routing_mode",
|
||||
"custom"
|
||||
]
|
||||
],
|
||||
"depends": [],
|
||||
"values": [
|
||||
[
|
||||
"",
|
||||
@@ -1895,17 +1764,12 @@
|
||||
"name": "default_server",
|
||||
"title": "Default DNS server",
|
||||
"description": null,
|
||||
"depends": [
|
||||
[
|
||||
"routing_mode",
|
||||
"custom"
|
||||
]
|
||||
],
|
||||
"depends": [],
|
||||
"values": [],
|
||||
"subsection": null,
|
||||
"props": {
|
||||
"default": "default-dns",
|
||||
"load": "fn(326:b9d3cd52289f)",
|
||||
"load": "fn(326:345de0db50cf)",
|
||||
"rmempty": false
|
||||
}
|
||||
},
|
||||
@@ -1939,12 +1803,7 @@
|
||||
"name": "client_subnet",
|
||||
"title": "EDNS Client subnet",
|
||||
"description": "Append a <code>edns0-subnet</code> OPT extra record with the specified IP prefix to every query by default.<br/>If value is an IP address instead of prefix, <code>/32</code> or <code>/128</code> will be appended automatically.",
|
||||
"depends": [
|
||||
[
|
||||
"routing_mode",
|
||||
"custom"
|
||||
]
|
||||
],
|
||||
"depends": [],
|
||||
"values": [],
|
||||
"subsection": null,
|
||||
"props": {
|
||||
@@ -2150,7 +2009,7 @@
|
||||
"kind": "DynamicList",
|
||||
"name": "headers",
|
||||
"title": "Headers",
|
||||
"description": "Additional headers to be sent to the DNS server, one <code>Name: Value</code> per line.",
|
||||
"description": "Additional headers to be sent to the DNS server.",
|
||||
"depends": [
|
||||
[
|
||||
"type",
|
||||
@@ -2205,7 +2064,7 @@
|
||||
"values": [],
|
||||
"subsection": null,
|
||||
"props": {
|
||||
"load": "fn(384:2a0361acfbde)",
|
||||
"load": "fn(384:9fa59da460cf)",
|
||||
"modalonly": true,
|
||||
"validate": "fn(322:e76c15d647fc)"
|
||||
}
|
||||
@@ -2259,7 +2118,7 @@
|
||||
"props": {
|
||||
"default": "direct-out",
|
||||
"editable": true,
|
||||
"load": "fn(263:a19a1b2bf7ba)",
|
||||
"load": "fn(263:6f52e51cea77)",
|
||||
"rmempty": false
|
||||
}
|
||||
}
|
||||
@@ -2383,8 +2242,7 @@
|
||||
"values": [],
|
||||
"subsection": null,
|
||||
"props": {
|
||||
"modalonly": true,
|
||||
"validate": "fn(286:47dea50f33ea)"
|
||||
"modalonly": true
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -2474,7 +2332,7 @@
|
||||
"values": [],
|
||||
"subsection": null,
|
||||
"props": {
|
||||
"load": "fn(219:0275f2977497)",
|
||||
"load": "fn(219:b7b6c816012f)",
|
||||
"modalonly": true
|
||||
}
|
||||
},
|
||||
@@ -2552,7 +2410,7 @@
|
||||
"subsection": null,
|
||||
"props": {
|
||||
"editable": true,
|
||||
"load": "fn(326:b9d3cd52289f)",
|
||||
"load": "fn(326:345de0db50cf)",
|
||||
"rmempty": false
|
||||
}
|
||||
},
|
||||
@@ -3022,8 +2880,7 @@
|
||||
"values": [],
|
||||
"subsection": null,
|
||||
"props": {
|
||||
"modalonly": true,
|
||||
"validate": "fn(184:0290625d9d6f)"
|
||||
"modalonly": true
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -3211,8 +3068,7 @@
|
||||
"values": [],
|
||||
"subsection": null,
|
||||
"props": {
|
||||
"modalonly": true,
|
||||
"validate": "fn(184:0290625d9d6f)"
|
||||
"modalonly": true
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -3290,13 +3146,9 @@
|
||||
"kind": "ListValue",
|
||||
"name": "format",
|
||||
"title": "Format",
|
||||
"description": "Leave empty to infer it from a <code>.json</code> or <code>.srs</code> path/url; sing-box requires it for any other extension.",
|
||||
"description": null,
|
||||
"depends": [],
|
||||
"values": [
|
||||
[
|
||||
"",
|
||||
"Automatic (by extension)"
|
||||
],
|
||||
[
|
||||
"binary",
|
||||
"Binary file"
|
||||
@@ -3308,7 +3160,8 @@
|
||||
],
|
||||
"subsection": null,
|
||||
"props": {
|
||||
"rmempty": true
|
||||
"default": "binary",
|
||||
"rmempty": false
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -3364,7 +3217,7 @@
|
||||
"values": [],
|
||||
"subsection": null,
|
||||
"props": {
|
||||
"load": "fn(293:81de93c500ec)"
|
||||
"load": "fn(293:8a1c8158e8ba)"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -3425,6 +3278,177 @@
|
||||
},
|
||||
"props": {}
|
||||
},
|
||||
{
|
||||
"kind": "Value",
|
||||
"name": "dns_server",
|
||||
"title": "Overseas DNS server",
|
||||
"description": "Resolves every domain that is not on the China DNS path, through the selected proxy node. 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 - so only overseas services are offered here. Support UDP, TCP, DoH, DoQ, DoT. TCP protocol will be used if not specified.",
|
||||
"depends": [
|
||||
{
|
||||
"routing_mode": "custom",
|
||||
"!reverse": true
|
||||
}
|
||||
],
|
||||
"values": [
|
||||
[
|
||||
"1.1.1.1",
|
||||
"CloudFlare Public DNS (1.1.1.1)"
|
||||
],
|
||||
[
|
||||
"208.67.222.222",
|
||||
"Cisco Public DNS (208.67.222.222)"
|
||||
],
|
||||
[
|
||||
"8.8.8.8",
|
||||
"Google Public DNS (8.8.8.8)"
|
||||
]
|
||||
],
|
||||
"subsection": null,
|
||||
"props": {
|
||||
"default": "8.8.8.8",
|
||||
"rmempty": false,
|
||||
"validate": "fn(730:48f7f360fbd2)"
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "Value",
|
||||
"name": "china_dns_server",
|
||||
"title": "China DNS server",
|
||||
"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",
|
||||
"bypass_mainland_china"
|
||||
]
|
||||
],
|
||||
"values": [
|
||||
[
|
||||
"wan",
|
||||
"WAN DNS (read from interface)"
|
||||
],
|
||||
[
|
||||
"223.5.5.5",
|
||||
"Aliyun Public DNS (223.5.5.5)"
|
||||
],
|
||||
[
|
||||
"210.2.4.8",
|
||||
"CNNIC Public DNS (210.2.4.8)"
|
||||
],
|
||||
[
|
||||
"119.29.29.29",
|
||||
"Tencent Public DNS (119.29.29.29)"
|
||||
],
|
||||
[
|
||||
"117.50.10.10",
|
||||
"ThreatBook Public DNS (117.50.10.10)"
|
||||
]
|
||||
],
|
||||
"subsection": null,
|
||||
"props": {
|
||||
"default": "223.5.5.5",
|
||||
"rmempty": false,
|
||||
"validate": "fn(624:299cd4e865be)"
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "Flag",
|
||||
"name": "cn_ip_fallback",
|
||||
"title": "CN-IP DNS fallback (sing-box 1.14)",
|
||||
"description": "When the main DNS returns a mainland China IP, re-resolve via China DNS using evaluate/match_response.",
|
||||
"depends": [
|
||||
[
|
||||
"routing_mode",
|
||||
"bypass_mainland_china"
|
||||
]
|
||||
],
|
||||
"values": [],
|
||||
"subsection": null,
|
||||
"props": {
|
||||
"rmempty": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "SectionValue",
|
||||
"name": "_dns_cache",
|
||||
"description": "dns",
|
||||
"depends": [
|
||||
{
|
||||
"routing_mode": "custom",
|
||||
"!reverse": true
|
||||
}
|
||||
],
|
||||
"values": [],
|
||||
"subsection": {
|
||||
"id": "GridSection:_dns_cache#9",
|
||||
"kind": "GridSection",
|
||||
"name": "_dns_cache",
|
||||
"tabs": [],
|
||||
"options": [
|
||||
{
|
||||
"kind": "Flag",
|
||||
"name": "optimistic_cache",
|
||||
"title": "Optimistic DNS cache",
|
||||
"description": "Return expired cache immediately and refresh in background (sing-box 1.14).",
|
||||
"depends": [
|
||||
[
|
||||
"disable_cache",
|
||||
"0"
|
||||
],
|
||||
[
|
||||
"disable_cache_expire",
|
||||
"0"
|
||||
]
|
||||
],
|
||||
"values": [],
|
||||
"subsection": null,
|
||||
"props": {
|
||||
"rmempty": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "Value",
|
||||
"name": "optimistic_timeout",
|
||||
"title": "Optimistic cache timeout",
|
||||
"description": "Max time an expired entry may be served. Examples: 3d, 1h.",
|
||||
"depends": [],
|
||||
"values": [],
|
||||
"subsection": null,
|
||||
"props": {}
|
||||
},
|
||||
{
|
||||
"kind": "Value",
|
||||
"name": "dns_timeout",
|
||||
"title": "DNS query timeout",
|
||||
"description": "Default timeout per DNS query in seconds (sing-box default: 10).",
|
||||
"depends": [],
|
||||
"values": [],
|
||||
"subsection": null,
|
||||
"props": {
|
||||
"datatype": "uinteger"
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "Flag",
|
||||
"name": "cache_file_store_dns",
|
||||
"title": "Store DNS cache",
|
||||
"description": "Persist DNS cache across restarts (sing-box 1.14, replaces Store RDRC).",
|
||||
"depends": [
|
||||
[
|
||||
"disable_cache",
|
||||
"0"
|
||||
]
|
||||
],
|
||||
"values": [],
|
||||
"subsection": null,
|
||||
"props": {
|
||||
"rmempty": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"subsection": null
|
||||
},
|
||||
"props": {}
|
||||
},
|
||||
{
|
||||
"kind": "SectionValue",
|
||||
"name": "_control",
|
||||
@@ -3432,7 +3456,7 @@
|
||||
"depends": [],
|
||||
"values": [],
|
||||
"subsection": {
|
||||
"id": "GridSection:_control#9",
|
||||
"id": "GridSection:_control#10",
|
||||
"kind": "GridSection",
|
||||
"name": "_control",
|
||||
"tabs": [
|
||||
@@ -3488,12 +3512,12 @@
|
||||
"kind": "ListValue",
|
||||
"name": "lan_proxy_mode",
|
||||
"title": "Proxy filter mode",
|
||||
"description": "\"No filter\" proxies every LAN client, which is the default; the other two values select clients by the lists below.",
|
||||
"description": null,
|
||||
"depends": [],
|
||||
"values": [
|
||||
[
|
||||
"disabled",
|
||||
"No filter (proxy every client)"
|
||||
"Disable"
|
||||
],
|
||||
[
|
||||
"listed_only",
|
||||
|
||||
@@ -96,28 +96,6 @@ if ! grep -q '\.dnsmasq-nftset' "$ROOT/root/etc/init.d/homeproxy-pro"; then
|
||||
FAILED=1
|
||||
fi
|
||||
|
||||
# --- ipv6_support off refuses IPv6 forwarding ------------------------------
|
||||
# The DNS side answers AAAA with REFUSED, but only for clients that ask this
|
||||
# router: a cached address or a client with its own resolver still reaches the
|
||||
# WAN. The guard is therefore a forward-hook base chain of its own - fw4's
|
||||
# forward chain jumps into the zone chains, whose accept rules end the traversal
|
||||
# before anything appended at the end of it is reached - ordered ahead of fw4's
|
||||
# forward (priority mangle vs filter).
|
||||
#
|
||||
# Asserted at the source level, like the gfwlist gate below: the render uses
|
||||
# the device's own UCI, so the ipv6_support branch cannot be exercised in both
|
||||
# of its states from here.
|
||||
GUARD_COND="$(grep -c "if (ipv6_support !== '1'):" "$TEMPLATE_SRC")"
|
||||
GUARD_HOOK="$(grep -c 'type filter hook forward priority mangle' "$TEMPLATE_SRC")"
|
||||
GUARD_RULE="$(grep -c 'meta nfproto ipv6 counter reject' "$TEMPLATE_SRC")"
|
||||
|
||||
if [ "$GUARD_COND" -ne 1 ] || [ "$GUARD_HOOK" -ne 1 ] || [ "$GUARD_RULE" -ne 1 ]; then
|
||||
echo "FAIL: the IPv6 forwarding guard is incomplete or ungated"
|
||||
echo " condition=$GUARD_COND hook=$GUARD_HOOK rule=$GUARD_RULE (each must be 1)"
|
||||
echo " a guard on priority filter would run after fw4's forward chain and never match"
|
||||
FAILED=1
|
||||
fi
|
||||
|
||||
# --- layer 2: the render -------------------------------------------------
|
||||
# The render output and its stderr live beside the staged template, never at a
|
||||
# fixed /tmp path. A caller that passes a work dir (tests/ucode/run.sh hands
|
||||
@@ -203,14 +181,6 @@ if grep -nE "^#[^!].*homeproxy_[a-z0-9_]+ \{" "$OUT" > "/dev/null"; then
|
||||
FAILED=1
|
||||
fi
|
||||
|
||||
# The template defaults ipv6_support to '0', so the guard chain has to appear in
|
||||
# the render both on a device without the switch set and in CI, where there is
|
||||
# no /etc/config/homeproxy-pro at all.
|
||||
if ! grep -q "^chain homeproxy_ipv6_guard {" "$OUT"; then
|
||||
echo "FAIL: the IPv6 forwarding guard chain is missing from the rendered template"
|
||||
FAILED=1
|
||||
fi
|
||||
|
||||
if [ "$FAILED" -eq 0 ]; then
|
||||
echo "PASS: firewall_post.ut renders homeproxy-pro objects as standalone nft statements"
|
||||
echo "PASS: the gfwlist gate follows the dnsmasq nftset capability"
|
||||
|
||||
@@ -975,7 +975,7 @@ else
|
||||
esac
|
||||
fi
|
||||
|
||||
# 3) P2 #4 / §2.2.1: the NAPTR (qtype 35) bypass suffixes must be
|
||||
# 3) P2 #4 / §2.2.1: the three NAPTR (qtype 35) bypass suffixes must be
|
||||
# emitted as a single domain_suffix entry backed by the
|
||||
# NAPTR_BYPASS_SUFFIXES constant. ucode's pretty-printer emits the
|
||||
# query_type array and the domain_suffix array as multi-line:
|
||||
@@ -983,6 +983,7 @@ fi
|
||||
# 35
|
||||
# ],
|
||||
# "domain_suffix": [
|
||||
# "r.10086.cn",
|
||||
# "10086.cn",
|
||||
# "pub.3gppnetwork.org"
|
||||
# ],
|
||||
@@ -999,7 +1000,7 @@ else
|
||||
# naptr_body should contain the three suffixes in order. If
|
||||
# any is missing, the layout shifted and the test fails loud.
|
||||
naptr_ok=1
|
||||
for sfx in '10086.cn' 'pub.3gppnetwork.org'; do
|
||||
for sfx in 'r.10086.cn' '10086.cn' 'pub.3gppnetwork.org'; do
|
||||
if ! printf '%s\n' "$naptr_body" | grep -q "\"$sfx\""; then
|
||||
echo "FAIL: naptr-bypass-domains: NAPTR rule is missing suffix \"$sfx\""
|
||||
echo " grep window:"
|
||||
@@ -1008,7 +1009,7 @@ else
|
||||
fi
|
||||
done
|
||||
if [ "$naptr_ok" = 1 ]; then
|
||||
echo "PASS: naptr-bypass-domains: qtype-35 rule domain_suffix carries every NAPTR_BYPASS_SUFFIXES entry"
|
||||
echo "PASS: naptr-bypass-domains: qtype-35 rule domain_suffix carries the 3 NAPTR_BYPASS_SUFFIXES entries"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -1083,220 +1084,4 @@ run_case_type_error local-ruleset-bad-path "outside the homeproxy-pro whitelist"
|
||||
# covered by manual testing on r28. Re-add this case via a
|
||||
# custom_extra_tags.uci fixture when convenient.
|
||||
|
||||
|
||||
# --- review 2026-09-30: fields sing-box refuses at load time ----------------
|
||||
#
|
||||
# Each of these reached sing-box as a configuration it rejects outright - an
|
||||
# unknown field, an empty action object, a mutually exclusive pair - and the
|
||||
# only symptom the user saw was "my setting did not take". The generator now
|
||||
# refuses them by name before writing anything, which is what
|
||||
# run_case_type_error observes.
|
||||
cat > "$WORK/dns-field-guard.uc" <<'EOF'
|
||||
'use strict';
|
||||
|
||||
import { readfile } from 'fs';
|
||||
|
||||
const config = json(readfile(ARGV[0]));
|
||||
const mode = ARGV[1];
|
||||
|
||||
function has_aaaa() {
|
||||
for (let r in (config.dns?.rules || []))
|
||||
if (r.action === 'reject' && type(r.query_type) === 'array' && r.query_type[0] === 28)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case 'headers':
|
||||
for (let s in (config.dns?.servers || [])) {
|
||||
if (s.tag !== 'cfg-ds_https-dns')
|
||||
continue;
|
||||
if (type(s.headers) !== 'object') {
|
||||
print('headers is not an object');
|
||||
exit(1);
|
||||
}
|
||||
if (length(s.headers['X-Test'] || []) !== 2) {
|
||||
print('X-Test is not a two-value list');
|
||||
exit(1);
|
||||
}
|
||||
if (s.headers['X-Test'][0] !== 'one' || s.headers['X-Test'][1] !== 'two') {
|
||||
print('X-Test values are wrong');
|
||||
exit(1);
|
||||
}
|
||||
if ((s.headers['Authorization'] || [])[0] !== 'Bearer t') {
|
||||
print('Authorization is wrong');
|
||||
exit(1);
|
||||
}
|
||||
print('OK');
|
||||
exit(0);
|
||||
}
|
||||
print('cfg-ds_https-dns is missing');
|
||||
exit(1);
|
||||
case 'aaaa-present':
|
||||
print(has_aaaa() ? 'OK' : 'the AAAA reject rule is missing');
|
||||
exit(has_aaaa() ? 0 : 1);
|
||||
case 'aaaa-absent':
|
||||
print(has_aaaa() ? 'an AAAA reject rule was emitted with ipv6_support on' : 'OK');
|
||||
exit(has_aaaa() ? 1 : 0);
|
||||
case 'evaluate-timeout':
|
||||
for (let r in (config.dns?.rules || []))
|
||||
if (r.action === 'evaluate') {
|
||||
print(r.timeout === '5s' ? 'OK' : 'the evaluate rule carries no 5s timeout: ' + r.timeout);
|
||||
exit(r.timeout === '5s' ? 0 : 1);
|
||||
}
|
||||
print('no evaluate DNS rule was generated');
|
||||
exit(1);
|
||||
case 'http-client-detour':
|
||||
for (let c in (config.http_clients || [])) {
|
||||
if (c.detour !== 'main-out') {
|
||||
print('an http_client detours through ' + c.detour);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
print('OK');
|
||||
exit(0);
|
||||
case 'update-interval':
|
||||
for (let rs in (config.route?.rule_set || []))
|
||||
if (rs.tag === 'cfg-rs_remote-rule') {
|
||||
print(rs.update_interval === '3600s' ? 'OK' : 'update_interval is ' + rs.update_interval);
|
||||
exit(rs.update_interval === '3600s' ? 0 : 1);
|
||||
}
|
||||
print('cfg-rs_remote-rule is missing');
|
||||
exit(1);
|
||||
}
|
||||
print('unknown mode');
|
||||
exit(1);
|
||||
EOF
|
||||
|
||||
assert_guard() {
|
||||
local name="$1" json="$2" mode="$3" want="$4"
|
||||
local got
|
||||
|
||||
if [ ! -s "$json" ]; then
|
||||
echo "FAIL: $name: no configuration was generated"
|
||||
FAILED=1
|
||||
return
|
||||
fi
|
||||
|
||||
got="$(ucode "$WORK/dns-field-guard.uc" "$json" "$mode" 2>&1)"
|
||||
if [ "$got" = "$want" ]; then
|
||||
echo "PASS: $name"
|
||||
else
|
||||
echo "FAIL: $name: $got"
|
||||
FAILED=1
|
||||
fi
|
||||
}
|
||||
|
||||
# 1) DNS server headers: the UCI string list has to become
|
||||
# map[string][]string, not a JSON array.
|
||||
run_case dns-headers-object "$ROOT/tests/fixtures/generators/dns_headers.uci" \
|
||||
generate_client.uc sing-box-c.json
|
||||
assert_guard "dns-headers-object: the UCI list became a header object" \
|
||||
"$WORK/dns-headers-object/run/sing-box-c.json" headers OK
|
||||
|
||||
# 2) The baseline both variations below are measured against: with the flags
|
||||
# off, the fixture has to generate and pass sing-box check. Without it a
|
||||
# refusal in 3)-5) could just as well come from the fixture itself.
|
||||
run_case dns-action-guards-baseline "$ROOT/tests/fixtures/generators/dns_action_guards.uci" \
|
||||
generate_client.uc sing-box-c.json
|
||||
|
||||
# 3) race + speculative on one rule.
|
||||
run_case_type_error dns-race-speculative "enables both race and speculative" \
|
||||
"$ROOT/tests/fixtures/generators/dns_action_guards.uci" generate_client.uc sing-box-c.json \
|
||||
"s#option race '0'#option race '1'#; s#option speculative '0'#option speculative '1'#"
|
||||
|
||||
# 4) race on an action that is not final (evaluate).
|
||||
run_case_type_error dns-race-final-action "race is only allowed on a final action" \
|
||||
"$ROOT/tests/fixtures/generators/dns_action_guards.uci" generate_client.uc sing-box-c.json \
|
||||
"s#option action 'route'#option action 'evaluate'#; s#option race '0'#option race '1'#"
|
||||
|
||||
# 5) client_subnet + remove_client_subnet.
|
||||
run_case_type_error dns-client-subnet-conflict "sets both client_subnet and remove_client_subnet" \
|
||||
"$ROOT/tests/fixtures/generators/dns_action_guards.uci" generate_client.uc sing-box-c.json \
|
||||
"s#option remove_client_subnet '0'#option remove_client_subnet '1'#"
|
||||
|
||||
# 6) route-options with no payload at all.
|
||||
run_case_type_error routing-route-options-empty "selects the route-options action but sets none of its fields" \
|
||||
"$ROOT/tests/fixtures/generators/routing_rule_route_options_empty.uci" generate_client.uc sing-box-c.json
|
||||
|
||||
# 7) tls_fragment + tls_record_fragment.
|
||||
run_case_type_error routing-tls-fragment-conflict "enables both tls_fragment and tls_record_fragment" \
|
||||
"$ROOT/tests/fixtures/generators/routing_rule_tls_fragment.uci" generate_client.uc sing-box-c.json
|
||||
|
||||
# 8) AAAA is refused when the router does not proxy IPv6, and left alone when
|
||||
# it does. firewall_post.ut emits no IPv6 rule at all with the switch off,
|
||||
# so a AAAA answer was the client's ticket straight out over the WAN.
|
||||
run_case ipv6-guard-off "$ROOT/tests/fixtures/generators/custom.uci" \
|
||||
generate_client.uc sing-box-c.json
|
||||
assert_guard "ipv6-guard-off: AAAA is refused while ipv6_support is off" \
|
||||
"$WORK/ipv6-guard-off/run/sing-box-c.json" aaaa-present OK
|
||||
|
||||
run_case ipv6-guard-on "$ROOT/tests/fixtures/generators/client.uci" \
|
||||
generate_client.uc sing-box-c.json \
|
||||
"s#option ipv6_support '0'#option ipv6_support '1'#"
|
||||
assert_guard "ipv6-guard-on: no AAAA rule while ipv6_support is on" \
|
||||
"$WORK/ipv6-guard-on/run/sing-box-c.json" aaaa-absent OK
|
||||
|
||||
# 9) evaluate carries the same timeout override as route: emitting it for
|
||||
# route only made the field silently ineffective on an evaluate rule.
|
||||
run_case dns-evaluate-timeout "$ROOT/tests/fixtures/generators/dns_action_guards.uci" \
|
||||
generate_client.uc sing-box-c.json
|
||||
assert_guard "dns-evaluate-timeout: the evaluate rule kept its query timeout" \
|
||||
"$WORK/dns-evaluate-timeout/run/sing-box-c.json" evaluate-timeout OK
|
||||
|
||||
# 10) A bare-number update_interval is a duration to sing-box; without a unit
|
||||
# it reports "time: missing unit in duration" and refuses everything.
|
||||
run_case ruleset-update-interval "$ROOT/tests/fixtures/generators/ruleset_remote_update_interval.uci" \
|
||||
generate_client.uc sing-box-c.json
|
||||
assert_guard "ruleset-update-interval: '3600' became '3600s'" \
|
||||
"$WORK/ruleset-update-interval/run/sing-box-c.json" update-interval OK
|
||||
|
||||
# 11) A rule pointing at a routing_node that was switched off used to emit a
|
||||
# tag nothing generates ("outbound not found: cfg-rn_off-out").
|
||||
run_case_type_error routing-disabled-node "is disabled" \
|
||||
"$ROOT/tests/fixtures/generators/routing_rule_disabled_node.uci" generate_client.uc sing-box-c.json
|
||||
|
||||
# 12) initial_path outside the whitelist: the same gate as a local rule-set
|
||||
# path, and the same fail-loud treatment (it used to be dropped silently).
|
||||
run_case_type_error ruleset-bad-initial-path "initial_path" \
|
||||
"$ROOT/tests/fixtures/generators/ruleset_remote_bad_initial_path.uci" generate_client.uc sing-box-c.json
|
||||
|
||||
# 13) A preset mode downloads its remote rule-sets through the main outbound,
|
||||
# not direct-out: a router that can only reach the rule-set host through
|
||||
# the proxy otherwise just logs a failed download.
|
||||
run_case ruleset-remote-preset "$ROOT/tests/fixtures/generators/ruleset_remote_preset.uci" \
|
||||
generate_client.uc sing-box-c.json
|
||||
assert_guard "ruleset-remote-preset: the remote rule-set downloads through main-out" \
|
||||
"$WORK/ruleset-remote-preset/run/sing-box-c.json" http-client-detour OK
|
||||
|
||||
# 14) A missing china_ip4.json must not leave a `type: local` rule-set - and
|
||||
# the rule matching it - pointing at a path sing-box cannot open: that
|
||||
# fails the whole configuration, not just the rule. The mode keeps
|
||||
# working through geoip-cn. The staged bypass-mode case is reused with
|
||||
# the file removed and put back.
|
||||
CHINA_CASE="$WORK/client"
|
||||
if [ -f "$CHINA_CASE/resources/china_ip4.json" ]; then
|
||||
mv "$CHINA_CASE/resources/china_ip4.json" "$CHINA_CASE/resources/china_ip4.json.bak"
|
||||
rm -f "$CHINA_CASE/run/sing-box-c.json"
|
||||
if ( cd "$CHINA_CASE/scripts" && ucode -L "$CHINA_CASE/scripts" generate_client.uc 2>"$CHINA_CASE/missing.err" ); then
|
||||
if grep -q '"china-ip"' "$CHINA_CASE/run/sing-box-c.json"; then
|
||||
echo "FAIL: china-ip-missing: the configuration still references the missing rule-set"
|
||||
FAILED=1
|
||||
elif grep -q '"tag": "geoip-cn"' "$CHINA_CASE/run/sing-box-c.json"; then
|
||||
echo "PASS: china-ip-missing: the mainland split falls back to geoip-cn"
|
||||
else
|
||||
echo "FAIL: china-ip-missing: neither china-ip nor geoip-cn is declared"
|
||||
FAILED=1
|
||||
fi
|
||||
else
|
||||
echo "FAIL: china-ip-missing: generation failed"
|
||||
head -3 "$CHINA_CASE/missing.err"
|
||||
FAILED=1
|
||||
fi
|
||||
mv "$CHINA_CASE/resources/china_ip4.json.bak" "$CHINA_CASE/resources/china_ip4.json"
|
||||
else
|
||||
echo "FAIL: china-ip-missing: the client case did not stage china_ip4.json"
|
||||
FAILED=1
|
||||
fi
|
||||
|
||||
exit ${FAILED:-0}
|
||||
exit ${FAILED:-0}
|
||||
@@ -224,9 +224,9 @@ check('certificate_write tolerates an empty request', threw == null, threw);
|
||||
* executed. Four of these were referenced by no test at all. */
|
||||
const all_methods = [
|
||||
'acllist_read', 'acllist_write', 'certificate_write', 'connection_check',
|
||||
'gfw_set_counts', 'log_clean', 'node_parse', 'resources_get_version',
|
||||
'resources_update', 'singbox_generator', 'singbox_get_features',
|
||||
'update_subscriptions', 'update_subscriptions_status'
|
||||
'log_clean', 'node_parse', 'resources_get_version', 'resources_update',
|
||||
'singbox_generator', 'singbox_get_features', 'update_subscriptions',
|
||||
'update_subscriptions_status'
|
||||
];
|
||||
|
||||
for (let m in all_methods) {
|
||||
|
||||
Reference in new issue
Block a user