mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-07 20:54:14 +08:00
🌈 Sync 2026-07-29 14:47:30
This commit is contained in:
1 parent
d31b90c3d6
commit
9419456fd2
48 files changed
+13901
-3399
No files matched your search
@@ -7,6 +7,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import { md5 } from 'digest';
|
||||
import { readfile, writefile } from 'fs';
|
||||
import { isnan } from 'math';
|
||||
import { connect } from 'ubus';
|
||||
@@ -29,7 +30,10 @@ uci.load(uciconfig);
|
||||
|
||||
const uciinfra = 'infra',
|
||||
ucimain = 'config',
|
||||
ucicontrol = 'control';
|
||||
ucicontrol = 'control',
|
||||
uciclashapi = 'clash_api',
|
||||
ucintp = 'ntp',
|
||||
ucicache = 'cache';
|
||||
|
||||
const ucidnssetting = 'dns',
|
||||
ucidnsserver = 'dns_server',
|
||||
@@ -41,23 +45,63 @@ const uciroutingsetting = 'routing',
|
||||
|
||||
const ucinode = 'node';
|
||||
const uciruleset = 'ruleset';
|
||||
const RULESET_DIR = HP_DIR + '/ruleset';
|
||||
|
||||
const routing_mode = uci.get(uciconfig, ucimain, 'routing_mode') || 'bypass_mainland_china';
|
||||
|
||||
function normalize_list(value) {
|
||||
if (isEmpty(value))
|
||||
return [];
|
||||
if (type(value) === 'array')
|
||||
return value;
|
||||
return [value];
|
||||
}
|
||||
|
||||
function ruleset_file_name(tag, format) {
|
||||
let filename = replace(tag || 'ruleset', /[^A-Za-z0-9_.-]+/g, '_');
|
||||
if (isEmpty(filename))
|
||||
filename = 'ruleset';
|
||||
|
||||
return filename + ((format === 'source') ? '.json' : '.srs');
|
||||
}
|
||||
|
||||
function ruleset_default_path(tag, format) {
|
||||
return RULESET_DIR + '/' + ruleset_file_name(tag, format);
|
||||
}
|
||||
|
||||
function ruleset_remote_path(path, tag, format) {
|
||||
if (isEmpty(path))
|
||||
return ruleset_default_path(tag, format);
|
||||
|
||||
if (match(path, /\/$/))
|
||||
return path + ruleset_file_name(tag, format);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
let wan_dns = ubus.call('network.interface', 'status', {'interface': 'wan'})?.['dns-server']?.[0];
|
||||
if (!wan_dns)
|
||||
wan_dns = (routing_mode in ['proxy_mainland_china', 'global']) ? '8.8.8.8' : '223.5.5.5';
|
||||
wan_dns = (routing_mode in ['proxy_mainland_china', 'global']) ? '9.9.9.9' : '223.5.5.5';
|
||||
|
||||
const dns_port = uci.get(uciconfig, uciinfra, 'dns_port') || '5333';
|
||||
|
||||
const ntp_server = uci.get(uciconfig, uciinfra, 'ntp_server') || 'time.apple.com';
|
||||
let ntp_enabled = uci.get(uciconfig, ucintp, 'enabled'),
|
||||
ntp_server = uci.get(uciconfig, ucintp, 'server'),
|
||||
ntp_server_port = uci.get(uciconfig, ucintp, 'server_port'),
|
||||
ntp_interval = uci.get(uciconfig, ucintp, 'interval');
|
||||
|
||||
if (isEmpty(ntp_enabled) && isEmpty(ntp_server)) {
|
||||
ntp_server = uci.get(uciconfig, uciinfra, 'ntp_server');
|
||||
ntp_enabled = !isEmpty(ntp_server) ? '1' : '0';
|
||||
}
|
||||
|
||||
const ipv6_support = uci.get(uciconfig, ucimain, 'ipv6_support') || '0';
|
||||
|
||||
let main_node, main_udp_node, dedicated_udp_node, default_outbound, default_outbound_dns,
|
||||
let cache_file_enabled, cache_file_path, cache_store_rdrc, cache_rdrc_timeout,
|
||||
main_node, main_udp_node, dedicated_udp_node, default_outbound, default_outbound_dns,
|
||||
domain_strategy, sniff_override, dns_server, china_dns_server, dns_default_strategy,
|
||||
dns_default_server, dns_disable_cache, dns_disable_cache_expire, dns_independent_cache,
|
||||
dns_client_subnet, cache_file_store_rdrc, cache_file_rdrc_timeout, direct_domain_list,
|
||||
dns_client_subnet, direct_domain_list,
|
||||
proxy_domain_list;
|
||||
|
||||
if (routing_mode !== 'custom') {
|
||||
@@ -93,8 +137,6 @@ if (routing_mode !== 'custom') {
|
||||
dns_disable_cache_expire = uci.get(uciconfig, ucidnssetting, 'disable_cache_expire');
|
||||
dns_independent_cache = uci.get(uciconfig, ucidnssetting, 'independent_cache');
|
||||
dns_client_subnet = uci.get(uciconfig, ucidnssetting, 'client_subnet');
|
||||
cache_file_store_rdrc = uci.get(uciconfig, ucidnssetting, 'cache_file_store_rdrc'),
|
||||
cache_file_rdrc_timeout = uci.get(uciconfig, ucidnssetting, 'cache_file_rdrc_timeout');
|
||||
|
||||
/* Routing settings */
|
||||
default_outbound = uci.get(uciconfig, uciroutingsetting, 'default_outbound') || 'nil';
|
||||
@@ -110,7 +152,7 @@ const mixed_port = uci.get(uciconfig, uciinfra, 'mixed_port') || '5330';
|
||||
|
||||
let self_mark, redirect_port, tproxy_port, tun_name,
|
||||
tun_addr4, tun_addr6, tun_mtu, tcpip_stack,
|
||||
endpoint_independent_nat, udp_timeout;
|
||||
udp_timeout;
|
||||
|
||||
if (routing_mode === 'custom')
|
||||
udp_timeout = uci.get(uciconfig, uciroutingsetting, 'udp_timeout');
|
||||
@@ -130,13 +172,18 @@ if (match(proxy_mode, /tun/)) {
|
||||
tun_addr6 = uci.get(uciconfig, uciinfra, 'tun_addr6') || 'fdfe:dcba:9876::1/126';
|
||||
tun_mtu = uci.get(uciconfig, uciinfra, 'tun_mtu') || '9000';
|
||||
tcpip_stack = 'system';
|
||||
if (routing_mode === 'custom') {
|
||||
if (routing_mode === 'custom')
|
||||
tcpip_stack = uci.get(uciconfig, uciroutingsetting, 'tcpip_stack') || 'system';
|
||||
endpoint_independent_nat = uci.get(uciconfig, uciroutingsetting, 'endpoint_independent_nat');
|
||||
}
|
||||
}
|
||||
|
||||
const log_level = uci.get(uciconfig, ucimain, 'log_level') || 'warn';
|
||||
|
||||
cache_file_enabled = uci.get(uciconfig, ucicache, 'enabled');
|
||||
cache_file_path = uci.get(uciconfig, ucicache, 'path');
|
||||
cache_store_rdrc = uci.get(uciconfig, ucicache, 'store_rdrc');
|
||||
cache_rdrc_timeout = uci.get(uciconfig, ucicache, 'rdrc_timeout');
|
||||
|
||||
let clash_api = null;
|
||||
/* UCI config end */
|
||||
|
||||
/* Config helper start */
|
||||
@@ -168,6 +215,31 @@ function parse_dnsserver(server_addr, default_protocol) {
|
||||
}
|
||||
}
|
||||
|
||||
function parse_custom_dnsserver(cfg) {
|
||||
let server = cfg.server,
|
||||
server_port = (cfg.type in ['https', 'h3']) ? null : strToInt(cfg.server_port),
|
||||
path = cfg.path;
|
||||
|
||||
if (!isEmpty(server) && match(server, /:\/\//)) {
|
||||
const server_url = parseURL(server);
|
||||
|
||||
server = server_url.hostname;
|
||||
if (isEmpty(server_port))
|
||||
server_port = strToInt(server_url.port);
|
||||
if ((cfg.type in ['https', 'h3']) && isEmpty(path) && server_url.pathname !== '/')
|
||||
path = server_url.pathname;
|
||||
}
|
||||
if ((cfg.type in ['https', 'h3']) && isEmpty(path))
|
||||
path = '/dns-query';
|
||||
|
||||
return {
|
||||
type: cfg.type,
|
||||
server,
|
||||
server_port,
|
||||
path
|
||||
};
|
||||
}
|
||||
|
||||
function parse_dnsquery(strquery) {
|
||||
if (type(strquery) !== 'array' || isEmpty(strquery))
|
||||
return null;
|
||||
@@ -180,13 +252,149 @@ function parse_dnsquery(strquery) {
|
||||
|
||||
}
|
||||
|
||||
function filter_existing_nodes(nodes) {
|
||||
if (type(nodes) !== 'array' || isEmpty(nodes))
|
||||
return [];
|
||||
|
||||
return filter(nodes, (k) => {
|
||||
const node = uci.get_all(uciconfig, k) || {};
|
||||
return !isEmpty(node);
|
||||
});
|
||||
}
|
||||
|
||||
function collect_group_nodes(groups, subscription_nodes, nodes, legacy_nodes) {
|
||||
let result = [];
|
||||
|
||||
for (let group in normalize_list(groups))
|
||||
uci.foreach(uciconfig, ucinode, (cfg) => {
|
||||
if (cfg.grouphash === group && !~index(result, cfg['.name']))
|
||||
push(result, cfg['.name']);
|
||||
});
|
||||
|
||||
for (let node in filter_existing_nodes(normalize_list(subscription_nodes)))
|
||||
if (!~index(result, node))
|
||||
push(result, node);
|
||||
|
||||
for (let node in filter_existing_nodes(normalize_list(nodes)))
|
||||
if (!~index(result, node))
|
||||
push(result, node);
|
||||
|
||||
for (let node in filter_existing_nodes(normalize_list(legacy_nodes)))
|
||||
if (!~index(result, node))
|
||||
push(result, node);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function collect_policy_nodes(nodes, current) {
|
||||
let result = [];
|
||||
|
||||
for (let node in normalize_list(nodes)) {
|
||||
if (node === current || ~index(result, node))
|
||||
continue;
|
||||
|
||||
const outbound = uci.get_all(uciconfig, node) || {};
|
||||
if (!isEmpty(outbound) && outbound['.type'] === uciroutingnode && outbound.enabled === '1' && outbound.node in ['urltest', 'selector'])
|
||||
push(result, node);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const reserved_outbound_tags = {
|
||||
'GLOBAL': true,
|
||||
'main-out': true,
|
||||
'main-udp-out': true,
|
||||
'direct-out': true,
|
||||
'block-out': true
|
||||
};
|
||||
|
||||
let outbound_tag_map = {},
|
||||
outbound_tag_used = {};
|
||||
|
||||
function normalize_outbound_tag(tag) {
|
||||
if (isEmpty(tag))
|
||||
return null;
|
||||
|
||||
tag = trim(sprintf('%s', tag));
|
||||
tag = replace(tag, /[\r\n\t]+/g, ' ');
|
||||
tag = replace(tag, /[\/\\?#%]+/g, '-');
|
||||
tag = replace(tag, /\s+/g, ' ');
|
||||
|
||||
return tag || null;
|
||||
}
|
||||
|
||||
function section_display_name(section) {
|
||||
if (type(section) !== 'object' || isEmpty(section))
|
||||
return null;
|
||||
|
||||
let label = normalize_outbound_tag(section.label);
|
||||
if (!isEmpty(label))
|
||||
return label;
|
||||
|
||||
if (section['.type'] === ucinode) {
|
||||
const address = trim(section.override_address || section.address || '');
|
||||
const port = trim(section.override_port || section.port || '');
|
||||
|
||||
if (address && port)
|
||||
return normalize_outbound_tag(`${address}:${port}`);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function register_outbound_tag(section_id, display_name) {
|
||||
if (isEmpty(section_id))
|
||||
return null;
|
||||
|
||||
let tag = outbound_tag_map[section_id];
|
||||
if (!isEmpty(tag))
|
||||
return tag;
|
||||
|
||||
const fallback_tag = `cfg-${section_id}-out`;
|
||||
const base_tag = normalize_outbound_tag(display_name) || fallback_tag;
|
||||
|
||||
tag = base_tag;
|
||||
let suffix = 2;
|
||||
while (reserved_outbound_tags[tag] || outbound_tag_used[tag]) {
|
||||
tag = `${base_tag} (${suffix})`;
|
||||
suffix++;
|
||||
}
|
||||
|
||||
outbound_tag_map[section_id] = tag;
|
||||
outbound_tag_used[tag] = true;
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
function get_section_outbound_tag(section_id) {
|
||||
if (isEmpty(section_id))
|
||||
return null;
|
||||
|
||||
let tag = outbound_tag_map[section_id];
|
||||
if (!isEmpty(tag))
|
||||
return tag;
|
||||
|
||||
const section = uci.get_all(uciconfig, section_id) || {};
|
||||
return register_outbound_tag(section_id, section_display_name(section));
|
||||
}
|
||||
|
||||
uci.foreach(uciconfig, ucinode, (cfg) => {
|
||||
register_outbound_tag(cfg['.name'], section_display_name(cfg));
|
||||
});
|
||||
|
||||
uci.foreach(uciconfig, uciroutingnode, (cfg) => {
|
||||
if (cfg.enabled === '1')
|
||||
register_outbound_tag(cfg['.name'], section_display_name(cfg));
|
||||
});
|
||||
|
||||
function generate_endpoint(node) {
|
||||
if (type(node) !== 'object' || isEmpty(node))
|
||||
return null;
|
||||
|
||||
const endpoint = {
|
||||
type: node.type,
|
||||
tag: 'cfg-' + node['.name'] + '-out',
|
||||
tag: get_section_outbound_tag(node['.name']),
|
||||
address: node.wireguard_local_address,
|
||||
mtu: strToInt(node.wireguard_mtu),
|
||||
private_key: node.wireguard_private_key,
|
||||
@@ -217,13 +425,14 @@ function generate_outbound(node) {
|
||||
if (type(node) !== 'object' || isEmpty(node))
|
||||
return null;
|
||||
|
||||
const tls_utls_value = (node.type === 'anytls' && isEmpty(node.tls_utls)) ? 'chrome' : node.tls_utls;
|
||||
const outbound = {
|
||||
type: node.type,
|
||||
tag: 'cfg-' + node['.name'] + '-out',
|
||||
tag: get_section_outbound_tag(node['.name']),
|
||||
routing_mark: strToInt(self_mark),
|
||||
|
||||
server: node.address,
|
||||
server_port: strToInt(node.port),
|
||||
server: (node.type !== 'direct') ? node.address : null,
|
||||
server_port: (node.type !== 'direct') ? strToInt(node.port) : null,
|
||||
/* Hysteria(2) */
|
||||
server_ports: node.hysteria_hopping_port,
|
||||
|
||||
@@ -231,10 +440,6 @@ function generate_outbound(node) {
|
||||
user: (node.type === 'ssh') ? node.username : null,
|
||||
password: node.password,
|
||||
|
||||
/* Direct */
|
||||
override_address: node.override_address,
|
||||
override_port: strToInt(node.override_port),
|
||||
proxy_protocol: strToInt(node.proxy_protocol),
|
||||
/* AnyTLS */
|
||||
idle_session_check_interval: strToTime(node.anytls_idle_session_check_interval),
|
||||
idle_session_timeout: strToTime(node.anytls_idle_session_timeout),
|
||||
@@ -306,9 +511,9 @@ function generate_outbound(node) {
|
||||
config: node.tls_ech_config,
|
||||
config_path: node.tls_ech_config_path
|
||||
} : null,
|
||||
utls: !isEmpty(node.tls_utls) ? {
|
||||
utls: !isEmpty(tls_utls_value) ? {
|
||||
enabled: true,
|
||||
fingerprint: node.tls_utls
|
||||
fingerprint: tls_utls_value
|
||||
} : null,
|
||||
reality: (node.tls_reality === '1') ? {
|
||||
enabled: true,
|
||||
@@ -364,10 +569,10 @@ function get_outbound(cfg) {
|
||||
const node = uci.get(uciconfig, cfg, 'node');
|
||||
if (isEmpty(node))
|
||||
die(sprintf("%s's node is missing, please check your configuration.", cfg));
|
||||
else if (node === 'urltest')
|
||||
return 'cfg-' + cfg + '-out';
|
||||
else if (node in ['urltest', 'selector'])
|
||||
return get_section_outbound_tag(cfg);
|
||||
else
|
||||
return 'cfg-' + node + '-out';
|
||||
return get_section_outbound_tag(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -390,10 +595,117 @@ function get_ruleset(cfg) {
|
||||
return null;
|
||||
|
||||
let rules = [];
|
||||
for (let i in cfg)
|
||||
push(rules, isEmpty(i) ? null : 'cfg-' + i + '-rule');
|
||||
for (let i in cfg) {
|
||||
if (isEmpty(i)) {
|
||||
push(rules, null);
|
||||
continue;
|
||||
}
|
||||
|
||||
const ruleset = uci.get_all(uciconfig, i);
|
||||
push(rules, !isEmpty(ruleset?.tag) ? ruleset.tag : ('cfg-' + i + '-rule'));
|
||||
}
|
||||
return rules;
|
||||
}
|
||||
|
||||
function subscription_provider_name(name) {
|
||||
name = trim(name || '');
|
||||
return name;
|
||||
}
|
||||
|
||||
function unique_provider_name(name, used) {
|
||||
const base = name;
|
||||
let suffix = 2;
|
||||
|
||||
while (used[name]) {
|
||||
name = sprintf('%s (%d)', base, suffix);
|
||||
suffix++;
|
||||
}
|
||||
used[name] = true;
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
function get_subscription_info(group_hash) {
|
||||
const value = uci.get(uciconfig, 'subscription', 'subinfo_' + substr(group_hash, 0, 16));
|
||||
if (isEmpty(value))
|
||||
return null;
|
||||
|
||||
try {
|
||||
const info = json(value);
|
||||
if (type(info) !== 'object')
|
||||
return null;
|
||||
|
||||
const has_subscription_info = ('upload' in info) || ('download' in info) ||
|
||||
('total' in info) || ('expire' in info);
|
||||
|
||||
return {
|
||||
updated_at: info.updated_at,
|
||||
subscription_info: has_subscription_info ? {
|
||||
upload: info.upload,
|
||||
download: info.download,
|
||||
total: info.total,
|
||||
expire: info.expire
|
||||
} : null
|
||||
};
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function build_proxy_providers() {
|
||||
let providers = [],
|
||||
used_names = {},
|
||||
subscription_names = normalize_list(uci.get(uciconfig, 'subscription', 'subscription_name')),
|
||||
sub_index = 0;
|
||||
|
||||
for (let suburl in normalize_list(uci.get(uciconfig, 'subscription', 'subscription_url'))) {
|
||||
const subscription_name = subscription_names[sub_index];
|
||||
sub_index++;
|
||||
|
||||
if (isEmpty(suburl) || isEmpty(trim(subscription_name || '')))
|
||||
continue;
|
||||
|
||||
const url = replace(suburl, /#.*$/, ''),
|
||||
group_hash = md5(url);
|
||||
|
||||
let proxies = [];
|
||||
uci.foreach(uciconfig, ucinode, (cfg) => {
|
||||
if (cfg.grouphash !== group_hash)
|
||||
return;
|
||||
|
||||
const tag = get_section_outbound_tag(cfg['.name']);
|
||||
if (!isEmpty(tag) && !~index(proxies, tag))
|
||||
push(proxies, tag);
|
||||
});
|
||||
|
||||
if (isEmpty(proxies))
|
||||
continue;
|
||||
|
||||
const subscription_info = get_subscription_info(group_hash);
|
||||
|
||||
push(providers, {
|
||||
name: unique_provider_name(subscription_provider_name(subscription_name), used_names),
|
||||
type: 'Proxy',
|
||||
vehicle_type: 'HTTP',
|
||||
proxies: proxies,
|
||||
update_id: (routing_mode === 'custom') ? group_hash : null,
|
||||
updated_at: subscription_info?.updated_at,
|
||||
subscription_info: subscription_info?.subscription_info
|
||||
});
|
||||
}
|
||||
|
||||
return providers;
|
||||
}
|
||||
|
||||
clash_api = {
|
||||
external_controller: uci.get(uciconfig, uciclashapi, 'external_controller') || '127.0.0.1:9090',
|
||||
external_ui: uci.get(uciconfig, uciclashapi, 'external_ui'),
|
||||
external_ui_download_url: uci.get(uciconfig, uciclashapi, 'external_ui_download_url'),
|
||||
external_ui_download_detour: get_outbound(uci.get(uciconfig, uciclashapi, 'external_ui_download_detour')),
|
||||
secret: uci.get(uciconfig, uciclashapi, 'secret'),
|
||||
default_mode: uci.get(uciconfig, uciclashapi, 'default_mode') || 'rule',
|
||||
proxy_providers: build_proxy_providers()
|
||||
};
|
||||
/* Config helper end */
|
||||
|
||||
const config = {};
|
||||
@@ -409,8 +721,10 @@ config.log = {
|
||||
/* NTP */
|
||||
if (!isEmpty(ntp_server))
|
||||
config.ntp = {
|
||||
enabled: true,
|
||||
enabled: ntp_enabled === '1',
|
||||
server: ntp_server,
|
||||
server_port: strToInt(ntp_server_port),
|
||||
interval: ntp_interval,
|
||||
detour: 'direct-out',
|
||||
domain_resolver: 'default-dns',
|
||||
};
|
||||
@@ -520,10 +834,7 @@ if (!isEmpty(main_node)) {
|
||||
|
||||
push(config.dns.servers, {
|
||||
tag: 'cfg-' + cfg['.name'] + '-dns',
|
||||
type: cfg.type,
|
||||
server: cfg.server,
|
||||
server_port: strToInt(cfg.server_port),
|
||||
path: cfg.path,
|
||||
...parse_custom_dnsserver(cfg),
|
||||
headers: cfg.headers,
|
||||
tls: cfg.tls_sni ? {
|
||||
enabled: true,
|
||||
@@ -565,7 +876,6 @@ if (!isEmpty(main_node)) {
|
||||
user: cfg.user,
|
||||
rule_set: get_ruleset(cfg.rule_set),
|
||||
rule_set_ip_cidr_match_source: strToBool(cfg.rule_set_ip_cidr_match_source),
|
||||
rule_set_ip_cidr_accept_empty: strToBool(cfg.rule_set_ip_cidr_accept_empty),
|
||||
invert: strToBool(cfg.invert),
|
||||
outbound: get_outbound(cfg.outbound),
|
||||
action: cfg.action,
|
||||
@@ -606,8 +916,6 @@ push(config.inbounds, {
|
||||
listen: '::',
|
||||
listen_port: int(mixed_port),
|
||||
udp_timeout: strToTime(udp_timeout),
|
||||
sniff: true,
|
||||
sniff_override_destination: strToBool(sniff_override),
|
||||
set_system_proxy: false
|
||||
});
|
||||
|
||||
@@ -617,9 +925,7 @@ if (match(proxy_mode, /redirect/))
|
||||
tag: 'redirect-in',
|
||||
|
||||
listen: '::',
|
||||
listen_port: int(redirect_port),
|
||||
sniff: true,
|
||||
sniff_override_destination: strToBool(sniff_override)
|
||||
listen_port: int(redirect_port)
|
||||
});
|
||||
if (match(proxy_mode, /tproxy/))
|
||||
push(config.inbounds, {
|
||||
@@ -629,9 +935,7 @@ if (match(proxy_mode, /tproxy/))
|
||||
listen: '::',
|
||||
listen_port: int(tproxy_port),
|
||||
network: 'udp',
|
||||
udp_timeout: strToTime(udp_timeout),
|
||||
sniff: true,
|
||||
sniff_override_destination: strToBool(sniff_override)
|
||||
udp_timeout: strToTime(udp_timeout)
|
||||
});
|
||||
if (match(proxy_mode, /tun/))
|
||||
push(config.inbounds, {
|
||||
@@ -642,11 +946,8 @@ if (match(proxy_mode, /tun/))
|
||||
address: (ipv6_support === '1') ? [tun_addr4, tun_addr6] : [tun_addr4],
|
||||
mtu: strToInt(tun_mtu),
|
||||
auto_route: false,
|
||||
endpoint_independent_nat: strToBool(endpoint_independent_nat),
|
||||
udp_timeout: strToTime(udp_timeout),
|
||||
stack: tcpip_stack,
|
||||
sniff: true,
|
||||
sniff_override_destination: strToBool(sniff_override)
|
||||
stack: tcpip_stack
|
||||
});
|
||||
/* Inbound end */
|
||||
|
||||
@@ -671,14 +972,16 @@ if (!isEmpty(main_node)) {
|
||||
let urltest_nodes = [];
|
||||
|
||||
if (main_node === 'urltest') {
|
||||
const main_urltest_nodes = uci.get(uciconfig, ucimain, 'main_urltest_nodes') || [];
|
||||
const main_urltest_nodes = filter_existing_nodes(
|
||||
normalize_list(uci.get(uciconfig, ucimain, 'main_urltest_nodes'))
|
||||
);
|
||||
const main_urltest_interval = uci.get(uciconfig, ucimain, 'main_urltest_interval');
|
||||
const main_urltest_tolerance = uci.get(uciconfig, ucimain, 'main_urltest_tolerance');
|
||||
|
||||
push(config.outbounds, {
|
||||
type: 'urltest',
|
||||
tag: 'main-out',
|
||||
outbounds: map(main_urltest_nodes, (k) => `cfg-${k}-out`),
|
||||
outbounds: map(main_urltest_nodes, (k) => get_section_outbound_tag(k)),
|
||||
interval: strToTime(main_urltest_interval),
|
||||
tolerance: strToInt(main_urltest_tolerance),
|
||||
idle_timeout: (strToInt(main_urltest_interval) > 1800) ? `${main_urltest_interval * 2}s` : null,
|
||||
@@ -687,23 +990,31 @@ if (!isEmpty(main_node)) {
|
||||
} else {
|
||||
const main_node_cfg = uci.get_all(uciconfig, main_node) || {};
|
||||
if (main_node_cfg.type === 'wireguard') {
|
||||
push(config.endpoints, generate_endpoint(main_node_cfg));
|
||||
config.endpoints[length(config.endpoints)-1].tag = 'main-out';
|
||||
const main_endpoint = generate_endpoint(main_node_cfg);
|
||||
if (main_endpoint) {
|
||||
main_endpoint.tag = 'main-out';
|
||||
push(config.endpoints, main_endpoint);
|
||||
}
|
||||
} else {
|
||||
push(config.outbounds, generate_outbound(main_node_cfg));
|
||||
config.outbounds[length(config.outbounds)-1].tag = 'main-out';
|
||||
const main_outbound = generate_outbound(main_node_cfg);
|
||||
if (main_outbound) {
|
||||
main_outbound.tag = 'main-out';
|
||||
push(config.outbounds, main_outbound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (main_udp_node === 'urltest') {
|
||||
const main_udp_urltest_nodes = uci.get(uciconfig, ucimain, 'main_udp_urltest_nodes') || [];
|
||||
const main_udp_urltest_nodes = filter_existing_nodes(
|
||||
normalize_list(uci.get(uciconfig, ucimain, 'main_udp_urltest_nodes'))
|
||||
);
|
||||
const main_udp_urltest_interval = uci.get(uciconfig, ucimain, 'main_udp_urltest_interval');
|
||||
const main_udp_urltest_tolerance = uci.get(uciconfig, ucimain, 'main_udp_urltest_tolerance');
|
||||
|
||||
push(config.outbounds, {
|
||||
type: 'urltest',
|
||||
tag: 'main-udp-out',
|
||||
outbounds: map(main_udp_urltest_nodes, (k) => `cfg-${k}-out`),
|
||||
outbounds: map(main_udp_urltest_nodes, (k) => get_section_outbound_tag(k)),
|
||||
interval: strToTime(main_udp_urltest_interval),
|
||||
tolerance: strToInt(main_udp_urltest_tolerance),
|
||||
idle_timeout: (strToInt(main_udp_urltest_interval) > 1800) ? `${main_udp_urltest_interval * 2}s` : null,
|
||||
@@ -712,22 +1023,37 @@ if (!isEmpty(main_node)) {
|
||||
} else if (dedicated_udp_node) {
|
||||
const main_udp_node_cfg = uci.get_all(uciconfig, main_udp_node) || {};
|
||||
if (main_udp_node_cfg.type === 'wireguard') {
|
||||
push(config.endpoints, generate_endpoint(main_udp_node_cfg));
|
||||
config.endpoints[length(config.endpoints)-1].tag = 'main-udp-out';
|
||||
const main_udp_endpoint = generate_endpoint(main_udp_node_cfg);
|
||||
if (main_udp_endpoint) {
|
||||
main_udp_endpoint.tag = 'main-udp-out';
|
||||
push(config.endpoints, main_udp_endpoint);
|
||||
}
|
||||
} else {
|
||||
push(config.outbounds, generate_outbound(main_udp_node_cfg));
|
||||
config.outbounds[length(config.outbounds)-1].tag = 'main-udp-out';
|
||||
const main_udp_outbound = generate_outbound(main_udp_node_cfg);
|
||||
if (main_udp_outbound) {
|
||||
main_udp_outbound.tag = 'main-udp-out';
|
||||
push(config.outbounds, main_udp_outbound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let i in urltest_nodes) {
|
||||
const urltest_node = uci.get_all(uciconfig, i) || {};
|
||||
if (isEmpty(urltest_node))
|
||||
continue;
|
||||
|
||||
if (urltest_node.type === 'wireguard') {
|
||||
push(config.endpoints, generate_endpoint(urltest_node));
|
||||
config.endpoints[length(config.endpoints)-1].tag = 'cfg-' + i + '-out';
|
||||
const endpoint = generate_endpoint(urltest_node);
|
||||
if (endpoint) {
|
||||
endpoint.tag = get_section_outbound_tag(i);
|
||||
push(config.endpoints, endpoint);
|
||||
}
|
||||
} else {
|
||||
push(config.outbounds, generate_outbound(urltest_node));
|
||||
config.outbounds[length(config.outbounds)-1].tag = 'cfg-' + i + '-out';
|
||||
const outbound = generate_outbound(urltest_node);
|
||||
if (outbound) {
|
||||
outbound.tag = get_section_outbound_tag(i);
|
||||
push(config.outbounds, outbound);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!isEmpty(default_outbound)) {
|
||||
@@ -739,37 +1065,63 @@ if (!isEmpty(main_node)) {
|
||||
return;
|
||||
|
||||
if (cfg.node === 'urltest') {
|
||||
const legacy_nodes = (isEmpty(cfg.subscription_groups) && isEmpty(cfg.subscription_nodes) && isEmpty(cfg.selected_nodes)) ? cfg.urltest_nodes : null;
|
||||
const urltest_list = collect_group_nodes(cfg.subscription_groups, cfg.subscription_nodes, cfg.selected_nodes, legacy_nodes);
|
||||
push(config.outbounds, {
|
||||
type: 'urltest',
|
||||
tag: 'cfg-' + cfg['.name'] + '-out',
|
||||
outbounds: map(cfg.urltest_nodes, (k) => `cfg-${k}-out`),
|
||||
tag: get_section_outbound_tag(cfg['.name']),
|
||||
outbounds: map(urltest_list, (k) => get_section_outbound_tag(k)),
|
||||
url: cfg.urltest_url,
|
||||
interval: strToTime(cfg.urltest_interval),
|
||||
tolerance: strToInt(cfg.urltest_tolerance),
|
||||
idle_timeout: strToTime(cfg.urltest_idle_timeout),
|
||||
interrupt_exist_connections: strToBool(cfg.urltest_interrupt_exist_connections)
|
||||
});
|
||||
urltest_nodes = [...urltest_nodes, ...filter(cfg.urltest_nodes, (l) => !~index(urltest_nodes, l))];
|
||||
urltest_nodes = [...urltest_nodes, ...filter(urltest_list, (l) => !~index(urltest_nodes, l))];
|
||||
} else if (cfg.node === 'selector') {
|
||||
const selector_node_list = collect_group_nodes(cfg.subscription_groups, cfg.subscription_nodes, cfg.selected_nodes, null);
|
||||
const selector_policy_list = collect_policy_nodes(cfg.policy_nodes, cfg['.name']);
|
||||
const selector_list = [...selector_node_list, ...filter(selector_policy_list, (l) => !~index(selector_node_list, l))];
|
||||
const selector_default = (!isEmpty(cfg.selector_default) && ~index(selector_list, cfg.selector_default)) ? cfg.selector_default : null;
|
||||
push(config.outbounds, {
|
||||
type: 'selector',
|
||||
tag: get_section_outbound_tag(cfg['.name']),
|
||||
outbounds: map(selector_list, (k) => get_section_outbound_tag(k)),
|
||||
default: selector_default ? get_section_outbound_tag(selector_default) : null,
|
||||
interrupt_exist_connections: strToBool(cfg.selector_interrupt_exist_connections)
|
||||
});
|
||||
urltest_nodes = [...urltest_nodes, ...filter(selector_node_list, (l) => !~index(urltest_nodes, l))];
|
||||
} else {
|
||||
const outbound = uci.get_all(uciconfig, cfg.node) || {};
|
||||
if (isEmpty(outbound))
|
||||
return;
|
||||
|
||||
if (outbound.type === 'wireguard') {
|
||||
push(config.endpoints, generate_endpoint(outbound));
|
||||
config.endpoints[length(config.endpoints)-1].bind_interface = cfg.bind_interface;
|
||||
config.endpoints[length(config.endpoints)-1].detour = get_outbound(cfg.outbound);
|
||||
const endpoint = generate_endpoint(outbound);
|
||||
if (!endpoint)
|
||||
return;
|
||||
|
||||
endpoint.bind_interface = cfg.bind_interface;
|
||||
endpoint.detour = get_outbound(cfg.outbound);
|
||||
if (cfg.domain_resolver)
|
||||
config.endpoints[length(config.endpoints)-1].domain_resolver = {
|
||||
endpoint.domain_resolver = {
|
||||
server: get_resolver(cfg.domain_resolver),
|
||||
strategy: cfg.domain_strategy
|
||||
};
|
||||
push(config.endpoints, endpoint);
|
||||
} else {
|
||||
push(config.outbounds, generate_outbound(outbound));
|
||||
config.outbounds[length(config.outbounds)-1].bind_interface = cfg.bind_interface;
|
||||
config.outbounds[length(config.outbounds)-1].detour = get_outbound(cfg.outbound);
|
||||
const routed_outbound = generate_outbound(outbound);
|
||||
if (!routed_outbound)
|
||||
return;
|
||||
|
||||
routed_outbound.bind_interface = cfg.bind_interface;
|
||||
routed_outbound.detour = get_outbound(cfg.outbound);
|
||||
if (cfg.domain_resolver)
|
||||
config.outbounds[length(config.outbounds)-1].domain_resolver = {
|
||||
routed_outbound.domain_resolver = {
|
||||
server: get_resolver(cfg.domain_resolver),
|
||||
strategy: cfg.domain_strategy
|
||||
};
|
||||
push(config.outbounds, routed_outbound);
|
||||
}
|
||||
push(routing_nodes, cfg.node);
|
||||
}
|
||||
@@ -777,10 +1129,18 @@ if (!isEmpty(main_node)) {
|
||||
|
||||
for (let i in filter(urltest_nodes, (l) => !~index(routing_nodes, l))) {
|
||||
const urltest_node = uci.get_all(uciconfig, i) || {};
|
||||
if (urltest_node.type === 'wireguard')
|
||||
push(config.endpoints, generate_endpoint(urltest_node));
|
||||
else
|
||||
push(config.outbounds, generate_outbound(urltest_node));
|
||||
if (isEmpty(urltest_node))
|
||||
continue;
|
||||
|
||||
if (urltest_node.type === 'wireguard') {
|
||||
const endpoint = generate_endpoint(urltest_node);
|
||||
if (endpoint)
|
||||
push(config.endpoints, endpoint);
|
||||
} else {
|
||||
const outbound = generate_outbound(urltest_node);
|
||||
if (outbound)
|
||||
push(config.outbounds, outbound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -795,13 +1155,10 @@ config.route = {
|
||||
{
|
||||
inbound: 'dns-in',
|
||||
action: 'hijack-dns'
|
||||
},
|
||||
{
|
||||
action: 'sniff'
|
||||
}
|
||||
/*
|
||||
* leave for sing-box 1.13.0
|
||||
* {
|
||||
* action: 'sniff'
|
||||
* }
|
||||
*/
|
||||
],
|
||||
rule_set: [],
|
||||
auto_detect_interface: isEmpty(default_interface) ? true : null,
|
||||
@@ -866,6 +1223,7 @@ if (!isEmpty(main_node)) {
|
||||
tag: 'geoip-cn',
|
||||
format: 'binary',
|
||||
url: 'https://fastly.jsdelivr.net/gh/1715173329/IPCIDR-CHINA@rule-set/cn.srs',
|
||||
path: ruleset_default_path('geoip-cn', 'binary'),
|
||||
download_detour: 'main-out'
|
||||
});
|
||||
push(config.route.rule_set, {
|
||||
@@ -873,6 +1231,7 @@ if (!isEmpty(main_node)) {
|
||||
tag: 'geosite-cn',
|
||||
format: 'binary',
|
||||
url: 'https://fastly.jsdelivr.net/gh/1715173329/sing-geosite@rule-set-unstable/geosite-geolocation-cn.srs',
|
||||
path: ruleset_default_path('geosite-cn', 'binary'),
|
||||
download_detour: 'main-out'
|
||||
});
|
||||
push(config.route.rule_set, {
|
||||
@@ -880,6 +1239,7 @@ if (!isEmpty(main_node)) {
|
||||
tag: 'geosite-noncn',
|
||||
format: 'binary',
|
||||
url: 'https://fastly.jsdelivr.net/gh/1715173329/sing-geosite@rule-set-unstable/geosite-geolocation-!cn.srs',
|
||||
path: ruleset_default_path('geosite-noncn', 'binary'),
|
||||
download_detour: 'main-out'
|
||||
});
|
||||
}
|
||||
@@ -924,17 +1284,25 @@ if (!isEmpty(main_node)) {
|
||||
user: cfg.user,
|
||||
rule_set: get_ruleset(cfg.rule_set),
|
||||
rule_set_ip_cidr_match_source: strToBool(cfg.rule_set_ip_cidr_match_source),
|
||||
rule_set_ip_cidr_accept_empty: strToBool(cfg.rule_set_ip_cidr_accept_empty),
|
||||
invert: strToBool(cfg.invert),
|
||||
action: cfg.action,
|
||||
outbound: get_outbound(cfg.outbound),
|
||||
override_address: cfg.override_address,
|
||||
override_port: strToInt(cfg.override_port),
|
||||
udp_disable_domain_unmapping: strToBool(cfg.udp_disable_domain_unmapping),
|
||||
udp_connect: strToBool(cfg.udp_connect),
|
||||
udp_timeout: strToTime(cfg.udp_timeout),
|
||||
tls_fragment: strToBool(cfg.tls_fragment),
|
||||
tls_fragment_fallback_delay: strToTime(cfg.tls_fragment_fallback_delay),
|
||||
tls_record_fragment: strToBool(cfg.tls_record_fragment)
|
||||
outbound: (cfg.action === 'route') ? get_outbound(cfg.outbound) : null,
|
||||
server: (cfg.action === 'resolve') ? get_resolver(cfg.resolve_server) : null,
|
||||
strategy: (cfg.action === 'resolve') ? cfg.resolve_strategy : null,
|
||||
disable_cache: (cfg.action === 'resolve') ? strToBool(cfg.resolve_disable_cache) : null,
|
||||
rewrite_ttl: (cfg.action === 'resolve') ? strToInt(cfg.resolve_rewrite_ttl) : null,
|
||||
client_subnet: (cfg.action === 'resolve') ? cfg.resolve_client_subnet : null,
|
||||
override_address: (cfg.action in ['route', 'route-options']) ? cfg.override_address : null,
|
||||
override_port: (cfg.action in ['route', 'route-options']) ? strToInt(cfg.override_port) : null,
|
||||
udp_disable_domain_unmapping: (cfg.action in ['route', 'route-options']) ? strToBool(cfg.udp_disable_domain_unmapping) : null,
|
||||
udp_connect: (cfg.action in ['route', 'route-options']) ? strToBool(cfg.udp_connect) : null,
|
||||
udp_timeout: (cfg.action in ['route', 'route-options']) ? strToTime(cfg.udp_timeout) : null,
|
||||
tls_fragment: (cfg.action in ['route', 'route-options']) ? strToBool(cfg.tls_fragment) : null,
|
||||
tls_fragment_fallback_delay: (cfg.action in ['route', 'route-options']) ? strToTime(cfg.tls_fragment_fallback_delay) : null,
|
||||
tls_record_fragment: (cfg.action in ['route', 'route-options']) ? strToBool(cfg.tls_record_fragment) : null,
|
||||
method: (cfg.action === 'reject') ? cfg.reject_method : null,
|
||||
no_drop: (cfg.action === 'reject') ? strToBool(cfg.reject_no_drop) : null
|
||||
});
|
||||
});
|
||||
|
||||
@@ -945,14 +1313,16 @@ if (!isEmpty(main_node)) {
|
||||
if (cfg.enabled !== '1')
|
||||
return null;
|
||||
|
||||
const tag = !isEmpty(cfg.tag) ? cfg.tag : ('cfg-' + cfg['.name'] + '-rule');
|
||||
|
||||
push(config.route.rule_set, {
|
||||
type: cfg.type,
|
||||
tag: 'cfg-' + cfg['.name'] + '-rule',
|
||||
tag: tag,
|
||||
format: cfg.format,
|
||||
path: cfg.path,
|
||||
url: cfg.url,
|
||||
path: (cfg.type === 'remote') ? ruleset_remote_path(cfg.remote_path || cfg.path, tag, cfg.format) : cfg.path,
|
||||
download_detour: get_outbound(cfg.outbound),
|
||||
update_interval: cfg.update_interval
|
||||
update_interval: (cfg.type === 'remote') ? '87600h' : null
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -961,15 +1331,17 @@ if (!isEmpty(main_node)) {
|
||||
/* Experimental start */
|
||||
if (routing_mode in ['bypass_mainland_china', 'custom']) {
|
||||
config.experimental = {
|
||||
clash_api,
|
||||
cache_file: {
|
||||
enabled: true,
|
||||
path: RUN_DIR + '/cache.db',
|
||||
store_rdrc: strToBool(cache_file_store_rdrc),
|
||||
rdrc_timeout: strToTime(cache_file_rdrc_timeout),
|
||||
enabled: cache_file_enabled !== '0',
|
||||
path: cache_file_path || (HP_DIR + '/cache.db'),
|
||||
store_rdrc: strToBool(cache_store_rdrc),
|
||||
rdrc_timeout: strToTime(cache_rdrc_timeout),
|
||||
}
|
||||
};
|
||||
}
|
||||
/* Experimental end */
|
||||
|
||||
system('mkdir -p ' + RUN_DIR);
|
||||
system('mkdir -p ' + RULESET_DIR);
|
||||
writefile(RUN_DIR + '/sing-box-c.json', sprintf('%.J\n', removeBlankAttrs(config)));
|
||||
Reference in new issue
Block a user