fix: разделение правил маршрутизации секции (fix коллизии AND) и поддержка http/https ссылок

This commit is contained in:
Dushnilin
2026-09-06 19:23:07 +03:00
parent 625715fed0
commit fbeadb48ed
6 changed files with 384 additions and 40 deletions
-10
View File
@@ -2276,22 +2276,12 @@ function nft_populate_runtime_set_for_section(section, deferred_sections, table,
"/tmp/sing-box/rulesets/community-subnets-" + service + ".lst",
"/etc/tachyon/rulesets/community-subnets-" + service + ".lst"
];
let found = false;
for (let path in cached_paths) {
if (helpers.file_is_usable(path, 50)) {
nft_add_file_chunks_to_family_sets(path, table, sets.subnets, sets.subnets6, "ips", "", "5000");
found = true;
break;
}
}
if (!found) {
if (service == "discord") {
nft_add_set_elements(table, sets.subnets, "162.158.0.0/15, 172.64.0.0/13, 138.128.136.0/21");
}
else if (service == "telegram") {
nft_add_set_elements(table, sets.subnets, "149.154.160.0/20, 91.108.4.0/22, 91.108.8.0/22, 91.108.12.0/22, 91.108.16.0/22, 91.108.56.0/22, 5.28.192.0/18, 91.105.192.0/23, 95.161.64.0/20, 185.76.151.0/24, 194.221.0.0/16");
}
}
}
}
@@ -643,8 +643,11 @@ function is_valid_detour(config, tag) {
if (tag == runtime_constants.DIRECT_OUTBOUND_TAG || tag == runtime_constants.BYPASS_OUTBOUND_TAG)
return true;
for (let out in array_or_empty(config.outbounds)) {
if (type(out) == "object" && out.tag == tag)
if (type(out) == "object" && out.tag == tag) {
if (out.tag == "DPI-out" || (out.type == "socks" && (out.server == "127.0.0.1" || out.server == "::1")))
return false;
return true;
}
}
return false;
}
@@ -1284,18 +1287,6 @@ function load_community_subnet_cidrs(community) {
return cidrs;
}
}
// Hardcoded seed fallback for cold boot before list-update downloads .lst
if (service == "discord") {
return [ "162.158.0.0/15", "172.64.0.0/13", "138.128.136.0/21" ];
}
if (service == "telegram") {
return [
"149.154.160.0/20", "91.108.4.0/22", "91.108.8.0/22",
"91.108.12.0/22", "91.108.16.0/22", "91.108.56.0/22",
"5.28.192.0/18", "91.105.192.0/23", "95.161.64.0/20",
"185.76.151.0/24", "194.221.0.0/16"
];
}
return [];
}
@@ -1321,10 +1312,8 @@ function add_combined_route_for_section(config, section) {
let ensured = ensure_community_ruleset(config, section_name, service);
push(rule_set_tags, ensured.tag);
push(dns_rule_set_tags, ensured.tag);
if (service != "cloudflare" && service != "cloudfront" && service != "hetzner" && service != "ovh" && service != "digitalocean") {
for (let cidr in load_community_subnet_cidrs(community))
push(ip_cidr, cidr);
}
for (let cidr in load_community_subnet_cidrs(community))
push(ip_cidr, cidr);
}
for (let reference in connections.rule_sets(section)) {
let ensured = ensure_custom_ruleset(config, as_string(reference));
@@ -1355,9 +1344,9 @@ function add_combined_route_for_section(config, section) {
if (target.unsupported)
ctx.runtime_generate_unsupported(target.unsupported);
let has_domain_or_ruleset = length(domain) > 0 || length(domain_suffix) > 0 ||
length(domain_keyword) > 0 || length(domain_regex) > 0 ||
length(rule_set_tags) > 0;
let has_domain = length(domain) > 0 || length(domain_suffix) > 0 ||
length(domain_keyword) > 0 || length(domain_regex) > 0;
let has_ruleset = length(rule_set_tags) > 0;
let has_ip_cidr = length(ip_cidr) > 0;
let create_section_route_rule = function() {
@@ -1375,14 +1364,12 @@ function add_combined_route_for_section(config, section) {
return r;
};
if (has_domain_or_ruleset) {
if (has_domain) {
let domain_rule = create_section_route_rule();
add_domain_array(domain_rule, "domain", domain);
add_domain_array(domain_rule, "domain_suffix", domain_suffix);
add_domain_array(domain_rule, "domain_keyword", domain_keyword);
add_domain_array(domain_rule, "domain_regex", domain_regex);
if (length(rule_set_tags) > 0)
domain_rule.rule_set = single_or_array(rule_set_tags);
let resolve = runtime_route.resolve_rule_for_section(section, domain_rule);
if (type(resolve) == "object" && resolve.warning)
@@ -1393,6 +1380,19 @@ function add_combined_route_for_section(config, section) {
push_section_route_rule(config, domain_rule, target.outbound);
}
if (has_ruleset) {
let rs_rule = create_section_route_rule();
rs_rule.rule_set = single_or_array(rule_set_tags);
let resolve = runtime_route.resolve_rule_for_section(section, rs_rule);
if (type(resolve) == "object" && resolve.warning)
warn(resolve.warning, "\n");
else if (type(resolve) == "object" && resolve.rule)
push(config.route.rules, resolve.rule);
push_section_route_rule(config, rs_rule, target.outbound);
}
if (has_ip_cidr) {
let ip_rule = create_section_route_rule();
ip_rule.ip_cidr = ip_cidr;
@@ -1426,7 +1426,7 @@ function add_combined_route_for_section(config, section) {
}
}
if (!has_domain_or_ruleset && !has_ip_cidr && length(country_list) == 0) {
if (!has_domain && !has_ruleset && !has_ip_cidr && 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 ||
@@ -1629,20 +1629,20 @@ function add_service_route_rules(config, sections) {
push(detour_domains, host);
}
if (length(detour_ips) > 0) {
push(config.route.rules, {
push_section_route_rule(config, {
action: "route",
inbound: tproxy_inbound_matcher(),
outbound: detour_target,
ip_cidr: single_or_array(detour_ips)
});
}, detour_target);
}
if (length(detour_domains) > 0) {
push(config.route.rules, {
push_section_route_rule(config, {
action: "route",
inbound: tproxy_inbound_matcher(),
outbound: detour_target,
domain: single_or_array(detour_domains)
});
}, detour_target);
}
}
}
+49 -1
View File
@@ -29,7 +29,9 @@ function is_supported_share_link(line) {
starts_with(line, "socks://") ||
starts_with(line, "socks4://") ||
starts_with(line, "socks4a://") ||
starts_with(line, "socks5://");
starts_with(line, "socks5://") ||
starts_with(line, "http://") ||
starts_with(line, "https://");
}
function split_csv(value) {
@@ -880,6 +882,50 @@ function process_socks(raw, url) {
return outbound;
}
function process_http(raw, url) {
let port = url.port;
if (port == null || port == "")
port = url.scheme == "https" ? 443 : 80;
if (url.host == "" || is_dummy_server(url.host, port) || !valid_port(port))
return null;
let username = "", password = "";
if (url.userinfo != "") {
let colon = index(url.userinfo, ":");
if (colon >= 0) {
username = urldecode(substr(url.userinfo, 0, colon));
password = urldecode(substr(url.userinfo, colon + 1));
if (username == password)
password = "";
}
else {
username = urldecode(url.userinfo);
}
}
let outbound = {
type: "http",
tag: url.fragment != "" ? url.fragment : (url.host + ":" + port),
share_link: raw,
server: url.host,
server_port: int(port)
};
if (username != "")
outbound.username = username;
if (password != "")
outbound.password = password;
if (url.scheme == "https") {
let tls = { enabled: true };
if ((url.query.sni || "") != "")
tls.server_name = url.query.sni;
if (is_true(url.query.allowInsecure || url.query.insecure))
tls.insecure = true;
outbound.tls = tls;
}
return outbound;
}
function is_shadowsocks_userinfo_format(value) {
if (type(value) != "string")
return false;
@@ -1183,6 +1229,8 @@ function parse_share_link(line) {
return process_tuic(line, url);
if (match(url.scheme, /^socks/))
return process_socks(line, url);
if (url.scheme == "http" || url.scheme == "https")
return process_http(line, url);
return null;
}
@@ -242,6 +242,33 @@ function serialize_socks(outbound) {
return scheme + "://" + auth + host_port(outbound.server, outbound.server_port) + fragment(outbound);
}
function serialize_http(outbound) {
if (as_string(outbound.server) == "" || outbound.server_port == null)
return "";
let is_tls = type(outbound.tls) == "object" && (outbound.tls.enabled === true || outbound.tls.enabled == 1);
let scheme = is_tls ? "https" : "http";
let auth = "";
if (as_string(outbound.username) != "") {
auth = uri_encode(outbound.username);
if (as_string(outbound.password) != "")
auth += ":" + uri_encode(outbound.password);
auth += "@";
}
let params = [];
if (is_tls && type(outbound.tls) == "object") {
let tls = outbound.tls;
if (tls.server_name)
add_query(params, "sni", tls.server_name);
if (tls.insecure === true)
add_query(params, "insecure", "1");
}
return scheme + "://" + auth + host_port(outbound.server, outbound.server_port) + query_string(params) + fragment(outbound);
}
function serialize_hysteria2(outbound) {
let port = hysteria2_server_ports_uri(outbound);
if (port == "" && outbound.server_port != null)
@@ -365,6 +392,8 @@ function serialize_outbound_link(outbound) {
return serialize_shadowsocks(outbound);
if (outbound_type == "socks")
return serialize_socks(outbound);
if (outbound_type == "http")
return serialize_http(outbound);
if (outbound_type == "hysteria2")
return serialize_hysteria2(outbound);
if (outbound_type == "tuic")
@@ -379,7 +408,8 @@ function is_copyable_link(value) {
let prefixes = [
"vless://", "vmess://", "trojan://", "ss://", "ssr://",
"hysteria2://", "hy2://", "tuic://",
"socks4://", "socks4a://", "socks5://"
"socks4://", "socks4a://", "socks5://",
"http://", "https://"
];
for (let prefix in prefixes) {
if (starts_with(value, prefix))