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:
@@ -1464,7 +1464,16 @@ function install_zapret2(action, target_tag) {
|
||||
|
||||
let hosts_content = read_file("/etc/hosts") || "";
|
||||
if (index(hosts_content, "::1") < 0)
|
||||
command_success("printf '\\n::1 localhost ip6-localhost ip6-loopback\\n' >> /etc/hosts");
|
||||
command_success("printf '\n::1 localhost ip6-localhost ip6-loopback\n' >> /etc/hosts");
|
||||
|
||||
if (!file_exists("/etc/config/zapret2"))
|
||||
write_file("/etc/config/zapret2", "config zapret2 'main'\n\toption enabled '0'\n");
|
||||
if (uci_core.available()) {
|
||||
if (!uci_core.exists("zapret2.main"))
|
||||
uci_core.set_section("zapret2.main", "zapret2");
|
||||
uci_core.set("zapret2.main.enabled", "0");
|
||||
uci_core.commit("zapret2");
|
||||
}
|
||||
|
||||
run_logged("Updating package lists before " + label + " package installation", pkg_list_update_command());
|
||||
|
||||
|
||||
@@ -164,7 +164,12 @@ function constants_map() {
|
||||
c.COMMUNITY_SERVICES = env("COMMUNITY_SERVICES", "russia_inside russia_outside ukraine_inside geoblock block porn news anime youtube hdrezka tiktok google_ai google_play hodca discord meta twitter cloudflare cloudfront digitalocean hetzner ovh telegram roblox ads_hagezi_pro supercell github");
|
||||
|
||||
c.ZAPRET_PROVIDER_BASE_DIR = env("ZAPRET_PROVIDER_BASE_DIR", "/opt/zapret");
|
||||
c.ZAPRET_PROVIDER_NFQWS_BIN = env("ZAPRET_PROVIDER_NFQWS_BIN", c.ZAPRET_PROVIDER_BASE_DIR + "/nfq/nfqws");
|
||||
let default_nfqws_bin = c.ZAPRET_PROVIDER_BASE_DIR + "/nfq/nfqws";
|
||||
if (fs.stat(default_nfqws_bin) == null && fs.stat(c.ZAPRET_PROVIDER_BASE_DIR + "/nfqws") != null)
|
||||
default_nfqws_bin = c.ZAPRET_PROVIDER_BASE_DIR + "/nfqws";
|
||||
else if (fs.stat(default_nfqws_bin) == null && fs.stat("/usr/bin/nfqws") != null)
|
||||
default_nfqws_bin = "/usr/bin/nfqws";
|
||||
c.ZAPRET_PROVIDER_NFQWS_BIN = env("ZAPRET_PROVIDER_NFQWS_BIN", default_nfqws_bin);
|
||||
c.ZAPRET_PROVIDER_FILES_DIR = env("ZAPRET_PROVIDER_FILES_DIR", c.ZAPRET_PROVIDER_BASE_DIR + "/files");
|
||||
c.ZAPRET_PROVIDER_IPSET_DIR = env("ZAPRET_PROVIDER_IPSET_DIR", c.ZAPRET_PROVIDER_BASE_DIR + "/ipset");
|
||||
c.ZAPRET_LEGACY_RUNTIME_BASE_DIR = env("ZAPRET_LEGACY_RUNTIME_BASE_DIR", "/var/run/tachyon/zapret-runtime");
|
||||
@@ -187,6 +192,8 @@ function constants_map() {
|
||||
let default_nfqws2_bin = c.ZAPRET2_PROVIDER_BASE_DIR + "/nfq2/nfqws2";
|
||||
if (fs.stat(default_nfqws2_bin) == null && fs.stat(c.ZAPRET2_PROVIDER_BASE_DIR + "/nfq/nfqws2") != null)
|
||||
default_nfqws2_bin = c.ZAPRET2_PROVIDER_BASE_DIR + "/nfq/nfqws2";
|
||||
else if (fs.stat(default_nfqws2_bin) == null && fs.stat(c.ZAPRET2_PROVIDER_BASE_DIR + "/nfqws2") != null)
|
||||
default_nfqws2_bin = c.ZAPRET2_PROVIDER_BASE_DIR + "/nfqws2";
|
||||
else if (fs.stat(default_nfqws2_bin) == null && fs.stat("/usr/bin/nfqws2") != null)
|
||||
default_nfqws2_bin = "/usr/bin/nfqws2";
|
||||
c.ZAPRET2_PROVIDER_NFQWS2_BIN = env("ZAPRET2_PROVIDER_NFQWS2_BIN", default_nfqws2_bin);
|
||||
|
||||
@@ -1466,6 +1466,7 @@ function nft_create_runtime_base(table, localv4_set, common_set, port_set, ip_po
|
||||
|
||||
if (!nft_create_chain(table, "dns_redirect", "{ type nat hook prerouting priority -100; policy accept; }") ||
|
||||
!nft_create_chain(table, "mangle", "{ type filter hook prerouting priority -149; policy accept; }") ||
|
||||
!nft_create_chain(table, "raw_output", "{ type filter hook output priority -300; policy accept; }") ||
|
||||
!nft_create_chain(table, "mangle_output", "{ type route hook output priority -150; policy accept; }") ||
|
||||
!nft_create_priority_chains(table) ||
|
||||
!nft_create_chain(table, "parental_control", "{ }") ||
|
||||
@@ -1475,6 +1476,11 @@ function nft_create_runtime_base(table, localv4_set, common_set, port_set, ip_po
|
||||
!nft_create_chain(table, "proxy", "{ type filter hook prerouting priority -100; policy accept; }"))
|
||||
return false;
|
||||
|
||||
if (!nft_add_rule(table, "raw_output", [ "meta", "mark", "&", "0x40000000", "==", "0x40000000", "notrack" ]) ||
|
||||
!nft_add_rule(table, "raw_output", [ "meta", "mark", "&", "0x20000000", "==", "0x20000000", "notrack" ]) ||
|
||||
!nft_add_rule(table, "raw_output", [ "meta", "skuid", "{ 2147483647, 65534 }", "notrack" ]))
|
||||
return false;
|
||||
|
||||
if (!nft_add_rule(table, "dns_redirect", [ "iifname", "@" + as_string(interface_set), "ip", "saddr", "@" + DNS_SOURCE_SET, "tcp", "dport", "53", "counter", "redirect", "to", ":" + as_string(runtime_constants.SOURCE_DNS_INBOUND_PORT) ]) ||
|
||||
!nft_add_rule(table, "dns_redirect", [ "iifname", "@" + as_string(interface_set), "ip", "saddr", "@" + DNS_SOURCE_SET, "udp", "dport", "53", "counter", "redirect", "to", ":" + as_string(runtime_constants.SOURCE_DNS_INBOUND_PORT) ]) ||
|
||||
!nft_add_rule(table, "dns_redirect", [ "iifname", "@" + as_string(interface_set), "ip6", "saddr", "@" + DNS_SOURCE6_SET, "tcp", "dport", "53", "counter", "redirect", "to", ":" + as_string(runtime_constants.SOURCE_DNS_INBOUND_PORT) ]) ||
|
||||
@@ -1594,6 +1600,9 @@ function nft_create_runtime_base(table, localv4_set, common_set, port_set, ip_po
|
||||
!nft_add_rule(table, "proxy", [ "meta", "mark", "&", fakeip_mark, "==", fakeip_mark, "meta", "l4proto", "udp", "tproxy", "ip", "to", ":" + as_string(tproxy_port), "counter" ]) ||
|
||||
!nft_add_rule(table, "proxy", [ "meta", "mark", "&", fakeip_mark, "==", fakeip_mark, "meta", "l4proto", "tcp", "tproxy", "ip6", "to", core_ip.format_ipv6_tproxy_target(tproxy6_address, tproxy_port), "counter" ]) ||
|
||||
!nft_add_rule(table, "proxy", [ "meta", "mark", "&", fakeip_mark, "==", fakeip_mark, "meta", "l4proto", "udp", "tproxy", "ip6", "to", core_ip.format_ipv6_tproxy_target(tproxy6_address, tproxy_port), "counter" ]) ||
|
||||
!nft_add_rule(table, "mangle_output", [ "meta", "mark", "&", "0x40000000", "==", "0x40000000", "counter", "return" ]) ||
|
||||
!nft_add_rule(table, "mangle_output", [ "meta", "mark", "&", "0x20000000", "==", "0x20000000", "counter", "return" ]) ||
|
||||
!nft_add_rule(table, "mangle_output", [ "meta", "skuid", "{ 2147483647, 65534 }", "counter", "return" ]) ||
|
||||
!nft_add_rule(table, "mangle_output", [ "ip", "daddr", "@" + as_string(localv4_set), "return" ]) ||
|
||||
!nft_add_rule(table, "mangle_output", [ "ip6", "daddr", "@" + as_string(localv6_set), "return" ]) ||
|
||||
!nft_add_rule(table, "mangle_output", [ "meta", "mark", outbound_mark, "counter", "return" ]) ||
|
||||
@@ -1601,6 +1610,8 @@ function nft_create_runtime_base(table, localv4_set, common_set, port_set, ip_po
|
||||
return false;
|
||||
|
||||
if (!nft_create_chain(table, "mangle_forward", "{ type filter hook forward priority -150; policy accept; }") ||
|
||||
!nft_add_rule(table, "mangle_forward", [ "meta", "mark", "&", "0x40000000", "==", "0x40000000", "counter", "return" ]) ||
|
||||
!nft_add_rule(table, "mangle_forward", [ "meta", "mark", "&", "0x20000000", "==", "0x20000000", "counter", "return" ]) ||
|
||||
!nft_add_rule(table, "mangle_forward", [ "jump", "guest_forward" ]) ||
|
||||
!nft_add_rule(table, "mangle_forward", [ "jump", "parental_forward" ]))
|
||||
return false;
|
||||
@@ -1720,6 +1731,35 @@ function nft_provider_mark_hex(route_mark_base, index) {
|
||||
return sprintf("0x%08x", base + index);
|
||||
}
|
||||
|
||||
function resolve_provider_bin(action, provider_bin) {
|
||||
if (provider_bin && file_executable(provider_bin))
|
||||
return provider_bin;
|
||||
let candidates = [];
|
||||
if (action == "zapret2") {
|
||||
candidates = [
|
||||
getenv("ZAPRET2_NFQWS2_BIN"),
|
||||
getenv("ZAPRET2_PROVIDER_NFQWS2_BIN"),
|
||||
"/opt/zapret2/nfq2/nfqws2",
|
||||
"/opt/zapret2/nfq/nfqws2",
|
||||
"/opt/zapret2/nfqws2",
|
||||
"/usr/bin/nfqws2"
|
||||
];
|
||||
} else if (action == "zapret") {
|
||||
candidates = [
|
||||
getenv("ZAPRET_NFQWS_BIN"),
|
||||
getenv("ZAPRET_PROVIDER_NFQWS_BIN"),
|
||||
"/opt/zapret/nfq/nfqws",
|
||||
"/opt/zapret/nfqws",
|
||||
"/usr/bin/nfqws"
|
||||
];
|
||||
}
|
||||
for (let c in candidates) {
|
||||
if (c && file_executable(c))
|
||||
return c;
|
||||
}
|
||||
return provider_bin;
|
||||
}
|
||||
|
||||
function nft_create_provider_output_rules_from_sections(sections, table, action, provider_bin, route_mark_base, queue_base, desync_mark, desync_mark_postnat) {
|
||||
if (!file_executable(provider_bin))
|
||||
return true;
|
||||
@@ -1741,6 +1781,7 @@ function nft_create_provider_output_rules_from_sections(sections, table, action,
|
||||
if (!added) {
|
||||
if (!nft_add_rule(table, "mangle_output", [ "meta", "mark", "&", desync_mark, "==", desync_mark, "return" ]) ||
|
||||
!nft_add_rule(table, "mangle_output", [ "meta", "mark", "&", desync_mark_postnat, "==", desync_mark_postnat, "return" ]) ||
|
||||
!nft_add_rule(table, "mangle_output", [ "meta", "skuid", "{ 2147483647, 65534 }", "counter", "return" ]) ||
|
||||
!nft_add_rule(table, "mangle_forward", [ "meta", "mark", "&", desync_mark, "==", desync_mark, "return" ]) ||
|
||||
!nft_add_rule(table, "mangle_forward", [ "meta", "mark", "&", desync_mark_postnat, "==", desync_mark_postnat, "return" ]))
|
||||
return false;
|
||||
@@ -2827,8 +2868,9 @@ function nft_populate_runtime_sets_from_uci(populate_enabled, deferred_section_n
|
||||
let common6 = default_arg(common6_set, "tachyon_subnets6");
|
||||
let ip_port6 = default_arg(ip_port6_set, "tachyon_ip6_ports");
|
||||
let frange6 = "fc00::/18";
|
||||
let taddr6 = "[::1]:1602";
|
||||
nft_rebuild_runtime_from_uci(rt_table, table, localv4, common, port, ip_port, iface, fmark, omark, frange4, tport, "", "", "", "", "", "/opt/zapret2/bin/nfqws2", "0x02000000", "200", "0x00000002", "0x00000004", localv6, common6, ip_port6, frange6, taddr6);
|
||||
let zapret_bin = resolve_provider_bin("zapret", "");
|
||||
let zapret2_bin = resolve_provider_bin("zapret2", "");
|
||||
nft_rebuild_runtime_from_uci(rt_table, table, localv4, common, port, ip_port, iface, fmark, omark, frange4, tport, zapret_bin, "0x01000000", "4000", "0x40000000", "0x20000000", zapret2_bin, "0x02000000", "4300", "0x40000000", "0x20000000", localv6, common6, ip_port6, frange6, taddr6);
|
||||
}
|
||||
|
||||
return nft_populate_runtime_sets_from_sections(uci_sections("section"), populate_enabled, deferred_section_names, table, common_set, port_set, ip_port_set, interface_set, localv4_set, mark, common6_set, ip_port6_set, localv6_set);
|
||||
|
||||
@@ -472,20 +472,19 @@ function start_runtime(cfg) {
|
||||
let started_sections = [];
|
||||
let index_value = 1;
|
||||
for (let section in sections) {
|
||||
let name = section_name(section);
|
||||
if (!start_rule(cfg, section, index_value)) {
|
||||
log_message("Rolling back " + as_string(length(started_sections)) + " started " + cfg.binary_name + " rules.", "warn");
|
||||
for (let started in started_sections) {
|
||||
let name = section_name(started);
|
||||
let pidfile = cfg.pid_dir + "/" + name + ".pid";
|
||||
let child_pidfile = cfg.child_pid_dir + "/" + name + ".pid";
|
||||
kill_pidfile_process(pidfile, "9");
|
||||
kill_pidfile_process(child_pidfile, "9");
|
||||
}
|
||||
exit(1);
|
||||
log_message("Failed to start " + cfg.binary_name + " rule '" + name + "'; skipping rule to preserve service availability.", "warn");
|
||||
} else {
|
||||
push(started_sections, section);
|
||||
}
|
||||
push(started_sections, section);
|
||||
index_value++;
|
||||
}
|
||||
|
||||
if (length(started_sections) == 0 && length(sections) > 0) {
|
||||
log_message("Failed to start any " + cfg.binary_name + " rules out of " + as_string(length(sections)) + " configured. Aborted.", "fatal");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function runtime_tag(base, postfix) {
|
||||
|
||||
@@ -62,11 +62,19 @@ function resolve_blobs(args_str) {
|
||||
return result;
|
||||
}
|
||||
|
||||
function safe_str(val) {
|
||||
if (type(as_string) == "function")
|
||||
return as_string(val);
|
||||
if (val == null)
|
||||
return "";
|
||||
return "" + val;
|
||||
}
|
||||
|
||||
function prepare_strategy_args(raw_opt) {
|
||||
let raw_str = as_string(raw_opt);
|
||||
let raw_str = safe_str(raw_opt);
|
||||
let extra_args = resolve_blobs(raw_str);
|
||||
let filter_prefix = [];
|
||||
if (index(raw_str, "--filter-tcp") < 0 && index(raw_str, "--filter-l7") < 0) {
|
||||
if (index(raw_str, "--filter-tcp") < 0 && index(raw_str, "--filter-l7") < 0 && index(raw_str, "--filter-udp") < 0) {
|
||||
push(filter_prefix, "--filter-tcp=443");
|
||||
push(filter_prefix, "--filter-l7=tls");
|
||||
push(filter_prefix, "--payload=tls_client_hello");
|
||||
|
||||
@@ -108,7 +108,10 @@ function get_system_status() {
|
||||
status_obj.singbox_variant = sb_variant;
|
||||
|
||||
// zapret2 status
|
||||
let z2_installed = fs.stat("/opt/zapret2/nfq2/nfqws2") != null;
|
||||
let z2_installed = fs.stat("/opt/zapret2/nfq2/nfqws2") != null ||
|
||||
fs.stat("/opt/zapret2/nfq/nfqws2") != null ||
|
||||
fs.stat("/opt/zapret2/nfqws2") != null ||
|
||||
fs.stat("/usr/bin/nfqws2") != null;
|
||||
status_obj.zapret2_installed = z2_installed;
|
||||
if (z2_installed) {
|
||||
let z2_running = command_success_from_args(["pidof", "nfqws2"]);
|
||||
|
||||
@@ -597,7 +597,40 @@ function release_start_subscription_update_lock() {
|
||||
start_subscription_update_lock_held = false;
|
||||
}
|
||||
|
||||
function resolve_zapret_bin() {
|
||||
let candidate_bins = [
|
||||
getenv("ZAPRET_NFQWS_BIN"),
|
||||
getenv("ZAPRET_PROVIDER_NFQWS_BIN"),
|
||||
ZAPRET_PROVIDER_NFQWS_BIN,
|
||||
"/opt/zapret/nfq/nfqws",
|
||||
"/opt/zapret/nfqws",
|
||||
"/usr/bin/nfqws"
|
||||
];
|
||||
for (let b in candidate_bins) {
|
||||
if (b && fs.stat(b) != null) return b;
|
||||
}
|
||||
return ZAPRET_PROVIDER_NFQWS_BIN;
|
||||
}
|
||||
|
||||
function resolve_zapret2_bin() {
|
||||
let candidate_bins = [
|
||||
getenv("ZAPRET2_NFQWS2_BIN"),
|
||||
getenv("ZAPRET2_PROVIDER_NFQWS2_BIN"),
|
||||
ZAPRET2_PROVIDER_NFQWS2_BIN,
|
||||
"/opt/zapret2/nfq2/nfqws2",
|
||||
"/opt/zapret2/nfq/nfqws2",
|
||||
"/opt/zapret2/nfqws2",
|
||||
"/usr/bin/nfqws2"
|
||||
];
|
||||
for (let b in candidate_bins) {
|
||||
if (b && fs.stat(b) != null) return b;
|
||||
}
|
||||
return ZAPRET2_PROVIDER_NFQWS2_BIN;
|
||||
}
|
||||
|
||||
function nft_rebuild_runtime() {
|
||||
let zapret_bin = resolve_zapret_bin();
|
||||
let zapret2_bin = resolve_zapret2_bin();
|
||||
return module_status(NFT_UC, [
|
||||
"nft-rebuild-runtime-from-uci",
|
||||
RT_TABLE_NAME,
|
||||
@@ -611,12 +644,12 @@ function nft_rebuild_runtime() {
|
||||
NFT_OUTBOUND_MARK,
|
||||
SB_FAKEIP_INET4_RANGE,
|
||||
SB_TPROXY_INBOUND_PORT,
|
||||
ZAPRET_PROVIDER_NFQWS_BIN,
|
||||
zapret_bin,
|
||||
ZAPRET_ROUTE_MARK_BASE,
|
||||
ZAPRET_QUEUE_BASE,
|
||||
ZAPRET_DESYNC_MARK,
|
||||
ZAPRET_DESYNC_MARK_POSTNAT,
|
||||
ZAPRET2_PROVIDER_NFQWS2_BIN,
|
||||
zapret2_bin,
|
||||
ZAPRET2_ROUTE_MARK_BASE,
|
||||
ZAPRET2_QUEUE_BASE,
|
||||
ZAPRET2_DESYNC_MARK,
|
||||
|
||||
@@ -93,8 +93,10 @@ function emit_reload_plan(previous, current, context) {
|
||||
needs.sing_box_reload = true;
|
||||
}
|
||||
|
||||
if (changed.zapret_runtime)
|
||||
if (changed.zapret_runtime) {
|
||||
needs.zapret_restart = true;
|
||||
needs.nft_rebuild = true;
|
||||
}
|
||||
|
||||
if (changed.zapret2_queue) {
|
||||
needs.zapret2_restart = true;
|
||||
@@ -102,8 +104,10 @@ function emit_reload_plan(previous, current, context) {
|
||||
needs.sing_box_reload = true;
|
||||
}
|
||||
|
||||
if (changed.zapret2_runtime)
|
||||
if (changed.zapret2_runtime) {
|
||||
needs.zapret2_restart = true;
|
||||
needs.nft_rebuild = true;
|
||||
}
|
||||
|
||||
if (changed.byedpi_runtime)
|
||||
needs.byedpi_restart = true;
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -147,4 +147,48 @@ if grep -q '"download_detour": "zapret_sec-out"' "$output_zapret"; then
|
||||
fail "zapret-out must never be selected as download_detour due to routing_mark"
|
||||
fi
|
||||
|
||||
# 5. Verify mieru outbound is rejected as download_detour and http_clients dial_detour
|
||||
cat >"$WORK_DIR/fixture_mieru.json" <<'JSON'
|
||||
{
|
||||
"settings": {
|
||||
".name": "settings",
|
||||
".type": "settings",
|
||||
"enabled": "1",
|
||||
"dns_type": "udp",
|
||||
"dns_server": "1.1.1.1",
|
||||
"service_listen_address": "127.0.0.1"
|
||||
},
|
||||
"section": [
|
||||
{
|
||||
".name": "mieru_sec",
|
||||
".type": "section",
|
||||
"enabled": "1",
|
||||
"action": "connection",
|
||||
"outbound_jsons": [ "{\"type\":\"mieru\",\"server\":\"1.2.3.4\",\"server_port\":443}" ],
|
||||
"community_lists": [ "google_ai" ]
|
||||
}
|
||||
]
|
||||
}
|
||||
JSON
|
||||
|
||||
output_mieru="$WORK_DIR/out_mieru.json"
|
||||
mkdir -p "$output_mieru.section-cache" "$output_mieru.rulesets"
|
||||
ucode -L "$TACHYON_LIB" "$GENERATOR_UC" generate-config-fixture \
|
||||
"$WORK_DIR/fixture_mieru.json" "$output_mieru" "127.0.0.1" "0" "1"
|
||||
|
||||
if grep -q '"download_detour": "mieru_sec-out"' "$output_mieru"; then
|
||||
fail "mieru outbound must never be assigned to rule_set download_detour (early startup crash)"
|
||||
fi
|
||||
|
||||
output_mieru_v14="$WORK_DIR/out_mieru_v14.json"
|
||||
mkdir -p "$output_mieru_v14.section-cache" "$output_mieru_v14.rulesets"
|
||||
SB_VERSION_STATE_FILE="$WORK_DIR/sb_v14" \
|
||||
ucode -L "$TACHYON_LIB" "$GENERATOR_UC" generate-config-fixture \
|
||||
"$WORK_DIR/fixture_mieru.json" "$output_mieru_v14" "127.0.0.1" "0" "1"
|
||||
|
||||
if grep -q '"dial_detour": "mieru_sec-out"' "$output_mieru_v14"; then
|
||||
fail "mieru outbound must never be assigned as http_clients dial_detour"
|
||||
fi
|
||||
|
||||
printf "ruleset download detour checks passed\n"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user