fix(providers): resolve zapret2 startup crash, nft desync EPERM, and mieru ruleset crash loop

- fix(zapret2): add safe_str fallback and avoid default --filter-tcp=443 injection on UDP rules in prepare_strategy_args()
- fix(providers): make individual rule failures non-fatal in start_runtime() so one bad rule does not rollback all running rules
- fix(binaries): dynamic candidate resolution for nfqws and nfqws2 across standard OpenWrt and custom paths (/opt/zapret2/nfq2, /opt/zapret2/nfq, /opt/zapret2, /usr/bin)
- fix(nft): add raw_output notrack chain and mangle_output/forward counter return rules for desync marks (0x40000000, 0x20000000) and musl/nobody skuid (2147483647, 65534) to prevent EPERM rawsend failure on reload
- fix(reload): ensure nft_rebuild is triggered on zapret and zapret2 runtime updates
- fix(singbox): reject mieru outbounds from rule_set download_detour and http_clients dial_detour to prevent fatal startup crash loop
- fix(singbox): detect sing-box 1.14+ and omit deprecated download_detour on remote rule-sets when http_clients is active
- fix(action): pre-seed /etc/config/zapret2 with enabled=0 via core.uci before package install to prevent conflicting upstream daemon startup
- test(ruleset): add verification tests for mieru download_detour and dial_detour rejection
This commit is contained in:
Dushnilin
2026-09-10 20:18:00 +03:00
parent a254cfc8b6
commit bc4f12913f
11 changed files with 233 additions and 28 deletions
+34 -3
View File
@@ -404,6 +404,32 @@ function tproxy_inbound_matcher() {
return [ runtime_constants.TPROXY_INBOUND_TAG, runtime_constants.TPROXY_INBOUND6_TAG ];
}
function is_sb_1_14_plus_detected(sb_version_val) {
if (sb_version_val == null || sb_version_val == "") {
let sb_version_file = getenv("SB_VERSION_STATE_FILE") || "/etc/tachyon/sing-box-version";
sb_version_val = trim(fs.readfile(sb_version_file) || "");
if (sb_version_val == "") {
let sb_ui_cache = getenv("TACHYON_UI_SING_BOX_VERSION_CACHE_FILE") || "/var/run/tachyon/ui-state/sing-box-version";
sb_version_val = trim(fs.readfile(sb_ui_cache) || "");
}
if (sb_version_val == "") {
try {
let pipe = fs.popen("sing-box version 2>/dev/null", "r");
if (pipe) {
let out = pipe.read("all");
pipe.close();
let m = match(out, /sing-box version ([^\s]+)/);
if (m)
sb_version_val = m[1];
}
} catch (e) {}
}
}
if (sb_version_val != "")
return match(sb_version_val, /^v?1\.(1[4-9]|[2-9][0-9])\./) != null;
return false;
}
function base_config(settings, service_address, runtime_context) {
runtime_context = object_or_empty(runtime_context);
let log_level = option(settings, "log_level", "warn");
@@ -547,9 +573,7 @@ function base_config(settings, service_address, runtime_context) {
? (match(sb_version_val, /^v?1\.1[0-3]\./) != null)
: !is_extended_variant;
let is_sb_1_14_plus = sb_version_val != ""
? (match(sb_version_val, /^v?1\.(1[4-9]|[2-9][0-9])\./) != null)
: false;
let is_sb_1_14_plus = is_sb_1_14_plus_detected(sb_version_val);
let route_section = runtime_route.config(settings, runtime_context);
if (is_sb_1_14_plus)
@@ -1272,6 +1296,12 @@ function generate_config(output_path, service_address, mwan3_active, supports_xh
add_content_blocking(config);
if (type(config.http_clients) == "array" && length(config.http_clients) > 0) {
let detour = download_detour_tag(settings, "lists");
if (detour != "" && generator_routes.is_valid_detour(config, detour))
config.http_clients[0].dial_detour = detour;
}
assert_unique_outbound_tags(config);
strip_internal_fields(config);
if (!atomic_write_json_file(output_path, config)) {
@@ -1401,6 +1431,7 @@ ctx.runtime_generate_unsupported = runtime_generate_unsupported;
ctx.uci_bin_to_hex = uci_bin_to_hex;
ctx.download_detour_tag = download_detour_tag;
ctx.atomic_write_json_file = atomic_write_json_file;
ctx.is_sb_1_14_plus = is_sb_1_14_plus_detected;
generator_outbounds.init(ctx);
generator_routes.init(ctx);
@@ -637,6 +637,25 @@ function add_priority_group_outbound(config, section, group_id, urltest_candidat
};
}
function is_mieru_detour(config, tag, depth) {
depth = int(depth || 0);
if (depth > 5 || tag == null || tag == "")
return false;
for (let out in array_or_empty(config.outbounds)) {
if (type(out) == "object" && out.tag == tag) {
if (out.type == "mieru")
return true;
if ((out.type == "selector" || out.type == "urltest") && type(out.outbounds) == "array") {
for (let sub in out.outbounds) {
if (is_mieru_detour(config, sub, depth + 1))
return true;
}
}
}
}
return false;
}
function is_valid_detour(config, tag) {
if (tag == null || tag == "")
return false;
@@ -648,6 +667,8 @@ function is_valid_detour(config, tag) {
return false;
if (out.routing_mark != null)
return false;
if (out.type == "mieru" || is_mieru_detour(config, tag, 0))
return false;
return true;
}
}
@@ -805,7 +826,8 @@ function ensure_custom_ruleset(config, reference) {
url: runtime_rulesets.community_url(reference)
};
let detour = ctx.download_detour_tag(ctx.runtime_settings());
if (is_valid_detour(config, detour))
let is_1_14 = ctx.is_sb_1_14_plus && ctx.is_sb_1_14_plus();
if (!is_1_14 && is_valid_detour(config, detour))
rule_set.download_detour = detour;
rule_set.update_interval = remote_ruleset_update_interval();
push(config.route.rule_set, rule_set);
@@ -842,7 +864,8 @@ function ensure_custom_ruleset(config, reference) {
url: reference
};
let detour = ctx.download_detour_tag(ctx.runtime_settings());
if (is_valid_detour(config, detour))
let is_1_14 = ctx.is_sb_1_14_plus && ctx.is_sb_1_14_plus();
if (!is_1_14 && is_valid_detour(config, detour))
rule_set.download_detour = detour;
rule_set.update_interval = remote_ruleset_update_interval();
push(config.route.rule_set, rule_set);
@@ -895,7 +918,8 @@ function ensure_community_ruleset(config, section_name, community) {
detour = sec_out;
}
}
if (is_valid_detour(config, detour))
let is_1_14 = ctx.is_sb_1_14_plus && ctx.is_sb_1_14_plus();
if (!is_1_14 && is_valid_detour(config, detour))
rule_set.download_detour = detour;
push(config.route.rule_set, rule_set);
}
@@ -1945,5 +1969,6 @@ return {
section_by_name,
add_server_routes,
outbound_supports_udp,
push_section_route_rule
push_section_route_rule,
is_valid_detour
};