fix(singbox): resolve 1.14.1 DNS deprecation, store_rdrc, ruleset download_detour, and provider routing isolation
This commit is contained in:
@@ -7,8 +7,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: TACHYON\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-09-17 05:45+0300\n"
|
||||
"PO-Revision-Date: 2026-09-17 05:45+0300\n"
|
||||
"POT-Creation-Date: 2026-09-17 07:46+0300\n"
|
||||
"PO-Revision-Date: 2026-09-17 07:46+0300\n"
|
||||
"Last-Translator: Dushnilin <dushnilin@gmail.com>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: \n"
|
||||
|
||||
@@ -7,8 +7,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: TACHYON\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-09-17 08:45+0300\n"
|
||||
"PO-Revision-Date: 2026-09-17 08:45+0300\n"
|
||||
"POT-Creation-Date: 2026-09-17 10:46+0300\n"
|
||||
"PO-Revision-Date: 2026-09-17 10:46+0300\n"
|
||||
"Last-Translator: Dushnilin\n"
|
||||
"Language-Team: none\n"
|
||||
"Language: ru\n"
|
||||
|
||||
@@ -7,8 +7,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: TACHYON\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-09-17 08:45+0300\n"
|
||||
"PO-Revision-Date: 2026-09-17 08:45+0300\n"
|
||||
"POT-Creation-Date: 2026-09-17 10:46+0300\n"
|
||||
"PO-Revision-Date: 2026-09-17 10:46+0300\n"
|
||||
"Last-Translator: Dushnilin\n"
|
||||
"Language-Team: none\n"
|
||||
"Language: ru\n"
|
||||
|
||||
@@ -7,8 +7,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: TACHYON\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-09-17 05:45+0300\n"
|
||||
"PO-Revision-Date: 2026-09-17 05:45+0300\n"
|
||||
"POT-Creation-Date: 2026-09-17 07:46+0300\n"
|
||||
"PO-Revision-Date: 2026-09-17 07:46+0300\n"
|
||||
"Last-Translator: Dushnilin <dushnilin@gmail.com>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: \n"
|
||||
|
||||
@@ -751,6 +751,93 @@ function rule_sets_with_subnets_value(section) {
|
||||
return list_option_value_from_array(rule_sets_with_subnets(section));
|
||||
}
|
||||
|
||||
function section_has_routing_matchers(section) {
|
||||
if (section == null || type(section) != "object")
|
||||
return false;
|
||||
|
||||
if (has_dns_matchers(section))
|
||||
return true;
|
||||
|
||||
if (length(list_option(section, "domain")) > 0 || option(section, "domain", "") != "" || option(section, "domain_text", "") != "" ||
|
||||
length(list_option(section, "domain_suffix")) > 0 || option(section, "domain_suffix", "") != "" || option(section, "domain_suffix_text", "") != "" ||
|
||||
length(list_option(section, "domain_keyword")) > 0 || option(section, "domain_keyword", "") != "" || option(section, "domain_keyword_text", "") != "" ||
|
||||
length(list_option(section, "domain_regex")) > 0 || option(section, "domain_regex", "") != "" || option(section, "domain_regex_text", "") != "" ||
|
||||
length(list_option(section, "user_domains")) > 0 || option(section, "user_domains", "") != "" || option(section, "user_domains_text", "") != "")
|
||||
return true;
|
||||
|
||||
if (rule_condition_csv(section, "ip_cidr", "subnets") != "" ||
|
||||
rule_condition_csv(section, "source_ip_cidr", "subnets") != "" ||
|
||||
length(list_option(section, "fully_routed_ips")) > 0 ||
|
||||
option(section, "fully_routed_ips", "") != "" ||
|
||||
length(list_option(section, "ip_cidr")) > 0 ||
|
||||
option(section, "ip_cidr", "") != "" ||
|
||||
option(section, "ip_cidr_text", "") != "" ||
|
||||
length(list_option(section, "source_ip_cidr")) > 0 ||
|
||||
option(section, "source_ip_cidr", "") != "" ||
|
||||
option(section, "source_ip_cidr_text", "") != "")
|
||||
return true;
|
||||
|
||||
if (rule_condition_csv(section, "ports", "ports") != "" ||
|
||||
length(list_option(section, "ports")) > 0 ||
|
||||
option(section, "ports", "") != "" ||
|
||||
option(section, "ports_text", "") != "" ||
|
||||
option(section, "port", "") != "" ||
|
||||
option(section, "port_range", "") != "")
|
||||
return true;
|
||||
|
||||
if (length(list_option(section, "protocol")) > 0 ||
|
||||
option(section, "protocol", "") != "")
|
||||
return true;
|
||||
|
||||
if (length(dscp_list(section)) > 0)
|
||||
return true;
|
||||
|
||||
if (length(geoip_country_list(section)) > 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function section_is_active_provider(section, action) {
|
||||
if (section == null || type(section) != "object")
|
||||
return false;
|
||||
let name = section_name(section);
|
||||
if (name == "")
|
||||
return false;
|
||||
if (!bool_option(section, "enabled", true))
|
||||
return false;
|
||||
if (action != null && action != "" && option(section, "action", "") != action)
|
||||
return false;
|
||||
|
||||
return section_has_routing_matchers(section) || bool_option(section, "match_all", false);
|
||||
}
|
||||
|
||||
function active_provider_sections(action, sections) {
|
||||
let result = [];
|
||||
let list = sections;
|
||||
if (list == null)
|
||||
list = uci_core.section_objects(CONFIG_NAME, "section");
|
||||
|
||||
for (let section in list) {
|
||||
if (section_is_active_provider(section, action))
|
||||
push(result, section);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function active_provider_section_index(action, sections, target_section) {
|
||||
let target_name = type(target_section) == "object" ? section_name(target_section) : as_string(target_section);
|
||||
if (target_name == "")
|
||||
return 0;
|
||||
|
||||
let active = active_provider_sections(action, sections);
|
||||
for (let i = 0; i < length(active); i++) {
|
||||
if (section_name(active[i]) == target_name)
|
||||
return i + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function has_connection_sources(section) {
|
||||
return length(connection_urls(section)) > 0 ||
|
||||
length(subscription_urls(section)) > 0 ||
|
||||
@@ -1653,5 +1740,9 @@ return {
|
||||
fptn_sections,
|
||||
is_wdtt_action,
|
||||
is_olcrtc_action,
|
||||
is_fptn_action
|
||||
is_fptn_action,
|
||||
section_has_routing_matchers,
|
||||
section_is_active_provider,
|
||||
active_provider_sections,
|
||||
active_provider_section_index
|
||||
};
|
||||
|
||||
@@ -1537,6 +1537,19 @@ function validate_rule(section, sections, context) {
|
||||
fail_validation("FPTN rule '" + name + "': " + vres.error + ". Aborted.");
|
||||
}
|
||||
|
||||
let provider_labels = {
|
||||
zapret: "Zapret",
|
||||
zapret2: "Zapret2",
|
||||
byedpi: "ByeDPI",
|
||||
wdtt: "WDTT",
|
||||
olcrtc: "OlcRTC",
|
||||
fptn: "FPTN"
|
||||
};
|
||||
if (provider_labels[action] != null) {
|
||||
if (!connections.section_has_routing_matchers(section) && !bool_option(section, "match_all", false))
|
||||
fail_validation(provider_labels[action] + " rule '" + name + "' has no routing conditions. Add domains, community lists, IP/subnet/port/protocol matchers or enable Match All explicitly. Aborted.");
|
||||
}
|
||||
|
||||
if (connections.is_connections_action(action)) {
|
||||
validate_dashboard_filter(section);
|
||||
|
||||
|
||||
@@ -36,11 +36,13 @@ function provider_available() {
|
||||
}
|
||||
|
||||
function enabled_sections() {
|
||||
let sections = uci_core.section_objects(CONFIG_NAME, "section");
|
||||
if (connections && connections.active_provider_sections)
|
||||
return connections.active_provider_sections("fptn", sections);
|
||||
if (connections && connections.fptn_sections)
|
||||
return connections.fptn_sections();
|
||||
|
||||
let result = [];
|
||||
let sections = uci_core.section_objects(CONFIG_NAME, "section");
|
||||
for (let s in sections) {
|
||||
if (bool_option(s, "enabled", true) && option(s, "action", "") == "fptn")
|
||||
push(result, s);
|
||||
@@ -94,6 +96,7 @@ function remove_kernel_routing() {
|
||||
}
|
||||
|
||||
command_status("ip route flush table " + cfg.route_table + " 2>/dev/null; true");
|
||||
command_status("ip -4 route del default dev " + shell_quote(cfg.tun_interface) + " table main 2>/dev/null; true");
|
||||
if (command_status("ip link show " + shell_quote(cfg.tun_interface) + " >/dev/null 2>&1") == 0)
|
||||
command_status("ip link set dev " + shell_quote(cfg.tun_interface) + " down 2>/dev/null; true");
|
||||
}
|
||||
@@ -116,6 +119,13 @@ function install_kernel_routing() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure table main default route was not hijacked by fptn-client-cli
|
||||
let main_default_dev = trim(command_output("ip -4 route show table main default 2>/dev/null | awk '{print $5; exit}'"));
|
||||
if (main_default_dev == cfg.tun_interface) {
|
||||
log_message("FPTN client hijacked table main default route; restoring WAN route", "warn");
|
||||
command_status("ip -4 route del default dev " + shell_quote(cfg.tun_interface) + " table main 2>/dev/null; true");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ let common = require("core.common");
|
||||
let constants = require("core.constants");
|
||||
let uci_core = require("core.uci");
|
||||
let runtime_constants = require("singbox.constants");
|
||||
let connections = require("config.connections");
|
||||
|
||||
let as_string = common.as_string;
|
||||
let bool_value = common.bool_value;
|
||||
@@ -77,11 +78,7 @@ function strategy_words(value) {
|
||||
}
|
||||
|
||||
function enabled_sections(cfg) {
|
||||
let result = [];
|
||||
for (let section in uci_sections("section"))
|
||||
if (bool_option(section, "enabled", true) && option(section, "action", "") == cfg.action)
|
||||
push(result, section);
|
||||
return result;
|
||||
return connections.active_provider_sections(cfg.action, uci_sections("section"));
|
||||
}
|
||||
|
||||
let parse_number = common.parse_number;
|
||||
|
||||
@@ -464,6 +464,39 @@ function has_domain_matchers(path) {
|
||||
return value_has_domain_matchers(read_json_file(path));
|
||||
}
|
||||
|
||||
function value_has_ip_matchers(value) {
|
||||
if (type(value) == "array") {
|
||||
for (let item in value) {
|
||||
if (value_has_ip_matchers(item))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type(value) != "object")
|
||||
return false;
|
||||
|
||||
for (let key, item in value) {
|
||||
if (key == "ip_cidr" || key == "source_ip_cidr" || key == "ip_is_private") {
|
||||
if (type(item) == "array" && length(item) > 0)
|
||||
return true;
|
||||
if (type(item) == "string" && item != "")
|
||||
return true;
|
||||
if (type(item) == "boolean" && item)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (value_has_ip_matchers(item))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function has_ip_matchers(path) {
|
||||
return value_has_ip_matchers(read_json_file(path));
|
||||
}
|
||||
|
||||
function has_rules(path) {
|
||||
let ruleset = object_or_empty(read_json_file(path));
|
||||
return length(array_or_empty(ruleset.rules)) > 0;
|
||||
@@ -507,6 +540,7 @@ function module_exports() {
|
||||
extract_ip_cidr,
|
||||
extract_ip_cidr_nft_elements,
|
||||
has_domain_matchers,
|
||||
has_ip_matchers,
|
||||
has_rules,
|
||||
compile_plain_list,
|
||||
ruleset_tag,
|
||||
@@ -534,6 +568,8 @@ else if (mode == "extract-ip-cidr-nft")
|
||||
extract_ip_cidr_nft_elements(ARGV[1], ARGV[2], ARGV[3], ARGV[4] || "[]", ARGV[5] || "[]");
|
||||
else if (mode == "has-domain-matchers")
|
||||
exit(has_domain_matchers(ARGV[1]) ? 0 : 1);
|
||||
else if (mode == "has-ip-matchers")
|
||||
exit(has_ip_matchers(ARGV[1]) ? 0 : 1);
|
||||
else if (mode == "has-rules")
|
||||
exit(has_rules(ARGV[1]) ? 0 : 1);
|
||||
else {
|
||||
|
||||
@@ -422,29 +422,76 @@ 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) {}
|
||||
let cached_sb_version = null;
|
||||
|
||||
function detect_sing_box_version() {
|
||||
if (cached_sb_version != null)
|
||||
return cached_sb_version;
|
||||
|
||||
let env_file = getenv("SB_VERSION_STATE_FILE");
|
||||
if (env_file != null && env_file != "") {
|
||||
let ver = trim(fs.readfile(env_file) || "");
|
||||
if (ver != "") {
|
||||
cached_sb_version = ver;
|
||||
return ver;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
for (let cmd in [ "sing-box version 2>/dev/null", "/usr/bin/sing-box version 2>/dev/null" ]) {
|
||||
let pipe = fs.popen(cmd, "r");
|
||||
if (pipe) {
|
||||
let out = pipe.read("all");
|
||||
pipe.close();
|
||||
let m = match(out, /sing-box version ([^\s]+)/);
|
||||
if (m) {
|
||||
cached_sb_version = m[1];
|
||||
try {
|
||||
let cur_disk = trim(fs.readfile("/etc/tachyon/sing-box-version") || "");
|
||||
if (cur_disk != cached_sb_version)
|
||||
fs.writefile("/etc/tachyon/sing-box-version", cached_sb_version + "\n");
|
||||
} catch (e) {}
|
||||
return cached_sb_version;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
let sb_version_file = "/etc/tachyon/sing-box-version";
|
||||
let 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) || "");
|
||||
}
|
||||
cached_sb_version = sb_version_val;
|
||||
return cached_sb_version;
|
||||
}
|
||||
|
||||
function is_sb_1_14_plus_detected(sb_version_val) {
|
||||
if (sb_version_val == null || sb_version_val == "")
|
||||
sb_version_val = detect_sing_box_version();
|
||||
if (sb_version_val != "") {
|
||||
let m = match(sb_version_val, /^v?([0-9]+)\.([0-9]+)/);
|
||||
if (m) {
|
||||
let major = int(m[1]);
|
||||
let minor = int(m[2]);
|
||||
return major > 1 || (major == 1 && minor >= 14);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function is_sb_1_15_plus_detected(sb_version_val) {
|
||||
if (sb_version_val == null || sb_version_val == "")
|
||||
sb_version_val = detect_sing_box_version();
|
||||
if (sb_version_val != "") {
|
||||
let m = match(sb_version_val, /^v?([0-9]+)\.([0-9]+)/);
|
||||
if (m) {
|
||||
let major = int(m[1]);
|
||||
let minor = int(m[2]);
|
||||
return major > 1 || (major == 1 && minor >= 15);
|
||||
}
|
||||
}
|
||||
if (sb_version_val != "")
|
||||
return match(sb_version_val, /^v?1\.(1[4-9]|[2-9][0-9])\./) != null;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -567,39 +614,25 @@ function base_config(settings, service_address, runtime_context) {
|
||||
strategy: option(settings, "dns_strategy", "prefer_ipv4")
|
||||
};
|
||||
|
||||
let sb_version_file = getenv("SB_VERSION_STATE_FILE") || "/etc/tachyon/sing-box-version";
|
||||
let 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) {}
|
||||
}
|
||||
let sb_variant_file = getenv("SB_VARIANT_STATE_FILE") || "/etc/tachyon/sing-box-variant";
|
||||
let sb_variant_val = trim(fs.readfile(sb_variant_file) || "");
|
||||
let is_extended_variant = sb_variant_val == "extended" || sb_variant_val == "extended-compressed" || index(sb_version_val, "extended") >= 0;
|
||||
|
||||
let use_legacy_rdrc = is_extended_variant || (sb_version_val != ""
|
||||
? (match(sb_version_val, /^v?1\.1[0-3]\./) != null)
|
||||
: false);
|
||||
|
||||
let sb_version_val = detect_sing_box_version();
|
||||
let is_sb_1_14_plus = is_sb_1_14_plus_detected(sb_version_val);
|
||||
let is_sb_1_15_plus = is_sb_1_15_plus_detected(sb_version_val);
|
||||
ctx.is_sb_1_14_plus = function() { return is_sb_1_14_plus; };
|
||||
ctx.is_sb_1_15_plus = function() { return is_sb_1_15_plus; };
|
||||
|
||||
let use_legacy_rdrc = !is_sb_1_14_plus;
|
||||
|
||||
let route_section = runtime_route.config(settings, runtime_context);
|
||||
if (is_sb_1_14_plus)
|
||||
route_section.default_http_client = "ruleset-http";
|
||||
|
||||
if (is_sb_1_14_plus) {
|
||||
dns_section.optimistic = {
|
||||
enabled: true,
|
||||
timeout: "3d"
|
||||
};
|
||||
}
|
||||
|
||||
let cache_file_section = {
|
||||
enabled: true,
|
||||
path: cache_path,
|
||||
@@ -610,6 +643,11 @@ function base_config(settings, service_address, runtime_context) {
|
||||
else
|
||||
cache_file_section.store_dns = true;
|
||||
|
||||
if (is_sb_1_15_plus) {
|
||||
cache_file_section.buffer_size = "1MB";
|
||||
cache_file_section.flush_interval = "10m";
|
||||
}
|
||||
|
||||
let base_cfg = {
|
||||
log: {
|
||||
disabled: false,
|
||||
@@ -1547,6 +1585,7 @@ 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;
|
||||
ctx.is_sb_1_15_plus = is_sb_1_15_plus_detected;
|
||||
|
||||
generator_outbounds.init(ctx);
|
||||
generator_routes.init(ctx);
|
||||
|
||||
@@ -1772,6 +1772,8 @@ function add_openvpn_endpoint(config, section) {
|
||||
}
|
||||
|
||||
function enabled_action_index(sections, target_section, action_name) {
|
||||
if (connections && connections.active_provider_section_index)
|
||||
return connections.active_provider_section_index(action_name, sections, target_section);
|
||||
let index = 0;
|
||||
for (let section in sections) {
|
||||
if (option(section, "action", "") != action_name)
|
||||
@@ -1784,6 +1786,8 @@ function enabled_action_index(sections, target_section, action_name) {
|
||||
}
|
||||
|
||||
function add_zapret_outbound(config, section, sections) {
|
||||
if (connections && !connections.section_is_active_provider(section, "zapret"))
|
||||
return;
|
||||
let index = enabled_action_index(sections, section, "zapret");
|
||||
if (index <= 0)
|
||||
ctx.runtime_generate_unsupported("unable to resolve Zapret index for " + section[".name"]);
|
||||
@@ -1795,6 +1799,8 @@ function add_zapret_outbound(config, section, sections) {
|
||||
}
|
||||
|
||||
function add_zapret2_outbound(config, section, sections) {
|
||||
if (connections && !connections.section_is_active_provider(section, "zapret2"))
|
||||
return;
|
||||
let index = enabled_action_index(sections, section, "zapret2");
|
||||
if (index <= 0)
|
||||
ctx.runtime_generate_unsupported("unable to resolve Zapret2 index for " + section[".name"]);
|
||||
@@ -1806,6 +1812,8 @@ function add_zapret2_outbound(config, section, sections) {
|
||||
}
|
||||
|
||||
function add_byedpi_outbound(config, section, sections) {
|
||||
if (connections && !connections.section_is_active_provider(section, "byedpi"))
|
||||
return;
|
||||
let index = enabled_action_index(sections, section, "byedpi");
|
||||
if (index <= 0)
|
||||
ctx.runtime_generate_unsupported("unable to resolve ByeDPI index for " + section[".name"]);
|
||||
@@ -1819,6 +1827,8 @@ function add_byedpi_outbound(config, section, sections) {
|
||||
}
|
||||
|
||||
function add_wdtt_outbound(config, section, sections) {
|
||||
if (connections && !connections.section_is_active_provider(section, "wdtt"))
|
||||
return;
|
||||
let index = enabled_action_index(sections, section, "wdtt");
|
||||
if (index <= 0)
|
||||
ctx.runtime_generate_unsupported("unable to resolve WDTT index for " + section[".name"]);
|
||||
@@ -1832,6 +1842,8 @@ function add_wdtt_outbound(config, section, sections) {
|
||||
}
|
||||
|
||||
function add_olcrtc_outbound(config, section, sections) {
|
||||
if (connections && !connections.section_is_active_provider(section, "olcrtc"))
|
||||
return;
|
||||
let index = enabled_action_index(sections, section, "olcrtc");
|
||||
if (index <= 0)
|
||||
ctx.runtime_generate_unsupported("unable to resolve OlcRTC index for " + section[".name"]);
|
||||
@@ -1847,6 +1859,8 @@ function add_olcrtc_outbound(config, section, sections) {
|
||||
// FPTN routes traffic through its TUN interface (tun-fptn) via kernel routing.
|
||||
// fwmark 0x00300000 (3145728) → ip rule → routing table 4249 → dev tun-fptn.
|
||||
function add_fptn_outbound(config, section, sections) {
|
||||
if (connections && !connections.section_is_active_provider(section, "fptn"))
|
||||
return;
|
||||
push(config.outbounds, {
|
||||
type: "direct",
|
||||
tag: outbound_tag(section[".name"]),
|
||||
|
||||
@@ -919,6 +919,9 @@ function ensure_custom_ruleset(config, reference) {
|
||||
else if (runtime_rulesets.is_valid_srs_file(etc_srs) || helpers.file_is_usable(etc_srs, 16))
|
||||
local_path = etc_srs;
|
||||
|
||||
if (config.route.rule_set == null)
|
||||
config.route.rule_set = [];
|
||||
|
||||
if (local_path != null) {
|
||||
push(config.route.rule_set, {
|
||||
type: "local",
|
||||
@@ -935,8 +938,7 @@ function ensure_custom_ruleset(config, reference) {
|
||||
url: runtime_rulesets.community_url(reference)
|
||||
};
|
||||
let detour = ctx.download_detour_tag(ctx.runtime_settings());
|
||||
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))
|
||||
if (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);
|
||||
@@ -946,8 +948,6 @@ function ensure_custom_ruleset(config, reference) {
|
||||
}
|
||||
|
||||
tag_name = "inline-custom-" + runtime_rulesets.hash12(reference) + "-ruleset";
|
||||
if (kind == "unknown")
|
||||
kind = "domains";
|
||||
if (ruleset_registered(config, tag_name))
|
||||
return { tag: tag_name, kind };
|
||||
|
||||
@@ -1072,6 +1072,8 @@ function ensure_custom_ruleset(config, reference) {
|
||||
// Skip broken local ruleset files — nonexistent or suspiciously small
|
||||
if (!helpers.file_is_usable(reference, 100))
|
||||
return null;
|
||||
if (config.route.rule_set == null)
|
||||
config.route.rule_set = [];
|
||||
push(config.route.rule_set, {
|
||||
type: "local",
|
||||
tag: tag_name,
|
||||
@@ -1087,10 +1089,11 @@ function ensure_custom_ruleset(config, reference) {
|
||||
url: reference
|
||||
};
|
||||
let detour = ctx.download_detour_tag(ctx.runtime_settings());
|
||||
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))
|
||||
if (is_valid_detour(config, detour))
|
||||
rule_set.download_detour = detour;
|
||||
rule_set.update_interval = remote_ruleset_update_interval();
|
||||
if (config.route.rule_set == null)
|
||||
config.route.rule_set = [];
|
||||
push(config.route.rule_set, rule_set);
|
||||
}
|
||||
else {
|
||||
@@ -1118,6 +1121,9 @@ function ensure_community_ruleset(config, section_name, community) {
|
||||
else if (runtime_rulesets.is_valid_srs_file(etc_srs) || helpers.file_is_usable(etc_srs, 16))
|
||||
local_path = etc_srs;
|
||||
|
||||
if (config.route.rule_set == null)
|
||||
config.route.rule_set = [];
|
||||
|
||||
if (local_path != null) {
|
||||
push(config.route.rule_set, {
|
||||
type: "local",
|
||||
@@ -1141,8 +1147,7 @@ function ensure_community_ruleset(config, section_name, community) {
|
||||
detour = sec_out;
|
||||
}
|
||||
}
|
||||
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))
|
||||
if (is_valid_detour(config, detour))
|
||||
rule_set.download_detour = detour;
|
||||
push(config.route.rule_set, rule_set);
|
||||
}
|
||||
@@ -1210,7 +1215,7 @@ function rebuild_local_domain_ip_list_ruleset(section_name, references, domains_
|
||||
}
|
||||
}
|
||||
|
||||
function add_domain_ip_list_ruleset(config, section_name, rule_set_tags, dns_rule_set_tags, references, domains_only) {
|
||||
function add_domain_ip_list_ruleset(config, section_name, rule_set_tags, dns_query_rule_set_tags, dns_response_rule_set_tags, references, domains_only) {
|
||||
if (length(references) == 0)
|
||||
return;
|
||||
|
||||
@@ -1222,6 +1227,8 @@ function add_domain_ip_list_ruleset(config, section_name, rule_set_tags, dns_rul
|
||||
|
||||
let tag_name = domain_ip_list_ruleset_tag(section_name);
|
||||
if (!ruleset_registered(config, tag_name)) {
|
||||
if (config.route.rule_set == null)
|
||||
config.route.rule_set = [];
|
||||
push(config.route.rule_set, {
|
||||
type: "local",
|
||||
tag: tag_name,
|
||||
@@ -1232,8 +1239,23 @@ function add_domain_ip_list_ruleset(config, section_name, rule_set_tags, dns_rul
|
||||
|
||||
if (!domains_only)
|
||||
push(rule_set_tags, tag_name);
|
||||
if (source_rulesets.has_domain_matchers(ruleset_path))
|
||||
push(dns_rule_set_tags, tag_name);
|
||||
|
||||
let is_1_14 = ctx.is_sb_1_14_plus && ctx.is_sb_1_14_plus();
|
||||
if (source_rulesets.has_domain_matchers(ruleset_path)) {
|
||||
if (is_1_14 && source_rulesets.has_ip_matchers(ruleset_path)) {
|
||||
if (dns_response_rule_set_tags != null)
|
||||
push(dns_response_rule_set_tags, tag_name);
|
||||
else if (dns_query_rule_set_tags != null)
|
||||
push(dns_query_rule_set_tags, tag_name);
|
||||
} else if (dns_query_rule_set_tags != null) {
|
||||
push(dns_query_rule_set_tags, tag_name);
|
||||
}
|
||||
} else if (source_rulesets.has_ip_matchers(ruleset_path)) {
|
||||
if (is_1_14 && dns_response_rule_set_tags != null)
|
||||
push(dns_response_rule_set_tags, tag_name);
|
||||
else if (!is_1_14 && dns_query_rule_set_tags != null)
|
||||
push(dns_query_rule_set_tags, tag_name);
|
||||
}
|
||||
}
|
||||
|
||||
function legacy_condition_values(section, key) {
|
||||
@@ -1441,14 +1463,25 @@ function add_dns_action_rules_for_section(config, section) {
|
||||
}
|
||||
for (let reference in connections.rule_sets(section)) {
|
||||
let ensured = ensure_custom_ruleset(config, as_string(reference));
|
||||
if (ensured != null && ensured.kind == "domains")
|
||||
if (ensured == null)
|
||||
continue;
|
||||
if (ensured.kind == "domains")
|
||||
push(query_rule_set_tags, ensured.tag);
|
||||
else
|
||||
push(response_rule_set_tags, ensured.tag);
|
||||
}
|
||||
for (let reference in connections.rule_sets_with_subnets(section)) {
|
||||
let ensured = ensure_custom_ruleset(config, as_string(reference));
|
||||
if (ensured == null)
|
||||
continue;
|
||||
push(response_rule_set_tags, ensured.tag);
|
||||
}
|
||||
add_domain_ip_list_ruleset(
|
||||
config,
|
||||
section_name,
|
||||
[],
|
||||
query_rule_set_tags,
|
||||
response_rule_set_tags,
|
||||
list_option(section, "domain_ip_lists"),
|
||||
true
|
||||
);
|
||||
@@ -1833,20 +1866,22 @@ function add_combined_route_for_section(config, section) {
|
||||
push(rule_set_tags, ensured.tag);
|
||||
if (ensured.kind == "domains")
|
||||
push(dns_query_rule_set_tags, ensured.tag);
|
||||
else
|
||||
push(dns_response_rule_set_tags, ensured.tag);
|
||||
}
|
||||
for (let reference in connections.rule_sets_with_subnets(section)) {
|
||||
let ensured = ensure_custom_ruleset(config, as_string(reference));
|
||||
if (ensured == null)
|
||||
continue;
|
||||
push(rule_set_tags, ensured.tag);
|
||||
if (ensured.kind == "domains")
|
||||
push(dns_query_rule_set_tags, ensured.tag);
|
||||
push(dns_response_rule_set_tags, ensured.tag);
|
||||
}
|
||||
add_domain_ip_list_ruleset(
|
||||
config,
|
||||
section_name,
|
||||
rule_set_tags,
|
||||
dns_query_rule_set_tags,
|
||||
dns_response_rule_set_tags,
|
||||
list_option(section, "domain_ip_lists"),
|
||||
false
|
||||
);
|
||||
@@ -1945,7 +1980,11 @@ function add_combined_route_for_section(config, section) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_domain && !has_ruleset && !has_ip_cidr && length(discord_cf_subnets) == 0 && length(country_list) == 0) {
|
||||
if (bool_option(section, "match_all", false)) {
|
||||
let all_rule = create_section_route_rule();
|
||||
push_section_route_rule(config, all_rule, target.outbound, excluded_cidrs);
|
||||
}
|
||||
else if (!has_domain && !has_ruleset && !has_ip_cidr && length(discord_cf_subnets) == 0 && length(country_list) == 0) {
|
||||
let fallback_rule = create_section_route_rule();
|
||||
let has_any_matcher = fallback_rule.source_ip_cidr != null ||
|
||||
fallback_rule.port != null || fallback_rule.port_range != null ||
|
||||
|
||||
@@ -180,13 +180,26 @@ function file_extension(value) {
|
||||
|
||||
function kind_from_reference_hint(reference) {
|
||||
reference = lc(as_string(reference));
|
||||
if (index(reference, "geosite") >= 0 || index(reference, "domain") >= 0 ||
|
||||
index(reference, "domains") >= 0 || index(reference, "adguard") >= 0 ||
|
||||
index(reference, "filter") >= 0)
|
||||
return "domains";
|
||||
if (index(reference, "geoip") >= 0 || index(reference, "subnet") >= 0 ||
|
||||
index(reference, "subnets") >= 0 || index(reference, "cidr") >= 0)
|
||||
let has_ip_hint = index(reference, "geoip") >= 0 ||
|
||||
index(reference, "subnet") >= 0 ||
|
||||
index(reference, "subnets") >= 0 ||
|
||||
index(reference, "cidr") >= 0 ||
|
||||
match(reference, /(^|[-_.\/])ips?([-_.\/]|$)/) != null;
|
||||
|
||||
let has_domain_hint = index(reference, "geosite") >= 0 ||
|
||||
index(reference, "domain") >= 0 ||
|
||||
index(reference, "domains") >= 0 ||
|
||||
index(reference, "adguard") >= 0 ||
|
||||
index(reference, "adblock") >= 0 ||
|
||||
index(reference, "host") >= 0 ||
|
||||
index(reference, "hosts") >= 0;
|
||||
|
||||
if (has_ip_hint && has_domain_hint)
|
||||
return "mixed";
|
||||
if (has_ip_hint)
|
||||
return "subnets";
|
||||
if (has_domain_hint)
|
||||
return "domains";
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user